electronoora / webaudio-mod-player

MOD/S3M/XM module player for Web Audio
https://mod.haxor.fi/
MIT License
364 stars 54 forks source link

Support additional MOD formats #27

Open krzykos opened 3 years ago

krzykos commented 3 years ago

Add additional MOD format support

The classic MOD format is quite straightforward - file has no header and only 15 samples. As a result, the data offsets are changed. Examples:

The Chiptracker MOD is more complex. It used patterns per track, so the pattern structures are different. The adapted UI now shows data per song position, not per pattern number. Also the 4-byte event encoding is much simpler, but on loading it gets encoded back to avoid major rework. Still it contains the same information. Some resources about this format:

electronoora commented 2 years ago

Also - sorry for responding so late. Completely missed that there were open PRs. :)

electronoora commented 2 years ago

Seems like SoundTracker format uses also a loop start point in bytes, as opposed to words like in ProTracker. The looped samples here have the same issue as the Dojo Dan song:

https://mod-staging.haxor.fi/mod.amegas

krzykos commented 2 years ago

Thanks for the reply!

Tested with one of the songs from Dojo Dan and noticed that the looping flute sample sounds a bit odd:

https://mod-staging.haxor.fi/mod.ORIENTALEV3.CHIP

I totally agree with that issue. I must have missed it. I remember testing on LEV1 all the time :D

krzykos commented 2 years ago

My fork actually got a little bit further - you can check out the master branch, which is deployed to https://music.cryptofolio.live/

I'm afraid I don't have time to work on this PR any more. Besides you've done the analysis, which is most of the work. If you're right, then the fix is trivial :)

My next project is https://gamus.space/ and eats up hell lots of time... That's why I don't want to get back to this PR. Feel free to take it and make any changes necessary (in any order, editing allowed) And let me know if you manage to fix LEV3 :) BTW. LEV4 has a similar issue - probably the same cause.

sholwe commented 2 years ago

FWIW, Gamus doesn't seem to work at all on Chrome/Linux anymore.

krzykos commented 2 years ago

@sholwe fixed Next time please use the bugtracker https://github.com/gamus-space/player/issues

krzykos commented 2 years ago

During recent days I've been playing with S3M and I found out that

Workaround - undoing this change restores proper notes:

diff --git a/js/ui.js b/js/ui.js
index b4ce19a..45f23cc 100644
--- a/js/ui.js
+++ b/js/ui.js
@@ -260,11 +260,11 @@ function updateUI(timestamp)
       $("#odd-channels").html(txt1);
     } else if (window.moduleVis==1) {
       if (oldpos>=0 && oldrow>=0) $(".currentrow").removeClass("currentrow");
-      $("#pattern"+hb(mod.position)+"_row"+hb(mod.row)).addClass("currentrow");
-      $("#pattern"+hb(mod.position)).scrollTop(mod.row*16);
+      $("#pattern"+hb(mod.currentpattern())+"_row"+hb(mod.row)).addClass("currentrow");
+      $("#pattern"+hb(mod.currentpattern())).scrollTop(mod.row*16);
       if (oldpos != mod.position) {
         if (oldpos>=0) $(".currentpattern").removeClass("currentpattern");
-        $("#pattern"+hb(mod.position)).addClass("currentpattern");
+        $("#pattern"+hb(mod.currentpattern())).addClass("currentpattern");
       }
     }

@@ -384,7 +384,7 @@ $(document).ready(function() {
     }

     var pd="";
-    for(p=0;p<this.songlen;p++) {
+    for(p=0;p<this.patterns;p++) {
       var pp, pdata;
       pd+="<div class=\"patterndata\" id=\"pattern"+hb(p)+"\">";
       for(i=0; i<12; i++) pd+="\n";
krzykos commented 2 years ago

That's the proper solution to the note problem for S3M and XM:

diff --git a/js/player.js b/js/player.js
index 26d0135..65b0354 100644
--- a/js/player.js
+++ b/js/player.js
@@ -336,13 +336,13 @@ Modplayer.prototype.patterndata = function(pn)
       }
     }
   } else if (this.format=='s3m') {
-    patt=new Uint8Array(this.player.pattern[pn]);
+    patt=new Uint8Array(this.player.pattern[this.player.patterntable[pn]]);
     for(i=0;i<64;i++) for(c=0;c<this.player.channels;c++) {
       if (patt[i*5*this.channels+c*5+3]==255) patt[i*5*this.channels+c*5+3]=0x2e;
       else patt[i*5*this.channels+c*5+3]+=0x40;
     }
   } else if (this.format=='xm') {
-    patt=new Uint8Array(this.player.pattern[pn]);
+    patt=new Uint8Array(this.player.pattern[this.player.patterntable[pn]]);
     for(i=0;i<this.player.patternlen[pn];i++) for(c=0;c<this.player.channels;c++) {
       if (patt[i*5*this.channels+c*5+0]<97)
         patt[i*5*this.channels+c*5+0]=(patt[i*5*this.channels+c*5+0]%12)|(Math.floor(patt[i*5*this.channels+c*5+0]/12)<<4);