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