import Server , { msg , OscType , updateNodeState , whenNodeEnd , whenNodeGo } from "@supercollider/server" ; import { Command , Dryad } from "dryadic" ; import _ from "lodash" ; import SynthDef , { CompiledSynthDef , LoadedSynthDef } from "./SCSynthDef" ; const { AddActions , nodeFree , synthNew } = msg ; interface SynthParams { [ name ] : OscType | Dryad ; } interface Properties { args : SynthParams ; def : SynthDef | CompiledSynthDef | LoadedSynthDef | string ; } interface Context { id : string ; out ? : number ; nodeID ? : number ; group ? : number ; scserver : Server ; } /**
 * Creates a synth on the server.
 *
 * Properties:
 * - def
 * - args
 */ export default class Synth extends Dryad < Properties > { /**
     * Make a Synth that will play the SynthDef compiled from sclang source code.
     *
     * source may be fully defined:
     *   `SynthDef("defName", { |out=0, freq=440| Out.ar(SinOsc.ar(freq)) });`
     * or more simply:
     *   `{|freq| SinOsc.ar(freq)}`
     * or even just:
     *    `|freq| SinOsc.ar(freq)`
     */ static fromSource ( source , args = { } ) { return new Synth ( { def : SynthDef . fromSource ( source ) , args } ) ; } /**
     * Make a Synth that will play the SynthDef compiled from an *.scd source code file
     */ static fromFile ( path , args = { } ) { return new Synth ( { def : SynthDef . fromFile ( path ) , args } ) ; } /**
     * If there is no SCServer in the parent context,
     * then this will wrap itself in an SCServer
     */ requireParent ( ) { return "SCServer" ; } prepareForAdd ( ) { return { updateContext : context => ( { nodeID : context . scserver . state . nextNodeID ( ) , } ) , } ; } add ( ) { const defName = def => ( typeof def === "string" ? def : def . name ) ; return { scserver : { msg : ( context , properties ) => { const args = _ . mapValues ( properties . args , ( value , key ) => this . _checkOscType ( value , key , context . id ) ) ; if ( _ . isUndefined ( args . out ) && ! _ . isUndefined ( context . out ) ) { args . out = context . out ; } const dn = this . _checkOscType ( defName ( properties . def ) , "def.name" , context . id ) ; return synthNew ( dn , context . nodeID , AddActions . TAIL , context . group , args ) ; } , } , run : ( context , properties ) : void | Promise < number > => { return whenNodeGo ( context . scserver , context . id , context . nodeID || - 1 ) . then ( nodeID => { updateNodeState ( context . scserver , nodeID , { synthDef : defName ( properties . def ) , } ) ; return nodeID ; } ) ; } , } ; } remove ( ) { return { scserver : { msg : ( context ) => nodeFree ( context . nodeID || - 1 ) , } , run : ( context ) => whenNodeEnd ( context . scserver , context . id , context . nodeID || - 1 ) , } ; } private _checkOscType ( v , key , id ) { switch ( typeof v ) { case "number" : case "string" : return v ; default : throw new Error ( `Invalid OSC type for Synth ${ key }: [${ typeof v }: ${ v }] @ ${ id }` ) ; } } }