Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@
code: <a href="http://github.com/a1k0n/jsxm/">github.com/a1k0n/jsxm</a>
todo:
- missing XM effects:
- E3x, E6x, E7x, E9x, EDx, EEx
- 7xy - tremolo
- E3x, E6x, E9x, EDx, EEx
- Kxx, Lxx, Pxy, Txy
- render pattern with the wider fonts for fewer channels
- fix occasional rendering/audio hiccups when switching songs
Expand Down
23 changes: 23 additions & 0 deletions test/effects.js
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,29 @@ exports['test E5x finetune override'] = function(assert) {
assert.equal(f2.toFixed(2), "131.00", "E5f finetune +127");
};

exports['test E7x set tremolo waveform'] = function(assert) {
var xm = testdata.resetXMData();
xm.patterns = [
[
[[48, 1, -1, 0, 0x00]], // C-4 1 -- --- (default waveform - sine)
[[48, 1, -1, 14, 0x71]], // C-4 1 -- E71 (saw, ramp-down)
[[48, 1, -1, 14, 0x72]], // C-4 1 -- E72 (square)
[[48, 1, -1, 14, 0x73]] // C-4 1 -- E73 (random)
]
];
xm.tempo = 1;
var ch = xm.channelinfo[0];
XMPlayer.nextTick();
assert.equal(ch.tremolotype, 0, 'row 0 tick 0 tremolotype=0');
XMPlayer.nextTick();
assert.equal(ch.tremolotype, 1, 'row 1 tick 0 tremolotype=1');
XMPlayer.nextTick();
assert.equal(ch.tremolotype, 2, 'row 2 tick 0 tremolotype=2');
XMPlayer.nextTick();
assert.equal(ch.tremolotype, 3, 'row 3 tick 0 tremolotype=3');
};


exports['test E60 loop twice with set beginning'] = function(assert) {
var xm = testdata.resetXMData(2);

Expand Down
1 change: 1 addition & 0 deletions test/testdata.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ exports.resetXMData = function(channels) {
vibratodepth: 1,
vibratospeed: 1,
vibratotype: 0,
tremolotype: 0,
});
}
xm.songpats = [0];
Expand Down
9 changes: 7 additions & 2 deletions xm.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ function nextTick() {
for (j = 0; j < player.xm.nchan; j++) {
ch = player.xm.channelinfo[j];
ch.periodoffset = 0;
ch.voloffset = 0;
}
if (player.cur_tick >= player.xm.tempo) {
player.cur_tick = 0;
Expand Down Expand Up @@ -414,8 +415,8 @@ function MixChannelIntoBuf(ch, start, end, dataL, dataR) {
var volE = ch.volE / 64.0; // current volume envelope
var panE = 4*(ch.panE - 32); // current panning envelope
var p = panE + ch.pan - 128; // final pan
var volL = player.xm.global_volume * volE * (128 - p) * ch.vol / (64 * 128 * 128);
var volR = player.xm.global_volume * volE * (128 + p) * ch.vol / (64 * 128 * 128);
var volL = player.xm.global_volume * volE * (128 - p) * (ch.vol + ch.voloffset) / (64 * 128 * 128);
var volR = player.xm.global_volume * volE * (128 + p) * (ch.vol + ch.voloffset) / (64 * 128 * 128);
if (volL < 0) volL = 0;
if (volR < 0) volR = 0;
if (volR === 0 && volL === 0)
Expand Down Expand Up @@ -710,6 +711,10 @@ function load(arrayBuf) {
vibratodepth: 1,
vibratospeed: 1,
vibratotype: 0,
tremolopos: 0,
tremolodepth: 1,
tremolospeed: 1,
tremolotype: 0,
});
}
console.log("header len " + hlen);
Expand Down
51 changes: 49 additions & 2 deletions xmeffects.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,50 @@ function eff_t1_6(ch) { // vibrato + volume slide
eff_t1_4(ch);
}

function eff_t0_7(ch, data) { // tremolo
if (data & 0x0f) {
ch.tremolodepth = (data & 0x0f) * 2;
}
if (data >> 4) {
ch.tremolospeed = data >> 4;
}
eff_t1_7(ch);
}

function eff_t1_7(ch) { // tremolo
ch.voloffset =
getTremoloDelta(ch.tremolotype, ch.tremolopos) * ch.tremolodepth;
if (isNaN(ch.voloffset)) {
console.log("tremolo voloffset NaN?",
ch.tremolopos, ch.tremolospeed, ch.tremolodepth);
ch.voloffset = 0;
}
// only updates on non-first ticks
if (player.cur_tick > 0) {
ch.tremolopos += ch.tremolospeed;
ch.tremolopos &= 63;
}
}

function getTremoloDelta(type, x) {
var delta = 0;
switch (type & 0x03) {
case 1: // sawtooth (ramp-down)
delta = ((1 + x * 2 / 64) % 2) - 1;
break;
case 2: // square
case 3: // random (in FT2 these two are the same)
delta = x < 32 ? 1 : -1;
break;
case 0:
default: // sine
delta = Math.sin(x * Math.PI / 32);
break;
}
return delta;
}


function eff_t0_8(ch, data) { // set panning
ch.pan = data;
}
Expand Down Expand Up @@ -179,6 +223,9 @@ function eff_t0_e(ch, data) { // extended effects!
}
}
break;
case 7: // set tremolo waveform
ch.tremolotype = data & 0x07;
break;
case 8: // panning
ch.pan = data * 0x11;
break;
Expand Down Expand Up @@ -290,7 +337,7 @@ player.effects_t0 = [ // effect functions on tick 0
eff_t0_4, // 4
eff_t0_a, // 5, same as A on first tick
eff_t0_a, // 6, same as A on first tick
eff_unimplemented_t0, // 7
eff_t0_7, // 7
eff_t0_8, // 8
eff_t0_9, // 9
eff_t0_a, // a
Expand Down Expand Up @@ -329,7 +376,7 @@ player.effects_t1 = [ // effect functions on tick 1+
eff_t1_4,
eff_t1_5, // 5
eff_t1_6, // 6
eff_unimplemented, // 7
eff_t1_7, // 7
null, // 8
null, // 9
eff_t1_a, // a
Expand Down