implement instrument fadeout
i just hacked the fadeout directly into envelopes rather than complicating anything else.
This commit is contained in:
parent
2615c201e2
commit
efec2d7273
2 changed files with 27 additions and 9 deletions
|
|
@ -9,7 +9,6 @@ tune: my dirty old kamel - <a href="http://zalza.bandcamp.com">Zalza</a>
|
||||||
todo:
|
todo:
|
||||||
- unroll mixer code loops
|
- unroll mixer code loops
|
||||||
- multi-sample instruments
|
- multi-sample instruments
|
||||||
- instrument fadeout
|
|
||||||
- missing XM effects (in rough order of priority):
|
- missing XM effects (in rough order of priority):
|
||||||
- Rxy - retrigger
|
- Rxy - retrigger
|
||||||
- E3x, E4x, E5x, E6x, E7x, E9x, EDx, EEx
|
- E3x, E4x, E5x, E6x, E7x, E9x, EDx, EEx
|
||||||
|
|
|
||||||
35
xm.js
35
xm.js
|
|
@ -167,8 +167,8 @@ function next_row() {
|
||||||
// alone
|
// alone
|
||||||
ch.envtick = 0;
|
ch.envtick = 0;
|
||||||
ch.release = 0;
|
ch.release = 0;
|
||||||
ch.env_vol = new EnvelopeFollower(inst.env_vol);
|
ch.env_vol = new EnvelopeFollower(inst.env_vol, inst.fadeout);
|
||||||
ch.env_pan = new EnvelopeFollower(inst.env_pan);
|
ch.env_pan = new EnvelopeFollower(inst.env_pan, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,8 +177,8 @@ function next_row() {
|
||||||
ch.release = 0;
|
ch.release = 0;
|
||||||
ch.envtick = 0;
|
ch.envtick = 0;
|
||||||
ch.vibratopos = 0;
|
ch.vibratopos = 0;
|
||||||
ch.env_vol = new EnvelopeFollower(inst.env_vol);
|
ch.env_vol = new EnvelopeFollower(inst.env_vol, inst.fadeout);
|
||||||
ch.env_pan = new EnvelopeFollower(inst.env_pan);
|
ch.env_pan = new EnvelopeFollower(inst.env_pan, 0);
|
||||||
ch.period = PeriodForNote(ch, note);
|
ch.period = PeriodForNote(ch, note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -214,14 +214,22 @@ Envelope.prototype.Get = function(ticks) {
|
||||||
return y0;
|
return y0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function EnvelopeFollower(env) {
|
function EnvelopeFollower(env, fadeout) {
|
||||||
this.env = env;
|
this.env = env;
|
||||||
|
this.fadeout = 64.0 * fadeout / 65536.0;
|
||||||
this.tick = 0;
|
this.tick = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
EnvelopeFollower.prototype.Tick = function(release, defaultval) {
|
EnvelopeFollower.prototype.Tick = function(release, defaultval) {
|
||||||
|
// no envelope, just emit full volume
|
||||||
if (this.env === undefined) {
|
if (this.env === undefined) {
|
||||||
return defaultval;
|
if (release) {
|
||||||
|
var value = Math.min(0, defaultval - this.tick * this.fadeout);
|
||||||
|
this.tick++;
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
return defaultval;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var value = this.env.Get(this.tick);
|
var value = this.env.Get(this.tick);
|
||||||
if (this.env.type & 1) { // sustain?
|
if (this.env.type & 1) { // sustain?
|
||||||
|
|
@ -409,7 +417,7 @@ function audio_cb(e) {
|
||||||
var canvas = document.getElementById("vu");
|
var canvas = document.getElementById("vu");
|
||||||
var ctx = canvas.getContext("2d");
|
var ctx = canvas.getContext("2d");
|
||||||
ctx.fillStyle = '#000';
|
ctx.fillStyle = '#000';
|
||||||
ctx.fillRect(0, 0, 300, 64);
|
ctx.fillRect(0, 0, 16 * nchan, 64);
|
||||||
ctx.fillStyle = '#0f0';
|
ctx.fillStyle = '#0f0';
|
||||||
for (var j = 0; j < nchan; j++) {
|
for (var j = 0; j < nchan; j++) {
|
||||||
var rms = VU[j] / e.outputBuffer.length;
|
var rms = VU[j] / e.outputBuffer.length;
|
||||||
|
|
@ -669,6 +677,8 @@ function ConvertSample(array, bits) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optimization: unroll short sample loops so we can run our inner mixing loop
|
||||||
|
// uninterrupted for as long as possible; this also handles pingpong loops.
|
||||||
function UnrollSampleLoop(inst) {
|
function UnrollSampleLoop(inst) {
|
||||||
var nloops = ((2048 + inst.looplen - 1) / inst.looplen) | 0;
|
var nloops = ((2048 + inst.looplen - 1) / inst.looplen) | 0;
|
||||||
var pingpong = inst.type & 2;
|
var pingpong = inst.type & 2;
|
||||||
|
|
@ -711,6 +721,7 @@ function playXM(arrayBuf) {
|
||||||
var flags = dv.getUint16(0x4a, true);
|
var flags = dv.getUint16(0x4a, true);
|
||||||
tempo = dv.getUint16(0x4c, true);
|
tempo = dv.getUint16(0x4c, true);
|
||||||
bpm = dv.getUint16(0x4e, true);
|
bpm = dv.getUint16(0x4e, true);
|
||||||
|
document.getElementById('vu').width = 16 * nchan;
|
||||||
for (var i = 0; i < nchan; i++) {
|
for (var i = 0; i < nchan; i++) {
|
||||||
channelinfo.push({
|
channelinfo.push({
|
||||||
filterstate: new Float32Array(3),
|
filterstate: new Float32Array(3),
|
||||||
|
|
@ -802,6 +813,7 @@ function playXM(arrayBuf) {
|
||||||
var env_pan_sustain = dv.getUint8(idx+230);
|
var env_pan_sustain = dv.getUint8(idx+230);
|
||||||
var env_pan_loop_start = dv.getUint8(idx+231);
|
var env_pan_loop_start = dv.getUint8(idx+231);
|
||||||
var env_pan_loop_end = dv.getUint8(idx+232);
|
var env_pan_loop_end = dv.getUint8(idx+232);
|
||||||
|
var vol_fadeout = dv.getUint16(idx+239);
|
||||||
var env_vol = [];
|
var env_vol = [];
|
||||||
for (var j = 0; j < env_nvol*2; j++) {
|
for (var j = 0; j < env_nvol*2; j++) {
|
||||||
env_vol.push(dv.getUint16(idx+129+j*2, true));
|
env_vol.push(dv.getUint16(idx+129+j*2, true));
|
||||||
|
|
@ -833,7 +845,8 @@ function playXM(arrayBuf) {
|
||||||
console.log(" type %d note %s(%d) finetune %d pan %d",
|
console.log(" type %d note %s(%d) finetune %d pan %d",
|
||||||
samptype, prettify_note(sampnote + 12*4), sampnote, sampfinetune, samppan);
|
samptype, prettify_note(sampnote + 12*4), sampnote, sampfinetune, samppan);
|
||||||
console.log(" vol env", env_vol, env_vol_sustain,
|
console.log(" vol env", env_vol, env_vol_sustain,
|
||||||
env_vol_loop_start, env_vol_loop_end, "type", env_vol_type);
|
env_vol_loop_start, env_vol_loop_end, "type", env_vol_type,
|
||||||
|
"fadeout", vol_fadeout);
|
||||||
console.log(" pan env", env_pan, env_pan_sustain,
|
console.log(" pan env", env_pan, env_pan_sustain,
|
||||||
env_pan_loop_start, env_pan_loop_end, "type", env_pan_type);
|
env_pan_loop_start, env_pan_loop_end, "type", env_pan_type);
|
||||||
idx += samphdrsiz;
|
idx += samphdrsiz;
|
||||||
|
|
@ -846,6 +859,7 @@ function playXM(arrayBuf) {
|
||||||
'looplen': samplooplen, 'note': sampnote, 'fine': sampfinetune,
|
'looplen': samplooplen, 'note': sampnote, 'fine': sampfinetune,
|
||||||
'pan': samppan, 'type': samptype, 'vol': sampvol,
|
'pan': samppan, 'type': samptype, 'vol': sampvol,
|
||||||
'fine': sampfinetune,
|
'fine': sampfinetune,
|
||||||
|
'fadeout': vol_fadeout,
|
||||||
'sampledata': ConvertSample(new Uint8Array(arrayBuf, sampleoffset, samplen), samptype & 16),
|
'sampledata': ConvertSample(new Uint8Array(arrayBuf, sampleoffset, samplen), samptype & 16),
|
||||||
};
|
};
|
||||||
if (samptype & 16) {
|
if (samptype & 16) {
|
||||||
|
|
@ -860,6 +874,11 @@ function playXM(arrayBuf) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (env_vol_type) {
|
if (env_vol_type) {
|
||||||
|
// insert an automatic fadeout to 0 at the end of the envelope
|
||||||
|
var env_end_tick = env_vol[env_vol.length-2];
|
||||||
|
var fadeout_ticks = 65536.0 / inst.fadeout;
|
||||||
|
env_vol.push(env_end_tick + fadeout_ticks);
|
||||||
|
env_vol.push(0);
|
||||||
inst.env_vol = new Envelope(
|
inst.env_vol = new Envelope(
|
||||||
env_vol,
|
env_vol,
|
||||||
env_vol_type,
|
env_vol_type,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue