我有以下问题:我正在尝试使用 javascipt 等实现音乐应用程序。我已经剖析了引擎和 UI 中的模块架构。我的问题在于引擎模块。基本上我有一个主引擎模块
var NoteEngine = (function(){
var that = {},
matrices = [],
beatCount = 1,
globalBPM = 100,
init = function(count){
window.AudioContext = window.AudioContext || window.webkitAudioContext;
context = new AudioContext();
_registerListener();
beatCount = count;
matrices = [
new NoteMatrix("piano", (16 * beatCount), globalBPM),
new NoteMatrix("guitar", (16 * beatCount), globalBPM),
new NoteMatrix("bass", (16 * beatCount), globalBPM),
new NoteMatrix("bell", (16 * beatCount), globalBPM)
];
},
_registerListener = function(){
};
that.init = init;
return that;
})();
用于加载声音并创建所有行的类
function NoteMatrix(instrument, colCount, bpm){
var rows = [],
matrixInstrument = instrument,
bufferLoader,
bufferList;
_loadBuffer();
function _loadBuffer(){
var notePaths = _createNoteFilePaths();
bufferLoader = new BufferLoader(
context,
notePaths,
_finishedLoading);
bufferLoader.load();
}
function _createNoteFilePaths(){
var basePath = "res/notes/" + matrixInstrument + "/",
scale = ['C6', 'A5', 'G5', 'E5', 'D5', 'C5', 'A4', 'G4', 'E4', 'D4', 'C4', 'A3', 'G3', 'E3', 'D3', 'C3'],
result = [];
for(var i = 0; i < 16; i++){
result[i] = basePath + scale[i] + ".mp3";
}
return result;
}
function _finishedLoading(buffer){
$("body").trigger("MODULE_FINISHED");
bufferList = buffer;
_createMatrix();
}
function _createMatrix(){
for(var i = 0; i < 16; i++){
rows[i] = new NoteRow(matrixInstrument, colCount, bpm, i, (i*colCount), (((i+1)*colCount) - 1), bufferList[i]);
}
}
}
和另一个子类,以便管理每个仪器的单行
function NoteRow(instrument, loopLength, bpm, row, minID, maxID, buffer){
var noteBuffer = buffer, // Notenklang in Bufferform
gainNode = null, // Hauptknoten für Ausgabe (auch lautstärke)
volume, // Gesamtlautstärke
notes = [], // Enthält alle Notenzustände in der Schleife (taktübergreifend)
rowInstrument = instrument, // Instrumentname in Stringform (für Abgleiche)
timeoutID = null, // Zuständig für Wiederholung/Stop der Schleife
isPlaying = false, // Status ob Schleife spielt oder nicht
current16thNote = 0, // Aktuelle Position in der Schleife
rowBPM = bpm, // Tempo der Schleife
scheduleDelay = 0, // Verzögerung der Wiederholung der Planschleife (in ms)
scheduleAheadTime = 0.1, // Abdeckung der Planschleife (in s)
nextNoteTime = 0.0; // Startzeit der nächsten Note
_init();
_registerListener();
// Initialisiert die Notenreihe
function _init(){
gainNode = context.createGain();
volume = 2.5;
gainNode.gain.value = volume;
for(var i = 0; i < loopLength; i++){
notes[i] = false;
}
}
// Registriert alle Listener für die Notenreihe
function _registerListener(){
$("body").on("CELL_CLICKED", _toggleNote);
$("body").on("PLAY", _play);
$("body").on("STOP", _stop);
$("body").on("VOLUME_CHANGE", _changeVolume);
$("body").on("BPM_CHANGE", _changeBPM);
$("body").on("MUTE", _mute);
$("body").on("RESUME_SOUND", _resumeSound);
$("body").on("REFRESH_ALL", _refresh);
}
// Schaltet eine Note um
function _toggleNote(event, data){
if(data.instrument == rowInstrument && (data.id >= minID && data.id <= maxID)){
console.log(data);
notes[data.id - minID] = !notes[data.id - minID];
}
}
function _play(){
current16thNote = 0;
nextNoteTime = context.currentTime;
_startScheduler();
}
function _stop(){
clearTimeout(timeoutId);
}
function _handlePlayback(){
isPlaying = !isPlaying;
if(isPlaying) {
current16thNote = 0;
nextNoteTime = context.currentTime;
_startScheduler();
}else{
clearTimeout(timeoutId);
}
}
// Schaltet die Notenreihe stumm
function _mute(){
gainNode.gain.value = 0;
}
// Stellt die ursprüngliche Lautstärke der Notenreihe wieder her
function _resumeSound(){
gainNode.gain.value = volume;
}
// Setzt die Notenreihe zurück
function _refresh(){
for(var i = 0; i < notes.length; i++){
notes[i] = false;
}
}
// Ändert die Lautstärke der Notenreihe
function _changeVolume(event, data){
volume = data/20;
gainNode.gain.value = volume;
}
// Ändert das Tempo der Notenreihe
function _changeBPM(event, data){
rowBPM = data;
}
// Startet die Playback Schleife, die immer wieder abprüft,
// ob im vorgelegten Zeitraum eine Note abgespielt werden soll
function _startScheduler(){
while (nextNoteTime < context.currentTime + scheduleAheadTime ) {
_scheduleNote(current16thNote, nextNoteTime);
_nextNote();
}
timeoutId = setTimeout(_startScheduler, scheduleDelay);
}
// Spielt die Note und aktiviert die entsprechende Animation
function _scheduleNote(beatPosition, time){
if(notes[beatPosition]){
var voice = context.createBufferSource();
voice.buffer = noteBuffer;
voice.connect(gainNode);
gainNode.connect(context.destination);
voice.start(time);
$("#" + (minID + beatPosition)).addClass("animation");
setTimeout(function(){
$("#" + (minID + beatPosition)).removeClass("animation");
}, 100);
}
}
// Verschiebt die Position der Schleife nach vorne
// Abhängig vom Tempo legt es auch das Interval zur nächsten Note fest
function _nextNote(){
var secondsPerBeat = 60.0 / rowBPM;
nextNoteTime += 0.25 * secondsPerBeat;
current16thNote++;
if (current16thNote == loopLength) {
current16thNote = 0;
}
}
}
我的问题在于 NoteRows。您可能会看到 NoteRow 类中的一个对象管理来自具有特定音符的特定乐器的整行。一切正常,除了我无法用 clearTimeout 停止播放循环。有什么建议吗?(可能不会改变整个架构)