在下面的代码中,我有一个音符调度程序,它将名为current16thNote
的变量增加到16,然后循环回到1.应用程序的最终目标是允许用户单击鼓垫和将current16thNote
值推送到数组。在current16thNote
的每次迭代中,在轨道阵列上运行循环以查找当前current16thNote
值,如果它找到它将播放声音。
//_________________________________________________________General variable declarations
var isPlaying = false,
tempo = 120.0, // tempo (in beats per minute)
current16thNote = 1,
futureTickTime = 0.0,
timerID = 0,
noteLength = 0.05; // length of "beep" (in seconds)
//_________________________________________________________END General variable declarations
//_________________________________________________________Load sounds
var kick = audioFileLoader("sounds/kick.mp3"),
snare = audioFileLoader("sounds/snare.mp3"),
hihat = audioFileLoader("sounds/hihat.mp3"),
shaker = audioFileLoader("sounds/shaker.mp3");
//_________________________________________________________END Load sounds
//_________________________________________________________Track arrays
var track1 = [],
track2 = [5, 13],
track3 = [],
track4 = [1, 3, 5, 7, 9, 11, 13, 15];
//_________________________________________________________END Track arrays
//_________________________________________________________Future tick
function futureTick() {
var secondsPerBeat = 60.0 / tempo;
futureTickTime += 0.25 * secondsPerBeat;
current16thNote += 1;
if (current16thNote > 16) {
current16thNote = 1
}
}
//_________________________________________________________END Future tick
function checkIfRecordedAndPlay(trackArr, sound, beatDivisionNumber, time) {
for (var i = 0; i < trackArr.length; i += 1) {
if (beatDivisionNumber === trackArr[i]) {
sound.play(time);
}
}
}
//__________________________________________________________Schedule note
function scheduleNote(beatDivisionNumber, time) {
var osc = audioContext.createOscillator(); //____Metronome
if (beatDivisionNumber === 1) {
osc.frequency.value = 800;
} else {
osc.frequency.value = 400;
}
osc.connect(audioContext.destination);
osc.start(time);
osc.stop(time + noteLength);//___________________END Metronome
checkIfRecordedAndPlay(track1, kick, beatDivisionNumber, time);
checkIfRecordedAndPlay(track2, snare, beatDivisionNumber, time);
checkIfRecordedAndPlay(track3, hihat, beatDivisionNumber, time);
checkIfRecordedAndPlay(track4, shaker, beatDivisionNumber, time);
}
//_________________________________________________________END schedule note
//_________________________________________________________Scheduler
function scheduler() {
while (futureTickTime < audioContext.currentTime + 0.1) {
scheduleNote(current16thNote, futureTickTime);
futureTick();
}
timerID = window.requestAnimationFrame(scheduler);
}
//_________________________________________________________END Scheduler
问题 除了上面的代码,我还有一些用户界面控件,如下图所示。
当用户mousedowns
在“鼓垫”上时,我想做两件事。第一种是立即听到声音,第二种是将current16thNote
值推送到相应的数组。
如果我使用以下代码执行此操作,则会出现一些问题。
$("#kick").on("mousedown", function() {
kick.play(audioContext.currentTime)
track1.push(current16thNote)
})
第一个问题是声音播放两次。这是因为当声音被推送到数组时,它会立即被音符调度程序的下一次迭代识别并立即播放。我通过使用setInterval创建延迟来修复此问题,以将推送偏移到数组。
$("#kick").on("mousedown", function() {
kick.play(audioContext.currentTime)
window.setTimeout(function() {
track1.push(note)
}, 500)
})
第二个问题是音乐剧。
当用户点击鼓垫时,用户预期鼓将被记录的值是之前的第16个值。换句话说,如果你聆听节拍器并点击底鼓垫,意图降落在1/1向下拍上,这将不会发生。相反,当节拍器循环回来时,它将被“记录”在第16个增量之后。
这可以通过编写故意将推送到数组的值偏移-1的代码来解决。
我编写了一个名为pushNote
的辅助函数来执行此操作。
$("#kick").on("mousedown", function() {
var note = current16thNote;
kick.play(audioContext.currentTime)
window.setTimeout(function() {
pushNote(track1, note)
}, 500)
})
//________________________________________________Helper
function pushNote(trackArr, note) {
if (note - 1 === 0) {
trackArr.push(16)
} else {
trackArr.push(note - 1)
}
}
//________________________________________________END Helper
我的问题非常基本。有没有办法解决这个问题,而不创造这些奇怪的“抵消”?
我怀疑有一种方法可以设置/写入/放置current16thNote
增量而无需为程序的其他部分创建偏移量。但我对它可能是什么感到朦胧。
在专业音频录制的世界中,每16个分区值没有勾选,每个四分音符通常有480个刻度。我想开始探索使用这个更大的值来编写我的应用程序但是我想在我走下那个兔子洞之前解决这个“偏移”问题。