import Server , { msg , OscType , MsgType } from "@supercollider/server" ; import { EventStream } from "baconjs" ; import { Dryad , DryadPlayer , Command } from "dryadic" ; import _ from "lodash" ; import Group from "./Group" ; const { AddActions , nodeFree , synthNew } = msg ; const LATENCY = 0.03 ; interface Params { [ name ] : OscType ; } interface Properties { stream : EventStream < any , Event > ; defaultParams ? : Params ; } interface Context { id : string ; nodeID ? : number ; out ? : number ; group ? : number ; scserver : Server ; subscription ? : any ; nodeIDs ? : { [ key ] : number ; } ; } export interface Event { defName : string ; args ? : Params ; key ? : number ; type ? : string ; } interface SynthStreamEventCommand extends Command { scserver : { bundle : { time : number ; packets : MsgType [ ] ; } ; } ; updateContext : { nodeIDs : { [ key ] : number ; } ; } ; } /**
 * Given a Bacon.js stream that returns objects, this spawns a series of Synths.
 *
 * Properties:
 *  {Bacon.EventStream} stream
 *  {object} defaultParams
 *
 * The event values should be simple JavaScript objects:
 *
 * {
 *   defName: 'synthDefName',
 *   args: {
 *     out: 0,
 *     freq: 440
 *   }
 * }
 *
 * defaultParams is a fixed object into which the event value is merged.
 */ export default class SynthStream extends Dryad < Properties > { add ( player ) { return { run : ( context , properties ) => { const subscription = properties . stream . subscribe ( event => { this . handleEvent ( event . value ( ) , context , properties , player ) ; } ) ; player . updateContext ( context , { subscription } ) ; } , } ; } commandsForEvent ( event , context , properties ) { const msgs = [ ] ; let updateContext ; const nodeIDs = context . nodeIDs || { } ; const key = event . key ? String ( event . key ) : "undefined" ; switch ( event . type ) { case "noteOff" : { const nodeID = nodeIDs [ key ] ; if ( nodeID ) { msgs . push ( nodeFree ( nodeID || - 1 ) ) ; updateContext = { nodeIDs : _ . omit ( nodeIDs , [ key ] ) , } ; } else { throw new Error ( `NodeID was not registered for event key ${ key || "undefined" }` ) ; } break ; } default : { const defaultParams = properties . defaultParams || { } ; const args = _ . assign ( { out : context . out || 0 } , defaultParams . args , event . args ) ; const defName = event . defName || ( defaultParams . defName as string ) ; let nodeID = - 1 ; if ( key ) { nodeID = context . scserver . state . nextNodeID ( ) ; updateContext = { nodeIDs : _ . assign ( { } , nodeIDs , { [ key ] : nodeID , } ) , } ; } const synth = synthNew ( defName , nodeID , AddActions . TAIL , context . group , args ) ; msgs . push ( synth ) ; } } return { scserver : { bundle : { time : LATENCY , packets : msgs , } , } , updateContext , } ; } handleEvent ( event , context , properties , player ) { player . callCommand ( context . id , this . commandsForEvent ( event , context , properties ) ) ; } remove ( ) { return { run : ( context ) => { if ( context . subscription ) { if ( _ . isFunction ( context . subscription ) ) { context . subscription ( ) ; } else { context . subscription . dispose ( ) ; } } } , } ; } subgraph ( ) { return new Group ( { } , [ this ] ) ; } }