1

我有以下问题:我正在尝试使用 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 停止播放循环。有什么建议吗?(可能不会改变整个架构)

4

1 回答 1

2

将所有超时函数添加到数组中。

当您想阻止它们时,只需这样做:

function stopTrackedTimeouts()

{
    for(var i=0; i<timeouts.length; i++)
    {
        clearTimeout(timeouts[i]);
    }
    timeouts = [];
}

如果您不关心在您启动的超时之外运行的其他超时,您可以通过添加 Null 超时来获取最新超时的 id 来清除所有超时。

function stopAllTimeouts()
{
    var id = window.setTimeout(null,0);
    while (id--) 
    {
        window.clearTimeout(id);
    }
}
于 2015-04-04T20:43:37.537 回答