Merge pull request #21 from hillerstorm/implement_E4x
Implement E4x and address some vibrato issues
This commit is contained in:
commit
7153fa7f3c
7 changed files with 216 additions and 22 deletions
|
|
@ -72,7 +72,7 @@
|
|||
code: <a href="http://github.com/a1k0n/jsxm/">github.com/a1k0n/jsxm</a>
|
||||
todo:
|
||||
- missing XM effects:
|
||||
- E3x, E4x, E6x, E7x, E9x, EDx, EEx
|
||||
- E3x, E6x, E7x, E9x, EDx, EEx
|
||||
- 7xy - tremolo
|
||||
- Kxx, Lxx, Pxy, Txy
|
||||
- render pattern with the wider fonts for fewer channels
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@
|
|||
"repository": "a1k0n/jsxm",
|
||||
"author": "Andy Sloane <andy@a1k0n.net> (http://www.a1k0n.net/)",
|
||||
"license": "MIT",
|
||||
|
||||
"contributors": [{
|
||||
"name": "Johan Hillerström",
|
||||
"email": "progr@mmer.nu"
|
||||
}],
|
||||
"main": "index.html",
|
||||
"window": {
|
||||
"frame": true,
|
||||
|
|
|
|||
191
test/effects.js
vendored
191
test/effects.js
vendored
|
|
@ -85,7 +85,7 @@ exports['test 3xx portamento'] = function(assert) {
|
|||
assert.equal(ch.period, 1152 - 16, 'row 1 tick 2 period -16');
|
||||
};
|
||||
|
||||
exports['test 4xy vibrato'] = function(assert) {
|
||||
exports['test 4xy vibrato - sine'] = function(assert) {
|
||||
var xm = testdata.resetXMData();
|
||||
// vibrato 4xy: speed x, depth y
|
||||
// full cycle is 64/speed
|
||||
|
|
@ -107,37 +107,37 @@ exports['test 4xy vibrato'] = function(assert) {
|
|||
XMPlayer.nextTick(); // row 0 tick 1
|
||||
// compute logical period p from actual play frequency
|
||||
var p = 16*12 * Math.log(p0 / ch.doff) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "0.707", 'row 0 tick 1 period +0.707');
|
||||
assert.equal(p.toFixed(3), "0.000", 'row 0 tick 1 period +0');
|
||||
XMPlayer.nextTick(); // row 0 tick 2
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "1.000", 'row 0 tick 2 period +1.000');
|
||||
assert.equal(p.toFixed(3), "1.414", 'row 0 tick 2 period +1.414');
|
||||
XMPlayer.nextTick(); // row 1 tick 0
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "1.414", 'row 1 tick 0 period +1.414');
|
||||
assert.equal(p.toFixed(3), "4.000", 'row 1 tick 0 period +4.000');
|
||||
XMPlayer.nextTick(); // row 1 tick 1
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "0.000", 'row 1 tick 1 period +0');
|
||||
assert.equal(p.toFixed(3), "4.000", 'row 1 tick 1 period +4.000');
|
||||
XMPlayer.nextTick(); // row 1 tick 2
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-1.414", 'row 1 tick 2 period -1.414');
|
||||
assert.equal(p.toFixed(3), "2.828", 'row 1 tick 2 period 2.828');
|
||||
XMPlayer.nextTick(); // row 2 tick 0
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-2.000", 'row 2 tick 0 period -2.000');
|
||||
assert.equal(p.toFixed(3), "0.000", 'row 2 tick 0 period +0');
|
||||
XMPlayer.nextTick(); // row 2 tick 1
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-1.990", 'row 2 tick 1 period -1.990');
|
||||
assert.equal(p.toFixed(3), "0.000", 'row 2 tick 1 period +0');
|
||||
XMPlayer.nextTick(); // row 2 tick 2
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-1.962", 'row 2 tick 2 period -1.962');
|
||||
assert.equal(p.toFixed(3), "-0.392", 'row 2 tick 2 period -0.392');
|
||||
XMPlayer.nextTick(); // row 3 tick 0
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-1.914", 'row 3 tick 0 period -1.914');
|
||||
assert.equal(p.toFixed(3), "-0.780", 'row 3 tick 0 period -0.780');
|
||||
XMPlayer.nextTick(); // row 3 tick 1
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-1.848", 'row 3 tick 1 period -1.848');
|
||||
assert.equal(p.toFixed(3), "-0.780", 'row 3 tick 1 period -0.780');
|
||||
XMPlayer.nextTick(); // row 3 tick 2
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-1.764", 'row 3 tick 2 period -1.764');
|
||||
assert.equal(p.toFixed(3), "-1.161", 'row 3 tick 2 period -1.161');
|
||||
XMPlayer.nextTick(); // row 4 tick 0
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "0.000", 'row 4 tick 0 period 0 - no vibrato');
|
||||
|
|
@ -148,16 +148,153 @@ exports['test 4xy vibrato'] = function(assert) {
|
|||
// that's what I'm assuming here...
|
||||
XMPlayer.nextTick(); // row 5 tick 0
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-1.663", 'row 5 tick 0 period -1.663 - vibrato resume');
|
||||
assert.equal(p.toFixed(3), "-1.531", 'row 5 tick 0 period -1.531 - vibrato resume');
|
||||
XMPlayer.nextTick(); // row 5 tick 1
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-1.546", 'row 5 tick 1 period -1.546');
|
||||
assert.equal(p.toFixed(3), "-1.531", 'row 5 tick 1 period -1.531');
|
||||
XMPlayer.nextTick(); // row 5 tick 2
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-1.414", 'row 5 tick 2 period -1.414');
|
||||
assert.equal(p.toFixed(3), "-1.886", 'row 5 tick 2 period -1.886');
|
||||
XMPlayer.nextTick(); // row 6 tick 0
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-1.269", 'row 6 tick 0 period -1.269');
|
||||
assert.equal(p.toFixed(3), "-1.111", 'row 6 tick 0 period -1.111');
|
||||
};
|
||||
|
||||
exports['test 4xy vibrato - saw'] = function(assert) {
|
||||
var xm = testdata.resetXMData();
|
||||
// vibrato 4xy: speed x, depth y
|
||||
// full cycle is 64/speed
|
||||
xm.patterns[0] = [
|
||||
[[48, 1, -1, 14, 0x41]], // C-4 1 -- E41 - 1 = saw (ramp-down)
|
||||
[[-1, -1, -1, 4, 0x81]], // --- -- -- 481
|
||||
[[-1, -1, -1, 4, 0x00]], // --- -- -- 400
|
||||
[[-1, -1, -1, 0, 0x00]], // --- -- -- --- - no vibrato
|
||||
[[-1, -1, -1, 4, 0x00]], // --- -- -- 400 - resume vibrato @ pos 0
|
||||
];
|
||||
XMPlayer.xm.tempo = 4;
|
||||
var ch = xm.channelinfo[0];
|
||||
assert.equal(ch.vibratotype, 0, 'initial vibratotype 0');
|
||||
XMPlayer.nextTick(); // row 0 tick 0
|
||||
var p0 = ch.doff;
|
||||
assert.equal(ch.vibratotype, 1, 'row 0 tick 0 vibratotype=1');
|
||||
XMPlayer.nextTick(); // row 0 tick 1
|
||||
XMPlayer.nextTick(); // row 0 tick 2
|
||||
XMPlayer.nextTick(); // row 0 tick 3
|
||||
XMPlayer.nextTick(); // row 1 tick 0
|
||||
assert.equal(ch.periodoffset, 0, 'row 1 tick 0 periodoffset=0');
|
||||
XMPlayer.nextTick(); // row 1 tick 1
|
||||
// compute logical period p from actual play frequency
|
||||
var p = 16*12 * Math.log(p0 / ch.doff) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "0.000", 'row 1 tick 1 period +0');
|
||||
XMPlayer.nextTick(); // row 1 tick 2
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "0.500", 'row 1 tick 2 period +0.500');
|
||||
XMPlayer.nextTick(); // row 1 tick 3
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "1.000", 'row 1 tick 3 period +1.000');
|
||||
XMPlayer.nextTick(); // row 2 tick 0
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "1.500", 'row 2 tick 0 period +1.500');
|
||||
XMPlayer.nextTick(); // row 2 tick 1
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "1.500", 'row 2 tick 1 period +1.500');
|
||||
XMPlayer.nextTick(); // row 2 tick 2
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-2.000", 'row 2 tick 2 period -2.000');
|
||||
XMPlayer.nextTick(); // row 2 tick 3
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-1.500", 'row 2 tick 3 period -1.500');
|
||||
XMPlayer.nextTick(); // row 3 tick 0
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "0.000", 'row 3 tick 0 period 0 - no vibrato');
|
||||
XMPlayer.nextTick(); // row 3 tick 1
|
||||
XMPlayer.nextTick(); // row 3 tick 2
|
||||
XMPlayer.nextTick(); // row 3 tick 3
|
||||
XMPlayer.nextTick(); // row 4 tick 0
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-1.000", 'row 4 tick 0 period -1.000 - vibrato resume');
|
||||
XMPlayer.nextTick(); // row 4 tick 1
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-1.000", 'row 4 tick 1 period -1.000');
|
||||
XMPlayer.nextTick(); // row 4 tick 2
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-0.500", 'row 4 tick 2 period -0.500');
|
||||
XMPlayer.nextTick(); // row 4 tick 3
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "0.000", 'row 4 tick 3 period +0');
|
||||
};
|
||||
|
||||
exports['test 4xy vibrato - square'] = function(assert) {
|
||||
var xm = testdata.resetXMData();
|
||||
// vibrato 4xy: speed x, depth y
|
||||
// full cycle is 64/speed
|
||||
xm.patterns[0] = [
|
||||
[[48, 1, -1, 14, 0x42]], // C-4 1 -- E42 - 2 = square
|
||||
[[-1, -1, -1, 4, 0x81]], // --- -- -- 481
|
||||
[[-1, -1, -1, 4, 0x02]], // --- -- -- 402
|
||||
[[-1, -1, -1, 4, 0x10]], // --- -- -- 410
|
||||
[[-1, -1, -1, 4, 0x03]], // --- -- -- 403
|
||||
[[-1, -1, -1, 0, 0x00]], // --- -- -- --- - no vibrato
|
||||
[[-1, -1, -1, 4, 0x00]], // --- -- -- 400 - resume vibrato @ pos 0
|
||||
];
|
||||
XMPlayer.xm.tempo = 3;
|
||||
var ch = xm.channelinfo[0];
|
||||
assert.equal(ch.vibratotype, 0, 'initial vibratotype 0');
|
||||
XMPlayer.nextTick(); // row 0 tick 0
|
||||
var p0 = ch.doff;
|
||||
assert.equal(ch.vibratotype, 2, 'row 0 tick 0 vibratotype=2');
|
||||
XMPlayer.nextTick(); // row 0 tick 1
|
||||
XMPlayer.nextTick(); // row 0 tick 2
|
||||
XMPlayer.nextTick(); // row 1 tick 0
|
||||
assert.equal(ch.periodoffset, 2, 'row 1 tick 0 periodoffset=2');
|
||||
XMPlayer.nextTick(); // row 1 tick 1
|
||||
// compute logical period p from actual play frequency
|
||||
var p = 16*12 * Math.log(p0 / ch.doff) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "2.000", 'row 1 tick 1 period +2.000');
|
||||
XMPlayer.nextTick(); // row 1 tick 2
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "2.000", 'row 1 tick 2 period +2.000');
|
||||
XMPlayer.nextTick(); // row 2 tick 0
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "4.000", 'row 2 tick 0 period +4.000');
|
||||
XMPlayer.nextTick(); // row 2 tick 1
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "4.000", 'row 2 tick 1 period +4.000');
|
||||
XMPlayer.nextTick(); // row 2 tick 2
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "4.000", 'row 2 tick 2 period +4.000');
|
||||
XMPlayer.nextTick(); // row 3 tick 0
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-4.000", 'row 3 tick 0 period -4.000');
|
||||
XMPlayer.nextTick(); // row 3 tick 1
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-4.000", 'row 3 tick 1 period -4.000');
|
||||
XMPlayer.nextTick(); // row 3 tick 2
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-4.000", 'row 3 tick 2 period -4.000');
|
||||
XMPlayer.nextTick(); // row 4 tick 0
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-6.000", 'row 4 tick 0 period -6.000');
|
||||
XMPlayer.nextTick(); // row 4 tick 1
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-6.000", 'row 4 tick 1 period -6.000');
|
||||
XMPlayer.nextTick(); // row 4 tick 2
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-6.000", 'row 4 tick 2 period -6.000');
|
||||
XMPlayer.nextTick(); // row 4 tick 0
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "0.000", 'row 4 tick 0 period 0 - no vibrato');
|
||||
XMPlayer.nextTick(); // row 4 tick 1
|
||||
XMPlayer.nextTick(); // row 4 tick 2
|
||||
XMPlayer.nextTick(); // row 5 tick 0
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-6.000", 'row 5 tick 0 period -6.000 - vibrato resume');
|
||||
XMPlayer.nextTick(); // row 5 tick 1
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-6.000", 'row 5 tick 1 period -6.000');
|
||||
XMPlayer.nextTick(); // row 5 tick 2
|
||||
p = -16*12 * Math.log(ch.doff / p0) / Math.log(2);
|
||||
assert.equal(p.toFixed(3), "-6.000", 'row 5 tick 2 period -6.000');
|
||||
};
|
||||
|
||||
exports['test Axy volume slide'] = function(assert) {
|
||||
|
|
@ -322,6 +459,28 @@ exports['test Hxy global volume slide'] = function(assert) {
|
|||
assert.equal(xm.global_volume, 128, 'row 3 tick 2 vol 128');
|
||||
};
|
||||
|
||||
exports['test E4x set vibrato waveform'] = function(assert) {
|
||||
var xm = testdata.resetXMData();
|
||||
xm.patterns = [
|
||||
[
|
||||
[[48, 1, -1, 0, 0x00]], // C-4 1 -- --- (default waveform - sine)
|
||||
[[48, 1, -1, 14, 0x41]], // C-4 1 -- E41 (saw, ramp-down)
|
||||
[[48, 1, -1, 14, 0x42]], // C-4 1 -- E42 (square)
|
||||
[[48, 1, -1, 14, 0x43]] // C-4 1 -- E43 (random)
|
||||
]
|
||||
];
|
||||
xm.tempo = 1;
|
||||
var ch = xm.channelinfo[0];
|
||||
XMPlayer.nextTick();
|
||||
assert.equal(ch.vibratotype, 0, 'row 0 tick 0 vibratotype=0');
|
||||
XMPlayer.nextTick();
|
||||
assert.equal(ch.vibratotype, 1, 'row 1 tick 0 vibratotype=1');
|
||||
XMPlayer.nextTick();
|
||||
assert.equal(ch.vibratotype, 2, 'row 2 tick 0 vibratotype=2');
|
||||
XMPlayer.nextTick();
|
||||
assert.equal(ch.vibratotype, 3, 'row 3 tick 0 vibratotype=3');
|
||||
};
|
||||
|
||||
exports['test E5x finetune override'] = function(assert) {
|
||||
var xm = testdata.resetXMData();
|
||||
// set an initial finetune so we know we're overriding it...
|
||||
|
|
|
|||
|
|
@ -46,10 +46,12 @@ exports['test note trigger'] = function(assert) {
|
|||
XMPlayer.nextRow();
|
||||
ch.pan = 1; // forcibly override panning
|
||||
ch.off = 100; // and sample offset
|
||||
ch.vibratopos = 1; // and vibrato position
|
||||
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');
|
||||
assert.equal(ch.vibratopos, 0, 'set vibrato pos=0');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ exports.resetXMData = function() {
|
|||
vibratopos: 0,
|
||||
vibratodepth: 1,
|
||||
vibratospeed: 1,
|
||||
vibratotype: 0,
|
||||
});
|
||||
xm.songpats = [0];
|
||||
// 1 channel, 2 row blank pattern
|
||||
|
|
|
|||
5
xm.js
5
xm.js
|
|
@ -264,6 +264,10 @@ function nextRow() {
|
|||
if (ch.note) {
|
||||
ch.period = periodForNote(ch, ch.note);
|
||||
}
|
||||
// waveforms 0-3 are retriggered on new notes while 4-7 are continuous
|
||||
if (ch.vibratotype < 4) {
|
||||
ch.vibratopos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -697,6 +701,7 @@ function load(arrayBuf) {
|
|||
vibratopos: 0,
|
||||
vibratodepth: 1,
|
||||
vibratospeed: 1,
|
||||
vibratotype: 0,
|
||||
});
|
||||
}
|
||||
console.log("header len " + hlen);
|
||||
|
|
|
|||
28
xmeffects.js
28
xmeffects.js
|
|
@ -56,7 +56,7 @@ function eff_t1_3(ch) { // portamento
|
|||
|
||||
function eff_t0_4(ch, data) { // vibrato
|
||||
if (data & 0x0f) {
|
||||
ch.vibratodepth = data & 0x0f;
|
||||
ch.vibratodepth = (data & 0x0f) * 2;
|
||||
}
|
||||
if (data >> 4) {
|
||||
ch.vibratospeed = data >> 4;
|
||||
|
|
@ -65,14 +65,35 @@ function eff_t0_4(ch, data) { // vibrato
|
|||
}
|
||||
|
||||
function eff_t1_4(ch) { // vibrato
|
||||
ch.periodoffset = Math.sin(ch.vibratopos * Math.PI / 32) * ch.vibratodepth;
|
||||
ch.periodoffset = getVibratoDelta(ch.vibratotype, ch.vibratopos) * ch.vibratodepth;
|
||||
if (isNaN(ch.periodoffset)) {
|
||||
console.log("vibrato periodoffset NaN?",
|
||||
ch.vibratopos, ch.vibratospeed, ch.vibratodepth);
|
||||
ch.periodoffset = 0;
|
||||
}
|
||||
// only updates on non-first ticks
|
||||
if (player.cur_tick > 0) {
|
||||
ch.vibratopos += ch.vibratospeed;
|
||||
ch.vibratopos &= 63;
|
||||
}
|
||||
}
|
||||
|
||||
function getVibratoDelta(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_t1_5(ch) { // portamento + volume slide
|
||||
|
|
@ -135,6 +156,9 @@ function eff_t0_e(ch, data) { // extended effects!
|
|||
case 2: // fine porta down
|
||||
ch.period += data;
|
||||
break;
|
||||
case 4: // set vibrato waveform
|
||||
ch.vibratotype = data & 0x07;
|
||||
break;
|
||||
case 5: // finetune
|
||||
ch.fine = (data<<4) + data - 128;
|
||||
break;
|
||||
|
|
|
|||
Loading…
Reference in a new issue