implemented vibrato
added effects table to clean things up a bit
This commit is contained in:
parent
50c5513aeb
commit
653c80c88a
1 changed files with 140 additions and 38 deletions
178
xm.js
178
xm.js
|
|
@ -1,4 +1,6 @@
|
|||
var _note_names = ["C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-"];
|
||||
var f_smp = 44100; // updated by play callback, default value here
|
||||
|
||||
function prettify_note(note) {
|
||||
if (note < 0) return "---";
|
||||
if (note == 96) return "^^^";
|
||||
|
|
@ -77,7 +79,7 @@ function FilterCoeffs(f_c) {
|
|||
popfilter = FilterCoeffs(200.0 / 44100.0);
|
||||
popfilter_alpha = 0.9837;
|
||||
|
||||
function UpdateChannelPeriod(ch, period, f_smp) {
|
||||
function UpdateChannelPeriod(ch, period) {
|
||||
var freq = 8363 * Math.pow(2, (1152.0 - period) / 192.0);
|
||||
ch.doff = freq / f_smp;
|
||||
ch.filter = FilterCoeffs(ch.doff / 2);
|
||||
|
|
@ -87,15 +89,15 @@ function PeriodForNote(ch, note) {
|
|||
return 1920 - note*16 - ch.inst.fine / 8.0;
|
||||
}
|
||||
|
||||
function UpdateChannelNote(ch, note, f_smp) {
|
||||
function UpdateChannelNote(ch, note) {
|
||||
ch.period = PeriodForNote(ch, note);
|
||||
UpdateChannelPeriod(ch, ch.period, f_smp);
|
||||
UpdateChannelPeriod(ch, ch.period);
|
||||
}
|
||||
|
||||
var cur_songpos = -1, cur_pat = -1, cur_row = 64, cur_ticksamp = 0;
|
||||
var cur_tick = 6;
|
||||
var patdisplay = [];
|
||||
function next_row(f_smp) {
|
||||
function next_row() {
|
||||
if (cur_row >= 64) {
|
||||
cur_row = 0;
|
||||
cur_songpos++;
|
||||
|
|
@ -156,18 +158,19 @@ function next_row(f_smp) {
|
|||
|
||||
ch.effect = r[i][3];
|
||||
ch.effectdata = r[i][4];
|
||||
// TODO: process initial effect tick for effects which need it
|
||||
if (ch.effect == 1 && ch.effectdata != 0) {
|
||||
ch.slideupspeed = ch.effectdata;
|
||||
}
|
||||
if (ch.effect == 2 && ch.effectdata != 0) {
|
||||
ch.slidedownspeed = ch.effectdata;
|
||||
if (ch.effect < 16) {
|
||||
ch.effectfn = effects_t1[ch.effect];
|
||||
if (effects_t0[ch.effect](ch, ch.effectdata)) {
|
||||
triggernote = false;
|
||||
}
|
||||
} else {
|
||||
console.log("channel", i, "effect > 16", ch.effect);
|
||||
}
|
||||
|
||||
// special handling for portamentos: don't trigger the note
|
||||
if (ch.effect == 3 || ch.effect == 5) {
|
||||
if (r[i][0] != -1)
|
||||
if (r[i][0] != -1) {
|
||||
ch.periodtarget = PeriodForNote(ch, ch.note);
|
||||
if (ch.effect == 3 && ch.effectdata != 0) {
|
||||
ch.portaspeed = ch.effectdata;
|
||||
}
|
||||
triggernote = false;
|
||||
if (ch.release) {
|
||||
|
|
@ -177,11 +180,13 @@ function next_row(f_smp) {
|
|||
ch.release = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (triggernote) {
|
||||
ch.off = 0;
|
||||
ch.release = 0;
|
||||
ch.envtick = 0;
|
||||
UpdateChannelNote(ch, note, f_smp);
|
||||
ch.vibratopos = 0;
|
||||
UpdateChannelNote(ch, note);
|
||||
}
|
||||
}
|
||||
var debug = document.getElementById("debug");
|
||||
|
|
@ -195,36 +200,17 @@ function next_row(f_smp) {
|
|||
pat.innerHTML = patdisplay.join("\n");
|
||||
}
|
||||
|
||||
function next_tick(f_smp) {
|
||||
function next_tick() {
|
||||
cur_tick++;
|
||||
if (cur_tick >= tempo) {
|
||||
cur_tick = 0;
|
||||
next_row(f_smp);
|
||||
next_row();
|
||||
}
|
||||
for (var j = 0; j < nchan; j++) {
|
||||
var ch = channelinfo[j];
|
||||
var inst = ch.inst;
|
||||
// process effects
|
||||
if (ch.effect == 0 && ch.effectdata != 0) {
|
||||
var arpeggio = [0, ch.effectdata>>4, ch.effectdata&15];
|
||||
var note = ch.note + arpeggio[cur_tick % 3];
|
||||
UpdateChannelNote(ch, note, f_smp);
|
||||
}
|
||||
if (ch.effect == 1 && cur_tick != 0 && ch.slideupspeed !== undefined) {
|
||||
ch.period -= ch.slideupspeed;
|
||||
UpdateChannelPeriod(ch, ch.period, f_smp);
|
||||
}
|
||||
if (ch.effect == 2 && cur_tick != 0 && ch.slidedownspeed !== undefined) {
|
||||
ch.period += ch.slidedownspeed;
|
||||
UpdateChannelPeriod(ch, ch.period, f_smp);
|
||||
}
|
||||
if (ch.effect == 3 && cur_tick != 0 && ch.periodtarget !== undefined && ch.portaspeed !== undefined) {
|
||||
if (ch.period > ch.periodtarget) {
|
||||
ch.period = Math.max(ch.periodtarget, ch.period - ch.portaspeed);
|
||||
} else {
|
||||
ch.period = Math.min(ch.periodtarget, ch.period + ch.portaspeed);
|
||||
}
|
||||
UpdateChannelPeriod(ch, ch.period, f_smp);
|
||||
if (ch.effectfn) {
|
||||
ch.effectfn(ch);
|
||||
}
|
||||
if (inst === undefined) continue;
|
||||
if (inst.env_vol !== undefined) {
|
||||
|
|
@ -245,7 +231,7 @@ function next_tick(f_smp) {
|
|||
}
|
||||
|
||||
function audio_cb(e) {
|
||||
var f_smp = audioctx.sampleRate;
|
||||
f_smp = audioctx.sampleRate;
|
||||
var buflen = e.outputBuffer.length;
|
||||
var dataL = e.outputBuffer.getChannelData(0);
|
||||
var dataR = e.outputBuffer.getChannelData(1);
|
||||
|
|
@ -328,6 +314,122 @@ function audio_cb(e) {
|
|||
}
|
||||
}
|
||||
|
||||
function eff_t0_0(ch, data) {
|
||||
// nothing to do here, arpeggio will be done on ch.effectdata
|
||||
}
|
||||
|
||||
function eff_t0_1(ch, data) {
|
||||
if (data != 0) {
|
||||
ch.slideupspeed = data;
|
||||
}
|
||||
}
|
||||
|
||||
function eff_t0_2(ch, data) {
|
||||
if (data != 0) {
|
||||
ch.slidedownspeed = data;
|
||||
}
|
||||
}
|
||||
|
||||
function eff_t0_3(ch, data) {
|
||||
if (data != 0) {
|
||||
ch.portaspeed = data;
|
||||
}
|
||||
}
|
||||
|
||||
function eff_t0_4(ch, data) {
|
||||
if (data & 0x0f) {
|
||||
ch.vibratodepth = data & 0x0f;
|
||||
}
|
||||
if (data >> 4) {
|
||||
ch.vibratospeed = data >> 4;
|
||||
}
|
||||
eff_t1_4(ch, data);
|
||||
}
|
||||
|
||||
|
||||
function eff_t1_0(ch) {
|
||||
if (ch.effectdata != 0) {
|
||||
var arpeggio = [0, ch.effectdata>>4, ch.effectdata&15];
|
||||
var note = ch.note + arpeggio[cur_tick % 3];
|
||||
UpdateChannelNote(ch, note);
|
||||
}
|
||||
}
|
||||
|
||||
function eff_t1_1(ch) {
|
||||
if (ch.slideupspeed !== undefined) {
|
||||
ch.period -= ch.slideupspeed;
|
||||
UpdateChannelPeriod(ch, ch.period);
|
||||
}
|
||||
}
|
||||
|
||||
function eff_t1_2(ch) {
|
||||
if (ch.slidedownspeed !== undefined) {
|
||||
ch.period += ch.slidedownspeed;
|
||||
UpdateChannelPeriod(ch, ch.period);
|
||||
}
|
||||
}
|
||||
|
||||
function eff_t1_3(ch) {
|
||||
if (ch.periodtarget !== undefined && ch.portaspeed !== undefined) {
|
||||
if (ch.period > ch.periodtarget) {
|
||||
ch.period = Math.max(ch.periodtarget, ch.period - ch.portaspeed);
|
||||
} else {
|
||||
ch.period = Math.min(ch.periodtarget, ch.period + ch.portaspeed);
|
||||
}
|
||||
UpdateChannelPeriod(ch, ch.period);
|
||||
}
|
||||
}
|
||||
|
||||
function eff_t1_4(ch) {
|
||||
var v0 = Math.sin(ch.vibratopos * Math.PI / 32);
|
||||
ch.vibratopos += ch.vibratospeed;
|
||||
ch.vibratopos &= 63;
|
||||
var v1 = Math.sin(ch.vibratopos * Math.PI / 32);
|
||||
ch.period += (v1 - v0) * ch.vibratodepth;
|
||||
console.log("vibrato", ch.vibratospeed, ch.vibratodepth, ch.period);
|
||||
UpdateChannelPeriod(ch, ch.period);
|
||||
}
|
||||
|
||||
function eff_unimplemented(ch, data) {}
|
||||
|
||||
var effects_t0 = [ // effect functions on tick 0
|
||||
eff_t0_0,
|
||||
eff_t0_1,
|
||||
eff_t0_2,
|
||||
eff_t0_3,
|
||||
eff_t0_4,
|
||||
eff_unimplemented, // 5
|
||||
eff_unimplemented, // 6
|
||||
eff_unimplemented, // 7
|
||||
eff_unimplemented, // 8
|
||||
eff_unimplemented, // 9
|
||||
eff_unimplemented, // a
|
||||
eff_unimplemented, // b
|
||||
eff_unimplemented, // c
|
||||
eff_unimplemented, // d
|
||||
eff_unimplemented, // e
|
||||
eff_unimplemented, // f
|
||||
];
|
||||
|
||||
var effects_t1 = [ // effect functions on tick 1+
|
||||
eff_t1_0,
|
||||
eff_t1_1,
|
||||
eff_t1_2,
|
||||
eff_t1_3,
|
||||
eff_t1_4,
|
||||
eff_unimplemented, // 5
|
||||
eff_unimplemented, // 6
|
||||
eff_unimplemented, // 7
|
||||
eff_unimplemented, // 8
|
||||
eff_unimplemented, // 9
|
||||
eff_unimplemented, // a
|
||||
eff_unimplemented, // b
|
||||
eff_unimplemented, // c
|
||||
eff_unimplemented, // d
|
||||
eff_unimplemented, // e
|
||||
eff_unimplemented, // f
|
||||
];
|
||||
|
||||
function ConvertSample(array, bits) {
|
||||
var len = array.length;
|
||||
var acc = 0;
|
||||
|
|
|
|||
Loading…
Reference in a new issue