diff --git a/index.html b/index.html index 0dfe25d..7dca2ce 100644 --- a/index.html +++ b/index.html @@ -74,7 +74,7 @@ todo: - missing XM effects: - E3x, E4x, E5x, E6x, E7x, E9x, EDx, EEx - 7xy - tremolo - - Gxx, Hxy, Kxx, Lxx, Pxy, Txy + - Hxy, Kxx, Lxx, Pxy, Txy - render pattern with the wider fonts for fewer channels - fix occasional rendering/audio hiccups when switching songs diff --git a/test/effects.js b/test/effects.js index a8f15c6..19f1112 100644 --- a/test/effects.js +++ b/test/effects.js @@ -200,3 +200,26 @@ exports['test Axy volume slide'] = function(assert) { XMPlayer.nextTick(); // tick 2 assert.equal(ch.vol, 32, 'row 3 tick 2 vol 32'); }; + +exports['test Gxx global volume'] = function(assert) { + var xm = testdata.resetXMData(); + // [pat][row][channel] + // 1 channel, 3 row blank pattern + xm.patterns = [ + [ + [[48, 1, -1, 16, 0x40]], // C-4 1 -- G40 + [[48, 1, -1, 16, 0x2B]], // C-4 1 -- G2B + // test out of bounds volume + [[48, 1, -1, 16, 0x80]] // C-4 1 -- G80 + ] + ]; + XMPlayer.xm.tempo = 1; + XMPlayer.nextTick(); + // volume gets multiplied by 2 to match + // the initial max global volume of 128 + assert.equal(XMPlayer.xm.global_volume, 0x40*2, 'global volume set to 0x40'); + XMPlayer.nextTick(); + assert.equal(XMPlayer.xm.global_volume, 0x2B*2, 'global volume set to 0x2B'); + XMPlayer.nextTick(); + assert.equal(XMPlayer.xm.global_volume, 0x40*2, 'global volume set to 0x40'); +}; diff --git a/xm.js b/xm.js index 1552f82..61577bf 100644 --- a/xm.js +++ b/xm.js @@ -22,6 +22,7 @@ player.cur_row = 64; player.cur_ticksamp = 0; player.cur_tick = 6; player.xm = {}; // contains all song data +player.xm.global_volume = player.max_global_volume = 128; // exposed for testing player.nextTick = nextTick; @@ -393,8 +394,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 = volE * (128 - p) * ch.vol / 8192.0; - var volR = volE * (128 + p) * ch.vol / 8192.0; + 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); if (volL < 0) volL = 0; if (volR < 0) volR = 0; if (volR === 0 && volL === 0) @@ -669,6 +670,7 @@ function load(arrayBuf) { player.xm.tempo = dv.getUint16(0x4c, true); player.xm.bpm = dv.getUint16(0x4e, true); player.xm.channelinfo = []; + player.xm.global_volume = player.max_global_volume; var i, j, k; @@ -939,6 +941,7 @@ function stop() { player.cur_row = 64; player.cur_songpos = -1; player.cur_ticksamp = 0; + player.xm.global_volume = player.max_global_volume; if (XMView.stop) XMView.stop(); init(); } diff --git a/xmeffects.js b/xmeffects.js index 7791cb5..83d1535 100644 --- a/xmeffects.js +++ b/xmeffects.js @@ -178,6 +178,16 @@ function eff_t0_f(ch, data) { // set tempo } } +function eff_t0_g(ch, data) { // set global volume + if (data <= 0x40) { + // volume gets multiplied by 2 to match + // the initial max global volume of 128 + player.xm.global_volume = Math.max(0, data * 2); + } else { + player.xm.global_volume = player.max_global_volume; + } +} + function eff_t0_r(ch, data) { // retrigger if (data & 0x0f) ch.retrig = (ch.retrig & 0xf0) + (data & 0x0f); if (data & 0xf0) ch.retrig = (ch.retrig & 0x0f) + (data & 0xf0); @@ -230,7 +240,7 @@ player.effects_t0 = [ // effect functions on tick 0 eff_t0_d, // d eff_t0_e, // e eff_t0_f, // f - eff_unimplemented_t0, // g + eff_t0_g, // g eff_unimplemented_t0, // h eff_unimplemented_t0, // i eff_unimplemented_t0, // j @@ -269,7 +279,7 @@ player.effects_t1 = [ // effect functions on tick 1+ null, // d eff_t1_e, // e null, // f - eff_unimplemented, // g + null, // g eff_unimplemented, // h eff_unimplemented, // i eff_unimplemented, // j