我要实现的最后两个效果与注释时间有关。第一个称为Retrigger Note,可用于从一定数量的刻度后从一开始就重新启动音符,并在一行中继续重复。第二个称为“延迟音符”,用于将音符的开始延迟一定数量。这两种效果都用于为音乐增添更多感觉,并经常添加到鼓声或低音音符中。
-
效果0'arpeggio of> (完成) em> -
效果1 - 滑动(完成) -
效果2 - 滑动(完成) -
效果3 - 音portamento del> (完成) em> -
效果4颤音 of> (完成) em> -
效果5 –音调门 +音量幻灯片 (完成) - <> <>效应6 vibrato +卷幻灯片 li>
-
效果9样品偏移 of> (完成) em> -
效果a卷幻灯片(完成) -
效果câset卷(完成) -
效果D图案断裂(完成) - 效果e9 retigger Note
- 效果d延迟注意
-
效果f - 设置速度(完成)
效果e9 tracigert注释
要重新验证注释,我需要检查每个tick是否由效果参数中指定的tick数除外。如果是这样,我可以通过将样本位置设置为0:
从一开始就开始样本
performTick() {
if (this.volumeSlide && this.worklet.tick > 0) {
// Unchanged
}
if (this.vibrato) {
// Unchanged
}
else if (this.periodDelta) {
// Unchanged
}
else if (this.arpeggio) {
// Unchanged
}
else if (this.retrigger && (this.worklet.tick % this.retrigger) == 0) {
this.sampleIndex = 0;
}
// Rest of performTick unchanged
}
effect(raw) {
// Add one line of initialization:
this.retrigger = false;
// Everything unchanged apart from one new case in the switch:
switch (id) {
// ...
case RETRIGGER_NOTE:
this.retrigger = data;
break;
效果ed延迟笔记
使用延迟音符效果时,在指定数量的刻度通过之前,不得中断当前的音符。为了实现这一目标,我需要首先准备仪器,音量和周期的更改中的单独变量,并且仅在延迟通过时才应用它们。这使得play(note)
方法更加复杂,但仍然很可读:
play(note) {
// Store the changes for later:
this.setInstrument = false;
this.setVolume = false;
this.setPeriod = false;
if (note.instrument) {
this.setInstrument = this.worklet.mod.instruments[note.instrument - 1];
this.setVolume = this.setInstrument.volume;
}
this.setSampleIndex = false;
this.setCurrentPeriod = false;
if (note.period) {
this.setPeriod = note.period - (this.setInstrument || this.instrument).finetune;
this.setCurrentPeriod = true;
this.setSampleIndex = 0;
}
this.effect(note.effect);
if (this.setInstrument) {
this.instrument = this.setInstrument;
}
if (this.setVolume !== false) {
this.volume = this.setVolume;
this.currentVolume = this.volume;
}
if (this.setPeriod) {
this.period = this.setPeriod;
}
if (this.setCurrentPeriod) {
this.currentPeriod = this.period;
}
if (this.setSampleIndex !== false) {
this.sampleIndex = this.setSampleIndex;
}
}
此更改后,卷控件有点损坏,但是仅需要对effect
方法进行一个小更改来修复它:
effect(raw) {
// ...
case SET_VOLUME:
this.setVolume = data;
break;
// ...
}
为了最终实施延迟,我需要对performTick
,play
和effect
方法进行一些小更改:
performTick() {
if (this.volumeSlide && this.worklet.tick > 0) {
// Unchanged
}
if (this.vibrato) {
// Unchanged
}
else if (this.periodDelta) {
// Unchanged
}
else if (this.arpeggio) {
// Unchanged
}
else if (this.retrigger && (this.worklet.tick % this.retrigger) == 0) {
// Unchanged
}
else if (this.delayNote === this.worklet.tick) {
this.instrument = this.setInstrument;
this.volume = this.setVolume;
this.currentVolume = this.volume;
this.period = this.setPeriod;
this.currentPeriod = this.period;
this.sampleIndex = 0;
}
// Rest of method unchanged
}
play(note) {
this.setInstrument = false;
this.setVolume = false;
this.setPeriod = false;
this.delayNote = false;
if (note.instrument) {
// Unchanged
}
this.setSampleIndex = false;
this.setCurrentPeriod = false;
if (note.period) {
// Unchanged
}
this.effect(note.effect);
// If note is delayed, do nothing right now
if (this.delayNote) return;
// Rest of method unchanged
}
effect(raw) {
// Add one more line of initialization:
this.delayNote = false;
// Everything unchanged apart from one new case in the switch, and
// a small but important change to SAMPLE_OFFSET:
switch (id) {
// ...
case DELAY_NOTE:
this.delayNote = data;
break;
// ...
case SAMPLE_OFFSET:
this.setSampleIndex = data * 256;
break;
// ...
}
}
修复一个潮汐错误
从头到尾听几首歌时,我注意到有些歌曲会在最后一遍又一遍地重复一次。这是因为我没有在任何地方存储歌曲的长度,因此玩家不知道什么时候停止。要解决此问题,我需要在Mod
类中添加length
成员变量,然后在PlayerWorklet
类的nextRow
方法中进行检查:
export class Mod {
constructor(modfile) {
// Store the song length
this.length = new Uint8Array(modfile, 950, 1)[0];
// The rest of the class is unchanged
class PlayerWorklet extends AudioWorkletProcessor {
// ...
nextRow() {
++this.rowIndex;
if (this.patternBreak !== false) {
this.rowIndex = this.patternBreak;
++this.position;
this.patternBreak = false;
}
else if (this.rowIndex == 64) {
this.rowIndex = 0;
++this.position;
}
if (this.position >= this.mod.length) {
this.position = 0;
}
结论
这样,玩家现在就可以播放我为支持的所有歌曲。我知道我还没有实施很多效果,但是到目前为止,我对进步感到满意。我对MOD格式的内部工作学历了很多,而且我也学到了很多有关Web音频API的信息。我期待实施更多效果,但我现在要休息一下。
制作该播放器的主要原因是在我的JavaScript remake of Arte by Sanity中使用Artsy。我将使用播放器进行两件事:首先,我将使用它在演示中播放音乐。其次,我将其用作定时源。因此,我将需要实现一种方法,以使播放器在音乐驱动的某些时间点触发事件。这将是下一篇文章的重点,这将是本系列的最后一篇。我希望在大约一周内发布它。在那之前,快乐的改装!
您可以在atornblad.github.io/js-mod-player中尝试此解决方案。
该代码的最新版本始终在the GitHub repository中可用。