// Copyright (C) 2013 Massachusetts Institute of Technology // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 2, // as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 'use strict'; var SoundPrims = function() {} SoundPrims.prototype.addPrimsTo = function(primTable) { primTable['playSound:'] = this.primPlaySound; primTable['doPlaySoundAndWait'] = this.primPlaySoundUntilDone; primTable['stopAllSounds'] = this.primStopAllSounds; primTable['playDrum'] = this.primPlayDrum; primTable['rest:elapsed:from:'] = this.primPlayRest; primTable['noteOn:duration:elapsed:from:'] = this.primPlayNote; primTable['instrument:'] = this.primSetInstrument; /*primTable['changeVolumeBy:'] = this.primChangeVolume; primTable['setVolumeTo:'] = this.primSetVolume; primTable['volume'] = this.primVolume;*/ primTable['changeTempoBy:'] = function(b) { runtime.stage.data.tempoBPM = runtime.stage.data.tempoBPM + interp.arg(b, 0); }; primTable['setTempoTo:'] = function(b) { runtime.stage.data.tempoBPM = interp.arg(b, 0); }; primTable['tempo'] = function(b) { return runtime.stage.data.tempoBPM; }; } var playSound = function(snd) { if (snd.source) { // If this particular sound is already playing, stop it. snd.source.noteOff(0); snd.source = null; } snd.source = runtime.audioContext.createBufferSource(); snd.source.buffer = snd.buffer; snd.source.connect(runtime.audioGain); // Track the sound's completion state snd.source.done = false; snd.source.finished = function() { // Remove from the active audio list and disconnect the source from // the sound dictionary. var i = runtime.audioPlaying.indexOf(snd); if (i > -1 && runtime.audioPlaying[i].source != null) { runtime.audioPlaying[i].source.done = true; runtime.audioPlaying[i].source = null; runtime.audioPlaying.splice(i, 1); } } window.setTimeout(snd.source.finished, snd.buffer.duration * 1000); // Add the global list of playing sounds and start playing. runtime.audioPlaying.push(snd); snd.source.noteOn(0); return snd.source; } var playDrum = function(drum, secs, client) { var player = SoundBank.getDrumPlayer(drum, secs); player.client = client; player.setDuration(secs); var source = runtime.audioContext.createScriptProcessor(4096, 1, 1); source.onaudioprocess = function(e) { player.writeSampleData(e); }; source.soundPlayer = player; source.connect(runtime.audioGain); runtime.notesPlaying.push(source); source.finished = function() { var i = runtime.notesPlaying.indexOf(source); if (i > -1 && runtime.notesPlaying[i] != null) { runtime.notesPlaying.splice(i, 1); } } window.setTimeout(source.finished, secs * 1000); return player; } var playNote = function(instrument, midiKey, secs, client) { var player = SoundBank.getNotePlayer(instrument, midiKey); player.client = client; player.setNoteAndDuration(midiKey, secs); var source = runtime.audioContext.createScriptProcessor(4096, 1, 1); source.onaudioprocess = function(e) { player.writeSampleData(e); }; source.connect(runtime.audioGain); runtime.notesPlaying.push(source); source.finished = function() { var i = runtime.notesPlaying.indexOf(source); if (i > -1 && runtime.notesPlaying[i] != null) { runtime.notesPlaying.splice(i, 1); } } window.setTimeout(source.finished, secs * 1000); return player; } var stopAllSounds = function() { var oldPlaying = runtime.audioPlaying; runtime.audioPlaying = []; for (var s = 0; s < oldPlaying.length; s++) { if (oldPlaying[s].source) { oldPlaying[s].source.noteOff(0); oldPlaying[s].source.finished(); } } var oldPlaying = runtime.notesPlaying; runtime.notesPlaying = []; for (var s = 0; s < oldPlaying.length; s++) { if (oldPlaying[s]) { oldPlaying[s].disconnect(); oldPlaying[s].finished(); } } } SoundPrims.prototype.primPlaySound = function(b) { var s = interp.targetSprite(); if (s == null) return; var snd = s.soundNamed(interp.arg(b, 0)); if (snd != null) playSound(snd); } SoundPrims.prototype.primPlaySoundUntilDone = function(b) { var activeThread = interp.activeThread; if (activeThread.firstTime) { var snd = interp.targetSprite().soundNamed(interp.arg(b, 0)); if (snd == null) return; activeThread.tmpObj = playSound(snd); activeThread.firstTime = false; } var player = activeThread.tmpObj; if (player == null || player.done || player.playbackState == 3) { activeThread.tmpObj = null; activeThread.firstTime = true; } else { interp.yield = true; } } var beatsToSeconds = function(beats) { return (beats * 60) / runtime.stage.data.tempoBPM; } SoundPrims.prototype.primPlayNote = function(b) { var s = interp.targetSprite(); if (s == null) return; if (interp.activeThread.firstTime) { var key = interp.arg(b, 0); var secs = beatsToSeconds(interp.arg(b, 1)); playNote(s.instrument, key, secs, s); interp.startTimer(secs); } else { interp.checkTimer(); } } SoundPrims.prototype.primPlayDrum = function(b) { var s = interp.targetSprite(); if (s == null) return; if (interp.activeThread.firstTime) { var drum = Math.round(interp.arg(b, 0)); var secs = beatsToSeconds(interp.arg(b, 1)); playDrum(drum, secs, s); interp.startTimer(secs); } else { interp.checkTimer(); } } SoundPrims.prototype.primPlayRest = function(b) { var s = interp.targetSprite(); if (s == null) return; if (interp.activeThread.firstTime) { var secs = beatsToSeconds(interp.arg(b, 0)); interp.startTimer(secs); } else { interp.checkTimer(); } } SoundPrims.prototype.primSetInstrument = function(b) { var s = interp.targetSprite(); if (s != null) s.instrument = interp.arg(b, 0); } SoundPrims.prototype.primStopAllSounds = function(b) { stopAllSounds(); } SoundPrims.prototype.primChangeVolume = function(b) { var s = interp.targetSprite(); if (s != null) s.volume += interp.arg(b, 0); } SoundPrims.prototype.primSetVolume = function(b) { var s = interp.targetSprite(); if (s != null) s.volume = interp.arg(b, 0); } SoundPrims.prototype.primVolume = function(b) { var s = interp.targetSprite(); return (s != null) ? s.volume : 0; }