2013-10-28 20:00:20 +00:00
// 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' ;
2013-11-01 22:44:51 -04:00
var SoundPrims = function ( ) { } ;
2013-10-28 20:00:20 +00:00
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 ;
/ * p r i m T a b l e [ ' c h a n g e V o l u m e B y : ' ] = t h i s . p r i m C h a n g e V o l u m e ;
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 ; } ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
var playSound = function ( snd ) {
if ( snd . source ) {
// If this particular sound is already playing, stop it.
2014-07-25 10:39:33 +01:00
snd . source . disconnect ( ) ;
2013-10-28 20:00:20 +00:00
snd . source = null ;
}
2013-11-01 22:44:51 -04:00
2013-10-28 20:00:20 +00:00
snd . source = runtime . audioContext . createBufferSource ( ) ;
snd . source . buffer = snd . buffer ;
snd . source . connect ( runtime . audioGain ) ;
2013-11-01 22:44:51 -04:00
2013-10-28 20:00:20 +00:00
// 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 ) ;
2014-07-25 10:39:33 +01:00
snd . source . start ( ) ;
2013-10-28 20:00:20 +00:00
return snd . source ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
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 ) ;
}
}
2013-11-01 22:44:51 -04:00
window . setTimeout ( source . finished , secs * 1000 ) ;
2013-10-28 20:00:20 +00:00
return player ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
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 ) ;
}
}
2013-11-01 22:44:51 -04:00
window . setTimeout ( source . finished , secs * 1000 ) ;
2013-10-28 20:00:20 +00:00
return player ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
var stopAllSounds = function ( ) {
var oldPlaying = runtime . audioPlaying ;
runtime . audioPlaying = [ ] ;
for ( var s = 0 ; s < oldPlaying . length ; s ++ ) {
if ( oldPlaying [ s ] . source ) {
2014-07-25 10:39:33 +01:00
oldPlaying [ s ] . source . disconnect ( ) ;
2013-10-28 20:00:20 +00:00
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 ( ) ;
}
}
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
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 ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
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 ;
}
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
var beatsToSeconds = function ( beats ) {
2013-11-01 22:44:51 -04:00
return beats * 60 / runtime . stage . data . tempoBPM ;
} ;
2013-10-28 20:00:20 +00:00
SoundPrims . prototype . primPlayNote = function ( b ) {
var s = interp . targetSprite ( ) ;
if ( s == null ) return ;
if ( interp . activeThread . firstTime ) {
2013-11-02 00:38:29 -05:00
var key = interp . numarg ( b , 0 ) ;
var secs = beatsToSeconds ( interp . numarg ( b , 1 ) ) ;
2013-10-28 20:00:20 +00:00
playNote ( s . instrument , key , secs , s ) ;
interp . startTimer ( secs ) ;
} else {
interp . checkTimer ( ) ;
}
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
SoundPrims . prototype . primPlayDrum = function ( b ) {
var s = interp . targetSprite ( ) ;
if ( s == null ) return ;
if ( interp . activeThread . firstTime ) {
2013-11-02 00:38:29 -05:00
var drum = Math . round ( interp . numarg ( b , 0 ) ) ;
var secs = beatsToSeconds ( interp . numarg ( b , 1 ) ) ;
2013-10-28 20:00:20 +00:00
playDrum ( drum , secs , s ) ;
interp . startTimer ( secs ) ;
} else {
interp . checkTimer ( ) ;
}
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
SoundPrims . prototype . primPlayRest = function ( b ) {
var s = interp . targetSprite ( ) ;
if ( s == null ) return ;
if ( interp . activeThread . firstTime ) {
2013-11-02 00:38:29 -05:00
var secs = beatsToSeconds ( interp . numarg ( b , 0 ) ) ;
2013-10-28 20:00:20 +00:00
interp . startTimer ( secs ) ;
} else {
interp . checkTimer ( ) ;
}
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
SoundPrims . prototype . primSetInstrument = function ( b ) {
var s = interp . targetSprite ( ) ;
if ( s != null ) s . instrument = interp . arg ( b , 0 ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
SoundPrims . prototype . primStopAllSounds = function ( b ) {
stopAllSounds ( ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
SoundPrims . prototype . primChangeVolume = function ( b ) {
var s = interp . targetSprite ( ) ;
2013-11-02 00:38:29 -05:00
if ( s != null ) s . volume += interp . numarg ( b , 0 ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
SoundPrims . prototype . primSetVolume = function ( b ) {
var s = interp . targetSprite ( ) ;
2013-11-02 00:38:29 -05:00
if ( s != null ) s . volume = interp . numarg ( b , 0 ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
SoundPrims . prototype . primVolume = function ( b ) {
var s = interp . targetSprite ( ) ;
2013-11-01 22:44:51 -04:00
return s != null ? s . volume : 0 ;
} ;