// --------------------------------------------------------------------- // Example of fastforward management to drive corectly sfplay ; We record the start date of a sample in a map to implement a database ; of the runing samples. $runing_sample := MAP{} ; We keep track of four sfplay~ commands: ; open : to load a sample ; 1 : to start the playback ; 0 : to stop the playback ; seek n : to play from n to the end of the sample @fun_def update_runing_sample($receiver, $args) { // the open command of sfplay~ does not change the state of our database switch ($args[0]) { case 0: ; sample stoped, it must be removed from the database $runing_sample.remove($receiver) case 1: ; a sample is started, we record it $runing_sample.insert($receiver, $NOW) case "seek": ; a sample is started at some position P, this is equivalent ; of starting the sample at a previous date such date now it ; is at the right position $runing_sample.insert($receiver, $NOW - $args[1]/1000.) } } ; This function is used to trap a 'sample' message during fastforward mode ; and we replace the 'non-sending' of a sampleN message in fastforward mode ; by the update of our database. If the command is a load, we have to do the ; load. The other command do nothing: in fastforward mode, the playbacks remain ; silent. @fun_def sample_in_ffw($receiver, $args) { ; print FFW $receiver " and args " $args " at " $NOW @update_runing_sample($receivers, $args) if ($args[0] == "load") { @send_message($receivers, $args) } } ; we install the handler of sampleN message @message_def "sample[0-9]+" [FFW] := @sample_in_ffw ; The same remark apply for the sampleN message sent during normal execution ; but this time we must also send the message. We cannot write directly the ; message to sent, because the handling of the message is redefined. So we use ; the @send_message() function to force the regular sending of the message. @fun_def sample_in_ordinary_mode($receiver, $args) { print send message @send_message($receiver, $args) @update_runing_sample($receiver, $args) } @message_def sample [msg] := @sample_in_ordinary_mode ; To resume the playback of the sample when we exit the fastforward mode ; we simply have to issue 'sampleN seek P' for every sample which is supposed ; to run with the right P. All information needed to compute P is in the database. ; ; There is however a small subtleties: the playback must resume when the expected ; event occurs, not when the fastforward phase stops. So we use two internal event ; callback: ; - the stop of the fastforward phase install an handler that is triggered with ; the occurence of the next event ; - and the occurence of the next event ; When the fastforward phas is toped, ; we install a specific transient handler for the nex event @fun_def @stop_ffw($rdate, $date) { print "callback stop fastforward at " $rdate $date @callback_next_event(@exit_ffw, false) } @callback_stop_fastforward(@stop_ffw, false) ; This is the handler of the next event received after the fastforward phase. ; It simplies send the message according the databases. @fun_def @exit_ffw($rdate, $date) { print "callback exit fastforward at " $rdate $date print "recorded: " ("" + $runing_sample) forall $receiver, $debut in $runing_sample { print "launch: " $receiver seek (($NOW - $debut)*1000.) @send_message($receiver, seek, ($NOW - $debut)*1000.) } } ; When a stop command is received anytime, ; we use the database to stop all the runing sample @fun_def cbstop($rdate, $date) { forall $recv, $d in $runing_sample { print "stop " $recv @send_message($recv, 0) } } @callback_stop(@cbstop, false) ; This whenever is used to trace each event whenever($BEAT_POS == $BEAT_POS) { print $BEAT_POS "-------------------------" } // The rest of the program is the initial score. // // We put here an arbitrary example using EVENT // to avoid the description of an actual score BPM 30 sample1 open "/path/to/sample1.wav" sample2 open "/path/to/sample2.wav" Event 1 sample1 1 Event 1 Event 1 sample2 1 Event 1 print sample1 seek 50 sample1 seek 50000 Event 1 Event 1 Event 1 Event 1 Part2 print Part2 $NOW Event 1 Event 1 Event 1 Event 1 Event 1 sample1 0 Event 1 sample2 0 print done antescofo::stop