I have the following problem: I'm trying to implement a music application with javascipt and etc. I've dissected the module architecture in Engine and UI. My Problem lies within the Engine Modules. Basically I have a main Engine module
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;
})();
A Class in order to load the sounds and create all rows
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]);
}
}
}
and another subclass in order to manage a single row per instrument
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;
}
}
}
My problem lies within the NoteRows. As you might see an object from the NoteRow class manages an entire row from a specific instrument with a specific note. Everything works just fine except I can't stop the playing loop with clearTimeout. Any advice? (likely without changing the entire architecture)
Add all of your timeout functions to an array.
When you want to stop them, just do it like this:
function stopTrackedTimeouts()
{
for(var i=0; i<timeouts.length; i++)
{
clearTimeout(timeouts[i]);
}
timeouts = [];
}
If you dont care about other timeouts running beside the ones you started, you can clear all timeouts by getting the the id of the latest timeout by adding a Null timeout.
function stopAllTimeouts()
{
var id = window.setTimeout(null,0);
while (id--)
{
window.clearTimeout(id);
}
}