diff --git a/test/all.js b/test/all.js index c12d2a8..a21d2bd 100644 --- a/test/all.js +++ b/test/all.js @@ -1,65 +1,19 @@ window = {}; -require('../xm.js', window); -require('../xmeffects.js', window); - +require('../xm.js'); +require('../xmeffects.js'); var XMPlayer = window.XMPlayer; -// set up basic blank single-channel, single-pattern XM -function ResetXMData() { - var xm = {}; - window.XMPlayer.xm = xm; - xm.channelinfo = []; - xm.songname = "test song"; - xm.song_looppos = 0; - xm.nchan = 1; - xm.flags = 1; - xm.tempo = 3; - xm.bpm = 125; - xm.channelinfo.push({ - number: 0, - filterstate: new Float32Array(3), - vol: 0, - pan: 128, - period: 1920 - 48*16, - vL: 0, vR: 0, - vLprev: 0, vRprev: 0, - mute: 0, - volE: 0, panE: 0, - retrig: 0, - vibratodepth: 1, - vibratospeed: 1, - }); - xm.songpats = [0]; - // 1 channel, 2 row blank pattern - xm.patterns = [ - [[[-1, -1, -1, 0, 0]]], - [[[-1, -1, -1, 0, 0]]]]; - xm.instruments = []; - xm.instruments.push({ - 'name': "test instrument", - 'number': 0, - 'samples': [ - { 'len': 256, 'loop': 0, - 'looplen': 256, 'note': 0, 'fine': 0, - 'pan': 128, 'type': 0, 'vol': 64, - 'sampledata': new Float32Array(256) - } - ], - 'samplemap': new Uint8Array(96), - 'env_vol': new XMPlayer.Envelope([0, 64, 1, 0], 2, 0, 0, 0), - 'env_pan': new XMPlayer.Envelope([0, 32], 0, 0, 0, 0) - }); - // reset song position - XMPlayer.cur_songpos = -1; - XMPlayer.cur_pat = -1; - XMPlayer.cur_tick = xm.tempo; - return xm; -} +tests = {}; +testdata = require('./testdata.js'); +// test TODO: +// - audio output sample positions after ticks +// - unit test for envelopes +// // using assert passed to the test function that just logs failures -exports['test XM startup'] = function(assert) { - ResetXMData(); +tests['test XM startup'] = function(assert) { + testdata.resetXMData(); XMPlayer.nextTick(); assert.equal(XMPlayer.cur_songpos, 0, 'advance to initial song position'); assert.equal(XMPlayer.cur_pat, 0, 'advance to pattern 0'); @@ -67,16 +21,7 @@ exports['test XM startup'] = function(assert) { assert.equal(XMPlayer.cur_row, 1, 'advance to row 1'); }; -exports['test note on'] = function(assert) { - var xm = ResetXMData(); - // [row][column][channel] - xm.patterns[0][0][0] = [48, 1, 0x10 + 33, 0, 0]; // C-4 1 40 000 - XMPlayer.nextTick(); - var ch = xm.channelinfo[0]; - assert.equal(ch.note, 48, 'set note'); - assert.equal(ch.period, 1152, 'channel period'); - assert.equal(ch.vol, 33, 'channel volume'); - assert.equal(ch.samp, xm.instruments[0].samples[0], 'set sample'); -}; +require('./instrument.js'); +require('./effects.js'); -if (module == require.main) require('test').run(exports); +if (module == require.main) require('test').run(tests); diff --git a/test/effects.js b/test/effects.js new file mode 100644 index 0000000..2993de8 --- /dev/null +++ b/test/effects.js @@ -0,0 +1,85 @@ +var XMPlayer = window.XMPlayer; + +// TODO: +// - portamento 3xx +// - vibrato 4xy +// - volume slide Axy +// - porta+vol 5xy +// - etc etc + +tests['test 0xy arpeggio'] = function(assert) { + var xm = testdata.resetXMData(); + // [pat][row][channel] + xm.patterns[0][0][0] = [48, 1, -1, 0, 0x4f]; // C-4 1 -- 04f + XMPlayer.xm.tempo = 4; + XMPlayer.nextTick(); + var ch = xm.channelinfo[0]; + assert.equal(ch.note, 48, 'note 0'); + assert.equal(ch.period, 1152, 'row 0 tick 0 period 0'); + XMPlayer.nextTick(); + assert.equal(ch.period, 1152 - 16*4, 'row 0 tick 1 period 4'); + XMPlayer.nextTick(); + assert.equal(ch.period, 1152 - 16*15, 'row 0 tick 2 period f'); + XMPlayer.nextTick(); + assert.equal(ch.period, 1152 - 16*0, 'row 0 tick 3 period 0'); + XMPlayer.nextTick(); + assert.equal(ch.period, 1152 - 16*0, 'row 1 tick 0 period 0'); +}; + +tests['test 1xx slide up'] = function(assert) { + var xm = testdata.resetXMData(); + // [pat][row][channel] + xm.patterns[0][0][0] = [48, 1, -1, 1, 0x01]; // C-4 1 -- 101 + xm.patterns[0][1][0] = [-1, -1, -1, 1, 0x00]; // --- -- -- 100 + XMPlayer.xm.tempo = 3; + XMPlayer.nextTick(); + var ch = xm.channelinfo[0]; + assert.equal(ch.period, 1152, 'row 0 tick 0 period 0'); + XMPlayer.nextTick(); + assert.equal(ch.period, 1152 - 1, 'row 0 tick 1 period -1'); + XMPlayer.nextTick(); + assert.equal(ch.period, 1152 - 2, 'row 0 tick 2 period -2'); + XMPlayer.nextTick(); + assert.equal(ch.period, 1152 - 2, 'row 1 tick 0 period -2'); + XMPlayer.nextTick(); + assert.equal(ch.period, 1152 - 3, 'row 1 tick 1 period -3'); +}; + +tests['test 2xx slide down'] = function(assert) { + var xm = testdata.resetXMData(); + // [pat][row][channel] + xm.patterns[0][0][0] = [48, 1, -1, 2, 0x01]; // C-4 1 -- 201 + xm.patterns[0][1][0] = [-1, -1, -1, 2, 0x00]; // --- -- -- 200 + XMPlayer.xm.tempo = 3; + XMPlayer.nextTick(); + var ch = xm.channelinfo[0]; + assert.equal(ch.period, 1152, 'row 0 tick 0 period 0'); + XMPlayer.nextTick(); + assert.equal(ch.period, 1152 + 1, 'row 0 tick 1 period -1'); + XMPlayer.nextTick(); + assert.equal(ch.period, 1152 + 2, 'row 0 tick 2 period -2'); + XMPlayer.nextTick(); + assert.equal(ch.period, 1152 + 2, 'row 1 tick 0 period -2'); + XMPlayer.nextTick(); + assert.equal(ch.period, 1152 + 3, 'row 1 tick 1 period -3'); +}; + +tests['test 3xx portamento'] = function(assert) { + var xm = testdata.resetXMData(); + // [pat][row][channel] + xm.patterns[0][0][0] = [48, 1, -1, 0, 0x00]; // C-4 1 -- 000 + xm.patterns[0][1][0] = [49, 1, -1, 3, 0x09]; // C#4 1 -- 309 + XMPlayer.xm.tempo = 3; + XMPlayer.nextTick(); // row 0 tick 0 + var ch = xm.channelinfo[0]; + assert.equal(ch.period, 1152, 'row 0 tick 0 period 0'); + XMPlayer.nextTick(); // row 0 tick 1 + XMPlayer.nextTick(); // row 0 tick 2 + XMPlayer.nextTick(); // row 1 tick 0 + assert.equal(ch.period, 1152, 'row 1 tick 0 period 0'); + XMPlayer.nextTick(); + assert.equal(ch.period, 1152 - 9, 'row 1 tick 1 period -9'); + XMPlayer.nextTick(); + assert.equal(ch.period, 1152 - 16, 'row 1 tick 2 period -16'); +}; + diff --git a/test/instrument.js b/test/instrument.js new file mode 100644 index 0000000..929ee7e --- /dev/null +++ b/test/instrument.js @@ -0,0 +1,55 @@ +var XMPlayer = window.XMPlayer; + +// TODO: +// - test volume envelope (sustain, release) +// - test panning envelope + +tests['test note on'] = function(assert) { + var xm = testdata.resetXMData(); + // [pat][row][channel] + xm.patterns[0][0][0] = [48, 1, 0x10 + 0x33, 0, 0]; // C-4 1 33 000 + XMPlayer.nextRow(); + var ch = xm.channelinfo[0]; + assert.equal(ch.note, 48, 'set note'); + assert.equal(ch.period, 1152, 'channel period'); + assert.equal(ch.vol, 0x33, 'channel volume'); + assert.equal(ch.samp, xm.instruments[0].samples[0], 'set sample'); + assert.equal(ch.off, 0, 'trigger offset'); +}; + +tests['test instrument trigger'] = function(assert) { + var xm = testdata.resetXMData(); + // [pat][row][channel] + xm.patterns[0][0][0] = [48, 1, 0x10 + 0x33, 0, 0]; // C-4 1 33 000 + xm.patterns[0][1][0] = [-1, 1, -1, 0, 0]; // --- 1 -- 000 + var ch = xm.channelinfo[0]; + XMPlayer.nextRow(); + ch.pan = 1; // forcibly override panning + ch.off = 100; // and sample offset + assert.equal(XMPlayer.cur_row, 1, 'row 1'); + XMPlayer.nextRow(); + assert.equal(XMPlayer.cur_row, 2, 'row 2'); + assert.equal(ch.note, 48, 'note same after inst trigger'); + assert.equal(ch.period, 1152, 'period same after inst trigger'); + assert.equal(ch.vol, 64, 'vol reset after inst trigger'); + assert.equal(ch.pan, 128, 'pan reset after inst trigger'); + assert.equal(ch.samp, xm.instruments[0].samples[0], 'set sample'); + assert.equal(ch.off, 0, 'set offset=0'); +}; + +tests['test note trigger'] = function(assert) { + var xm = testdata.resetXMData(); + // [pat][row][channel] + xm.patterns[0][0][0] = [48, 1, 0x10 + 0x33, 0, 0]; // C-4 1 33 000 + xm.patterns[0][1][0] = [49, -1, -1, 0, 0]; // C#4 -- -- 000 + var ch = xm.channelinfo[0]; + XMPlayer.nextRow(); + ch.pan = 1; // forcibly override panning + ch.off = 100; // and sample offset + XMPlayer.nextRow(); + assert.equal(ch.note, 49, 'note updated after inst trigger'); + assert.equal(ch.period, 1136, 'period updated after note trigger'); + assert.equal(ch.vol, 0x33, 'vol not reset after note trigger'); + assert.equal(ch.pan, 1, 'pan not reset after note trigger'); + assert.equal(ch.off, 0, 'set offset=0'); +}; diff --git a/test/testdata.js b/test/testdata.js new file mode 100644 index 0000000..ed04dec --- /dev/null +++ b/test/testdata.js @@ -0,0 +1,56 @@ +var XMPlayer = window.XMPlayer; + +// set up basic blank single-channel, single-pattern XM +exports.resetXMData = function() { + var xm = {}; + window.XMPlayer.xm = xm; + xm.channelinfo = []; + xm.songname = "test song"; + xm.song_looppos = 0; + xm.nchan = 1; + xm.flags = 1; + xm.tempo = 3; + xm.bpm = 125; + xm.channelinfo.push({ + number: 0, + filterstate: new Float32Array(3), + vol: 0, + pan: 128, + period: 1920 - 48*16, + vL: 0, vR: 0, + vLprev: 0, vRprev: 0, + mute: 0, + volE: 0, panE: 0, + retrig: 0, + vibratodepth: 1, + vibratospeed: 1, + }); + xm.songpats = [0]; + // 1 channel, 2 row blank pattern + xm.patterns = [ + [ + [[-1, -1, -1, 0, 0]], + [[-1, -1, -1, 0, 0]] + ] + ]; + xm.instruments = []; + xm.instruments.push({ + 'name': "test instrument", + 'number': 0, + 'samples': [ + { 'len': 256, 'loop': 0, + 'looplen': 256, 'note': 0, 'fine': 0, + 'pan': 128, 'type': 0, 'vol': 64, + 'sampledata': new Float32Array(256) + } + ], + 'samplemap': new Uint8Array(96), + 'env_vol': new XMPlayer.Envelope([0, 64, 1, 0], 2, 0, 0, 0), + 'env_pan': new XMPlayer.Envelope([0, 32], 0, 0, 0, 0) + }); + // reset song position + XMPlayer.cur_songpos = -1; + XMPlayer.cur_pat = -1; + XMPlayer.cur_tick = xm.tempo; + return xm; +}; diff --git a/xm.js b/xm.js index 7f0b989..5b28d9f 100644 --- a/xm.js +++ b/xm.js @@ -25,6 +25,7 @@ player.xm = {}; // contains all song data // exposed for testing player.nextTick = nextTick; +player.nextRow = nextRow; player.Envelope = Envelope; // for pretty-printing notes