├── .babelrc ├── .eslintrc.js ├── .gitignore ├── .prettierrc.js ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs ├── .nojekyll ├── README.md ├── _coverpage.md ├── _sidebar.md ├── api.md ├── index.html ├── index.md ├── install.md ├── packages │ ├── dryads │ │ ├── AudioBus.md │ │ ├── Group.md │ │ ├── README.md │ │ ├── SCLang.md │ │ ├── SCServer.md │ │ ├── SCSynthDef.md │ │ ├── Synth.md │ │ ├── SynthControl.md │ │ ├── SynthEventList.md │ │ ├── SynthStream.md │ │ ├── api.md │ │ ├── dryadic.md │ │ ├── h.md │ │ ├── layer.md │ │ └── play.md │ ├── lang │ │ ├── README.md │ │ ├── SCLang.md │ │ ├── SCLangError.md │ │ ├── SynthDefCompiler.md │ │ ├── api.md │ │ ├── boot.md │ │ └── resolveOptions.md │ ├── logger │ │ ├── Logger.md │ │ ├── README.md │ │ └── api.md │ ├── osc │ │ ├── README.md │ │ ├── api.md │ │ ├── asNTPTimeTag.md │ │ ├── dateToTimetag.md │ │ ├── deltaTimeTag.md │ │ ├── pack.md │ │ ├── packBundle.md │ │ ├── packMessage.md │ │ ├── timetagToDate.md │ │ ├── unpack.md │ │ ├── unpackBundle.md │ │ └── unpackMessage.md │ ├── scapi │ │ ├── README.md │ │ ├── SCAPI.md │ │ └── api.md │ ├── server-plus │ │ ├── AudioBus.md │ │ ├── Buffer.md │ │ ├── ControlBus.md │ │ ├── Group.md │ │ ├── README.md │ │ ├── ServerPlus.md │ │ ├── Synth.md │ │ ├── SynthDef.md │ │ ├── api.md │ │ └── boot.md │ ├── server │ │ ├── README.md │ │ ├── Server.md │ │ ├── ServerState.md │ │ ├── api.md │ │ ├── boot.md │ │ ├── deltaTimeTag.md │ │ ├── mapping.md │ │ ├── msg.md │ │ ├── onNodeEnd.md │ │ ├── onNodeGo.md │ │ ├── resolveOptions.md │ │ ├── updateNodeState.md │ │ ├── watchNodeNotifications.md │ │ ├── whenNodeEnd.md │ │ └── whenNodeGo.md │ └── supercolliderjs │ │ ├── README.md │ │ ├── SCLangError.md │ │ ├── api.md │ │ ├── dryads.md │ │ ├── exports.md │ │ ├── lang.md │ │ ├── map.md │ │ ├── msg.md │ │ ├── resolveOptions.md │ │ └── server.md ├── public │ ├── apple-touch-icon-144-precomposed.png │ ├── apple-touch-icon-256-precomposed.png │ └── favicon.ico └── src │ ├── _coverpage.md │ ├── packages │ ├── dryads │ │ ├── README.md │ │ └── api.md │ ├── lang │ │ ├── README.md │ │ └── api.md │ ├── logger │ │ ├── README.md │ │ └── api.md │ ├── osc │ │ ├── README.md │ │ └── api.md │ ├── scapi │ │ ├── README.md │ │ └── api.md │ ├── server-plus │ │ ├── README.md │ │ └── api.md │ ├── server │ │ ├── README.md │ │ └── api.md │ └── supercolliderjs │ │ ├── README.md │ │ └── api.md │ └── partials │ ├── footer.md │ └── header.md ├── examples ├── boot-lang.js ├── bubbles.js ├── dryads-synth-event-list.js ├── formant.scd ├── klang.scd ├── lang-interpret-the-long-way.js ├── lang-interpret.js ├── logger.js ├── package-lock.json ├── package.json ├── server-plus-promises.js ├── server-plus.js ├── server.js ├── serverp ├── sine-wave.js └── synthdef-and-synth.js ├── lerna.json ├── package-lock.json ├── package.json ├── packages ├── dryads │ ├── .npmignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── AudioBus.ts │ │ ├── Group.ts │ │ ├── SCLang.ts │ │ ├── SCServer.ts │ │ ├── SCSynthDef.ts │ │ ├── Synth.ts │ │ ├── SynthControl.ts │ │ ├── SynthEventList.ts │ │ ├── SynthStream.ts │ │ ├── __tests__ │ │ │ ├── Synth.spec.ts │ │ │ ├── SynthEventList.spec.ts │ │ │ ├── SynthStream.spec.ts │ │ │ └── index-spec.ts │ │ ├── index.ts │ │ ├── middleware │ │ │ ├── OSCSched.ts │ │ │ ├── __tests__ │ │ │ │ ├── OSCSched.spec.ts │ │ │ │ └── scserver.spec.ts │ │ │ └── scserver.ts │ │ └── utils │ │ │ ├── __tests__ │ │ │ └── iterators.ts │ │ │ ├── iterators.ts │ │ │ └── test-utils.ts │ └── tsconfig.json ├── jest.config.json ├── lang │ ├── .npmignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── Errors.ts │ │ ├── SynthDefCompiler.ts │ │ ├── __tests__ │ │ │ ├── fixtures │ │ │ │ └── sclang_test_conf.yaml │ │ │ └── sclang-test.ts │ │ ├── bin │ │ │ ├── compile-synthdefs.ts │ │ │ └── sclang.ts │ │ ├── checkInstall.ts │ │ ├── index.ts │ │ ├── internals │ │ │ ├── __tests__ │ │ │ │ ├── fixtures │ │ │ │ │ ├── errors-but-did-compile.txt │ │ │ │ │ ├── forward-stdout.txt │ │ │ │ │ ├── io-class-syntax-error-2.txt │ │ │ │ │ ├── io-class-syntax-error.txt │ │ │ │ │ ├── io-compile-success.txt │ │ │ │ │ ├── io-duplicate-class.txt │ │ │ │ │ ├── io-extension-for-non-existent-class.txt │ │ │ │ │ ├── io-na.txt │ │ │ │ │ ├── io-programmatic-compile.txt │ │ │ │ │ ├── routine-postln.txt │ │ │ │ │ ├── script.scd │ │ │ │ │ └── trig-not-defined.txt │ │ │ │ └── sclang-io-test.ts │ │ │ └── sclang-io.ts │ │ ├── options.ts │ │ ├── sclang.ts │ │ └── supercollider-js │ │ │ ├── SuperColliderJS.sc │ │ │ ├── SystemOverwrites │ │ │ ├── plusError.sc │ │ │ ├── plusQuarks.sc │ │ │ └── plusSynthDesc.sc │ │ │ └── supercollider-js.quark │ └── tsconfig.json ├── logger │ ├── .npmignore │ ├── LICENSE │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ └── logger-test.ts │ │ └── index.ts │ └── tsconfig.json ├── osc │ ├── .npmignore │ ├── README.md │ ├── __tests__ │ │ ├── osc-test.ts │ │ └── timetags-test.ts │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── buffers.ts │ │ ├── index.ts │ │ ├── packing.ts │ │ ├── timetags.ts │ │ └── types.ts │ └── tsconfig.json ├── scapi │ ├── .npmignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── server-plus │ ├── .npmignore │ ├── README.md │ ├── package.json │ ├── src │ │ └── ServerPlus.ts │ └── tsconfig.json ├── server │ ├── .npmignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── ServerState.ts │ │ ├── __tests__ │ │ │ ├── ServerState-test.ts │ │ │ ├── node-watcher-test.ts │ │ │ ├── resolve-options-test.ts │ │ │ └── server-test.ts │ │ ├── bin │ │ │ └── scsynth.ts │ │ ├── checkInstall.ts │ │ ├── index.ts │ │ ├── internals │ │ │ ├── SendOSC.ts │ │ │ ├── Store.ts │ │ │ ├── __tests__ │ │ │ │ └── allocators.ts │ │ │ ├── allocators.ts │ │ │ └── side-effects.ts │ │ ├── mapping.ts │ │ ├── node-watcher.ts │ │ ├── options.ts │ │ ├── osc-types.ts │ │ ├── osc │ │ │ ├── __tests__ │ │ │ │ ├── msg.ts │ │ │ │ └── utils.ts │ │ │ ├── msg.ts │ │ │ └── utils.ts │ │ └── server.ts │ └── tsconfig.json ├── supercolliderjs │ ├── .npmignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── bin │ │ │ └── export-supercollider.ts │ │ └── index.ts │ └── tsconfig.json └── tsconfig.json └── tasks ├── export-index.js ├── make-docs.js ├── render-api.js └── typedocs.sh /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/env"], 3 | "plugins": [] 4 | } 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: "@typescript-eslint/parser", 3 | plugins: ["@typescript-eslint"], 4 | 5 | extends: ["plugin:@typescript-eslint/recommended"], 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | // enables 'import' 9 | sourceType: "module", 10 | }, 11 | rules: { 12 | indent: "off", 13 | "@typescript-eslint/indent": ["error", 2], 14 | "@typescript-eslint/explicit-member-accessibility": ["error", { accessibility: "no-public" }], 15 | "@typescript-eslint/camelcase": ["warn"], 16 | "@typescript-eslint/no-empty-interface": ["warn"], 17 | "@typescript-eslint/no-use-before-define": [0], 18 | "@typescript-eslint/no-empty-function": ["warn"], 19 | "@typescript-eslint/no-var-requires": ["warn"], 20 | "@typescript-eslint/camelcase": [0], 21 | "@typescript-eslint/explicit-function-return-type": ["warn", { allowExpressions: true }], 22 | "@typescript-eslint/no-unused-vars": [ 23 | "error", 24 | { 25 | args: "none", 26 | }, 27 | ], 28 | "@typescript-eslint/no-var-requires": [0], 29 | 30 | // indent: ["error", 2] 31 | // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs 32 | // e.g. "@typescript-eslint/explicit-function-return-type": "off", 33 | }, 34 | overrides: [ 35 | { 36 | files: ["*.js"], 37 | rules: { 38 | "@typescript-eslint/explicit-function-return-type": "off", 39 | }, 40 | }, 41 | ], 42 | }; 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | coverage 3 | .node-version 4 | .tags1 5 | node_modules 6 | lerna-debug.log 7 | tsconfig.tsbuildinfo 8 | .vscode 9 | *.off 10 | docs/src/packages/**/api.json 11 | packages/**/index.json 12 | packages/**/typedoc.json 13 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: "all", 4 | singleQuote: false, 5 | printWidth: 120, 6 | tabWidth: 2, 7 | }; 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "10" 5 | - "11" 6 | - "12" 7 | 8 | install: 9 | - npm install 10 | 11 | cache: 12 | directories: 13 | - "$HOME/.npm" 14 | 15 | script: 16 | - npm run lint 17 | - npm run build 18 | - npm run jest 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-Present Chris Sattinger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crucialfelix/supercolliderjs/2e7c2b4819cd19346656755d1cf37bf6cea37d04/docs/.nojekyll -------------------------------------------------------------------------------- /docs/_coverpage.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | # supercollider.js 1.0.1 7 | 8 | > The JavaScript client library for SuperCollider. 9 | 10 | [GitHub](https://github.com/crucialfelix/supercolliderjs/) 11 | [Docs](#supercolliderjs) 12 | 13 | 14 | 15 | ![color](#202020) 16 | 17 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | link to TypeDoc 2 | -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | # Install 2 | 3 | ```shell 4 | npm install --save supercolliderjs 5 | ``` 6 | 7 | --- 8 | 9 | For help with Node.js and configuring supercollider.js to find your SuperCollider excutables, see the install instructions in the Guide: 10 | 11 | [https://crucialfelix.gitbooks.io/supercollider-js-guide/](https://crucialfelix.gitbooks.io/supercollider-js-guide/content/) 12 | -------------------------------------------------------------------------------- /docs/packages/dryads/AudioBus.md: -------------------------------------------------------------------------------- 1 | # AudioBus 2 | Package: @supercollider/dryads 3 | 4 |

class AudioBus

extends Dryad<Properties>

Allocates an audio bus, making it available in the children's context as .out (integer) 5 | and .numChannels (integer)

Constructor
Property

children [object Object][]

properties Properties

Accessor

isDryad boolean

This method is never actually called, but merely because its implemented 6 | (dryad.isDryad is not undefined) it marks the things as being a Dryad.

Method

defaultProperties(): Properties

prepareForAdd(): Command

remove(): Command

requireParent(): string

If there is no SCServer in the parent context, 7 | then this will wrap itself in an SCServer

8 | -------------------------------------------------------------------------------- /docs/packages/dryads/Group.md: -------------------------------------------------------------------------------- 1 | # Group 2 | Package: @supercollider/dryads 3 | 4 |

class Group

extends Dryad

Creates a group on the server; sets .group in context for its children, 5 | so any Synths or Groups will be spawned inside this group.

Constructor
Property

children [object Object][]

properties P

Accessor

isDryad boolean

This method is never actually called, but merely because its implemented 6 | (dryad.isDryad is not undefined) it marks the things as being a Dryad.

Method

add(): Command

prepareForAdd(): Command

remove(): Command

requireParent(): string

If there is no SCServer in the parent context, 7 | then this will wrap itself in an SCServer

8 | -------------------------------------------------------------------------------- /docs/packages/dryads/SCLang.md: -------------------------------------------------------------------------------- 1 | # SCLang 2 | Package: @supercollider/dryads 3 | 4 |

class SCLang

extends Dryad<Properties>

Boots a new SuperCollider language interpreter (sclang) making it available for all children as context.sclang

Always boots a new one, ignoring any possibly already existing one in the parent context. 5 | 6 | `options` are the command line options supplied to sclang (note: not all options are passed through yet) 7 | see {@link lang/SCLang} 8 | 9 | Not to be confused with the other class named SCLang which does all the hard work. 10 | This Dryad class is just a simple wrapper around that. 11 |

Constructor
Property

children [object Object][]

properties Properties

Accessor

isDryad boolean

This method is never actually called, but merely because its implemented 12 | (dryad.isDryad is not undefined) it marks the things as being a Dryad.

Method

defaultProperties(): Properties

prepareForAdd(): Command

remove(): Command

13 | -------------------------------------------------------------------------------- /docs/packages/dryads/SCServer.md: -------------------------------------------------------------------------------- 1 | # SCServer 2 | Package: @supercollider/dryads 3 | 4 |

class SCServer

extends Dryad<ServerProperties>

Boots a new SuperCollider server (scsynth) making it available for all children as `context.scserver`

Always boots a new one, ignoring any possibly already existing one in the parent context. 5 | 6 | `options` are the command line options supplied to scsynth (note: not all options are passed through yet) 7 | see {@link Server} 8 |

Constructor
Property

children [object Object][]

properties ServerProperties

Accessor

isDryad boolean

This method is never actually called, but merely because its implemented 9 | (dryad.isDryad is not undefined) it marks the things as being a Dryad.

Method

defaultProperties(): ServerProperties

initialContext(): Context

prepareForAdd(): Command

remove(): Command

10 | -------------------------------------------------------------------------------- /docs/packages/dryads/SynthControl.md: -------------------------------------------------------------------------------- 1 | # SynthControl 2 | Package: @supercollider/dryads 3 | 4 |

class SynthControl

extends Dryad<Properties>

Sends nodeSet messages to the Synth in the parent context.

This takes a Bacon.js stream which should return objects 5 | {param: value, ...} and sends `nodeSet` messages to the parent Synth. 6 | 7 | SynthControl should be a child of a Synth, Group or other Dryad that 8 | sets context.nodeID 9 |

Constructor
Property

children [object Object][]

properties Properties

Accessor

isDryad boolean

This method is never actually called, but merely because its implemented 10 | (dryad.isDryad is not undefined) it marks the things as being a Dryad.

Method

add(player: DryadPlayer): Command

remove(): Command

requireParent(): string

If there is no SCServer in the parent context, 11 | then this will wrap itself in an SCServer

12 | -------------------------------------------------------------------------------- /docs/packages/dryads/api.md: -------------------------------------------------------------------------------- 1 | # @supercollider/dryads 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/packages/dryads/dryadic.md: -------------------------------------------------------------------------------- 1 | # dryadic 2 | Package: @supercollider/dryads 3 | 4 |

dryadic(rootDryad: Dryad, moreLayers: [object Object][] = [], rootContext: Context = {}): DryadPlayer

Create a DryadPlayer from a Dryad or hyperscript definition.

Automatically includes the supercollider.js layer 5 | 6 | usage: 7 | ```js 8 | var sc = require('supercolliderjs'); 9 | var player = sc.dryadic([ 10 | 'scserver', [ 11 | ['group', [ 12 | ['synth', { 13 | defName: 'sinosc', 14 | args: { 15 | freq: 440 16 | } 17 | }] 18 | ] 19 | ]); 20 | player.play(); 21 | // ... 22 | player.stop(); 23 | ``` 24 |

25 | -------------------------------------------------------------------------------- /docs/packages/dryads/h.md: -------------------------------------------------------------------------------- 1 | # h 2 | Package: @supercollider/dryads 3 | 4 |

h(hgraph: any): Dryad

Convert hyperscript object to a tree of Dryads.

This lookups each class by lower class 'classname' 5 | and creates an instance with properties and children. 6 |

7 | -------------------------------------------------------------------------------- /docs/packages/dryads/layer.md: -------------------------------------------------------------------------------- 1 | # layer 2 | Package: @supercollider/dryads 3 | 4 |

layer = {

}
5 | -------------------------------------------------------------------------------- /docs/packages/dryads/play.md: -------------------------------------------------------------------------------- 1 | # play 2 | Package: @supercollider/dryads 3 | 4 |

play(rootDryad: Dryad): Promise<DryadPlayer>

Play a Dryad or hyperscript document.

usage: 5 | 6 | ```js 7 | var sc = require('supercolliderjs'); 8 | var player = sc.play([ 9 | 'scserver', [ 10 | ['group', [ 11 | ['synth', { 12 | defName: 'sinosc', 13 | args: { 14 | freq: 440 15 | } 16 | }] 17 | ] 18 | ]); 19 | ```

20 | -------------------------------------------------------------------------------- /docs/packages/lang/SCLangError.md: -------------------------------------------------------------------------------- 1 | # SCLangError 2 | Package: @supercollider/lang 3 | 4 |

class SCLangError

extends SCError

SCLangError - syntax errors while interpreting code, interpret code execution errors, and asynchronous errors.

Constructor

new SCLangError(message: string, type: string, error: object, data: any = {}): SCLangError

Property

data object

error object

message string

stack string

type string

Method
5 | -------------------------------------------------------------------------------- /docs/packages/lang/api.md: -------------------------------------------------------------------------------- 1 | # @supercollider/lang 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/packages/lang/boot.md: -------------------------------------------------------------------------------- 1 | # boot 2 | Package: @supercollider/lang 3 | 4 |

boot(options: SCLangArgs): Promise<SCLang>

Boots an sclang interpreter, resolving options and connecting.

5 | -------------------------------------------------------------------------------- /docs/packages/lang/resolveOptions.md: -------------------------------------------------------------------------------- 1 | # resolveOptions 2 | Package: @supercollider/lang 3 | 4 |

resolveOptions(options: SCLangArgs = {}): SCLangOptions

5 | -------------------------------------------------------------------------------- /docs/packages/logger/Logger.md: -------------------------------------------------------------------------------- 1 | # Logger 2 | Package: @supercollider/logger 3 | 4 |

class Logger

A customized logging interface for supercollider.js

Has special colors for osc messages and for logging stdin/stdout traffic. 5 |

Constructor

new Logger(debug: boolean = false, echo: boolean = false, log: Console = console): Logger

Property
Method

dbug(text: any): void

Log debugging information but only if this.debug is true

err(text: any): void

Log an error.

rcvosc(text: any): void

Log OSC messages received from scsynth.

sendosc(text: any): void

Log OSC messages sent to scsynth.

stderr(text: any): void

Log messages that were emitted from stderr of sclang/scsynth.

stdin(text: any): void

Log messages that were sent to stdin or sclang.

stdout(text: any): void

Log messages that were received from stdout of sclang/scsynth.

6 | -------------------------------------------------------------------------------- /docs/packages/logger/README.md: -------------------------------------------------------------------------------- 1 | # @supercollider/logger 2 | [![NPM downloads][npm-downloads-image]][npm-url] [![MIT License][license-image]][license-url] 3 | 4 | Console logging utility for supercollider.js for debugging with color support and special formatting for OSC messages. 5 | 6 | This is used internally by other `@supercollider` packages. 7 | 8 | ## Usage 9 | 10 | ```js 11 | const Logger = require("@supercollider/logger").default; 12 | 13 | const debug = true; 14 | const echo = true; 15 | 16 | const log = new Logger(debug, echo); 17 | // Log an error. 18 | log.err("Oh no!"); 19 | // Log debugging information but only if this.debug is true 20 | log.dbug({ log: "log", some: 1, context: 2, for: "The problem" }); 21 | // Log messages that were sent to stdin or sclang. 22 | log.stdin("1 + 1"); 23 | // Log messages that were received from stdout of sclang/scsynth. 24 | log.stdout("2"); 25 | // Log messages that were emitted from stderr of sclang/scsynth. 26 | log.stderr("ERROR: ..."); 27 | // Log OSC messages sent to scsynth. 28 | log.sendosc({ address: "/ping" }); 29 | // Log OSC messages received from scsynth. 30 | log.rcvosc({ value: "pong" }); 31 | 32 | ``` 33 | source 34 | 35 | 36 | Documentation 37 | ------------- 38 | 39 | [Documentation](https://crucialfelix.github.io/supercolliderjs/#/packages/logger/api) 40 | 41 | Compatibility 42 | ------------- 43 | 44 | Works on Node 10+ 45 | 46 | Source code is written in TypeScript and is usable in JavaScript [es2018](https://2ality.com/2017/02/ecmascript-2018.html) or [TypeScript](https://www.typescriptlang.org/docs/home.html) projects. 47 | 48 | Contribute 49 | ---------- 50 | 51 | - Issue Tracker: https://github.com/crucialfelix/supercolliderjs/issues 52 | - Source Code: https://github.com/crucialfelix/supercolliderjs 53 | 54 | License 55 | ------- 56 | 57 | MIT license 58 | 59 | [license-image]: http://img.shields.io/badge/license-MIT-blue.svg?style=flat 60 | [license-url]: LICENSE 61 | 62 | [npm-url]: https://npmjs.org/package/@supercollider/logger 63 | [npm-version-image]: http://img.shields.io/npm/v/@supercollider/logger.svg?style=flat 64 | [npm-downloads-image]: http://img.shields.io/npm/dm/@supercollider/logger.svg?style=flat 65 | 66 | [travis-url]: http://travis-ci.org/crucialfelix/supercolliderjs 67 | [travis-image]: https://travis-ci.org/crucialfelix/supercolliderjs.svg?branch=master 68 | -------------------------------------------------------------------------------- /docs/packages/logger/api.md: -------------------------------------------------------------------------------- 1 | # @supercollider/logger 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/packages/osc/README.md: -------------------------------------------------------------------------------- 1 | # @supercollider/osc 2 | [![NPM downloads][npm-downloads-image]][npm-url] [![MIT License][license-image]][license-url] 3 | 4 | Open Sound Control packet, bundle and buffer utilities for supercollider 5 | 6 | - Packs OSC messages and bundles into a Node `Buffer` for sending 7 | - Unpacks received OSC messages and bundles from a Node `Buffer` 8 | 9 | It does not concern itself with network connections. 10 | 11 | The OSC support is limited to the types and features of SuperCollider server. 12 | This means it does not support inline arrays `[f]` 13 | 14 | This is used internally by `@supercollider/server` 15 | 16 | ## Usage 17 | 18 | ```js 19 | const osc = require('@supercollider/osc'); 20 | 21 | // TODO: DEMONSTRATE API 22 | ``` 23 | 24 | Documentation 25 | ------------- 26 | 27 | [Documentation](https://crucialfelix.github.io/supercolliderjs/#/packages/osc/api) 28 | 29 | Compatibility 30 | ------------- 31 | 32 | Works on Node 10+ 33 | 34 | Source code is written in TypeScript and is usable in JavaScript [es2018](https://2ality.com/2017/02/ecmascript-2018.html) or [TypeScript](https://www.typescriptlang.org/docs/home.html) projects. 35 | 36 | Contribute 37 | ---------- 38 | 39 | - Issue Tracker: https://github.com/crucialfelix/supercolliderjs/issues 40 | - Source Code: https://github.com/crucialfelix/supercolliderjs 41 | 42 | License 43 | ------- 44 | 45 | MIT license 46 | 47 | [license-image]: http://img.shields.io/badge/license-MIT-blue.svg?style=flat 48 | [license-url]: LICENSE 49 | 50 | [npm-url]: https://npmjs.org/package/@supercollider/osc 51 | [npm-version-image]: http://img.shields.io/npm/v/@supercollider/osc.svg?style=flat 52 | [npm-downloads-image]: http://img.shields.io/npm/dm/@supercollider/osc.svg?style=flat 53 | 54 | [travis-url]: http://travis-ci.org/crucialfelix/supercolliderjs 55 | [travis-image]: https://travis-ci.org/crucialfelix/supercolliderjs.svg?branch=master 56 | -------------------------------------------------------------------------------- /docs/packages/osc/api.md: -------------------------------------------------------------------------------- 1 | # @supercollider/osc 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/packages/osc/asNTPTimeTag.md: -------------------------------------------------------------------------------- 1 | # asNTPTimeTag 2 | Package: @supercollider/osc 3 | 4 |

asNTPTimeTag(time: OSCTimeType): NTPTimeTag

5 | -------------------------------------------------------------------------------- /docs/packages/osc/dateToTimetag.md: -------------------------------------------------------------------------------- 1 | # dateToTimetag 2 | Package: @supercollider/osc 3 | 4 |

dateToTimetag(date: Date): NTPTimeTag

Convert a JavaScript Date to a NTP timetag array `[secondsSince1970, fractionalSeconds]`.

`toBuffer` already accepts Dates for timetags so you might not need this function. 5 | If you need to schedule bundles with sub-millisecond accuracy then you 6 | could use this to help assemble the NTP array. 7 |

8 | -------------------------------------------------------------------------------- /docs/packages/osc/deltaTimeTag.md: -------------------------------------------------------------------------------- 1 | # deltaTimeTag 2 | Package: @supercollider/osc 3 | 4 |

deltaTimeTag(seconds: number, now: number | Date): NTPTimeTag

Make NTP timetag array relative to the current time.

Returns `[ntpSecs, ntpFracs]`
5 | -------------------------------------------------------------------------------- /docs/packages/osc/pack.md: -------------------------------------------------------------------------------- 1 | # pack 2 | Package: @supercollider/osc 3 | 4 |

pack(msgOrBundle: BundleOrMessage): Buffer

Encodes an OSCMessage or OSCBundle to a Buffer

5 | -------------------------------------------------------------------------------- /docs/packages/osc/packBundle.md: -------------------------------------------------------------------------------- 1 | # packBundle 2 | Package: @supercollider/osc 3 | 4 |

packBundle(bundle: OSCBundle): Buffer

5 | -------------------------------------------------------------------------------- /docs/packages/osc/packMessage.md: -------------------------------------------------------------------------------- 1 | # packMessage 2 | Package: @supercollider/osc 3 | 4 |

packMessage(message: OSCMessage): Buffer

5 | -------------------------------------------------------------------------------- /docs/packages/osc/timetagToDate.md: -------------------------------------------------------------------------------- 1 | # timetagToDate 2 | Package: @supercollider/osc 3 | 4 |

timetagToDate(timetag: NTPTimeTag): Date

5 | -------------------------------------------------------------------------------- /docs/packages/osc/unpack.md: -------------------------------------------------------------------------------- 1 | # unpack 2 | Package: @supercollider/osc 3 | 4 |

unpack(buffer: Buffer): BundleOrMessage

Unpacks either an OSCMessage or OSCBundle from an OSC Packet 5 | It's a bundle if it starts with `#bundle`

6 | -------------------------------------------------------------------------------- /docs/packages/osc/unpackBundle.md: -------------------------------------------------------------------------------- 1 | # unpackBundle 2 | Package: @supercollider/osc 3 | 4 |

unpackBundle(buffer: Buffer): OSCBundle

5 | -------------------------------------------------------------------------------- /docs/packages/osc/unpackMessage.md: -------------------------------------------------------------------------------- 1 | # unpackMessage 2 | Package: @supercollider/osc 3 | 4 |

unpackMessage(buffer: Buffer): OSCMessage

5 | -------------------------------------------------------------------------------- /docs/packages/scapi/README.md: -------------------------------------------------------------------------------- 1 | # @supercollider/scapi 2 | [![NPM downloads][npm-downloads-image]][npm-url] [![MIT License][license-image]][license-url] 3 | 4 | Node to SuperCollider communication using OSC and the API quark. 5 | 6 | This works together with the 'API' quark to implement a simple two-way communication protocol for node <-> SuperCollider. 7 | 8 | It connects with an sclang process using UDP OSC and then sends OSC messages to '/API/call' 9 | 10 | The SuperCollider quark is here: 11 | https://github.com/supercollider-quarks/API 12 | 13 | And this package is the nodejs side. 14 | 15 | Sent messages return a promise, the responses are received here from sclang and the promises are resolved (or rejected if there was an error). 16 | 17 | This requires writing named handlers in SuperCollider and registering them with the API. From the node side, you make a call using that name and pass it some args and get back your response. 18 | 19 | This was an older solution. Probably just using `@supercollider/lang` is easier now. 20 | 21 | Note: this is not included in the [`supercolliderjs`](https://npmjs.org/package/supercolliderjs) package. 22 | 23 | ## Install 24 | 25 | ```shell 26 | npm install @supercollider/scapi 27 | ``` 28 | 29 | Start SuperCollider 30 | Install the API quark ( > 2.0 ) 31 | 32 | ## Usage 33 | 34 | Start SuperCollider and activate the OSC responders: 35 | 36 | ```supercollider 37 | API.mountDuplexOSC 38 | ``` 39 | 40 | Documentation 41 | ------------- 42 | 43 | [Documentation](https://crucialfelix.github.io/supercolliderjs/#/packages/scapi/api) 44 | 45 | Compatibility 46 | ------------- 47 | 48 | Works on Node 10+ 49 | 50 | Source code is written in TypeScript and is usable in JavaScript [es2018](https://2ality.com/2017/02/ecmascript-2018.html) or [TypeScript](https://www.typescriptlang.org/docs/home.html) projects. 51 | 52 | Contribute 53 | ---------- 54 | 55 | - Issue Tracker: https://github.com/crucialfelix/supercolliderjs/issues 56 | - Source Code: https://github.com/crucialfelix/supercolliderjs 57 | 58 | License 59 | ------- 60 | 61 | MIT license 62 | 63 | [license-image]: http://img.shields.io/badge/license-MIT-blue.svg?style=flat 64 | [license-url]: LICENSE 65 | 66 | [npm-url]: https://npmjs.org/package/@supercollider/scapi 67 | [npm-version-image]: http://img.shields.io/npm/v/@supercollider/scapi.svg?style=flat 68 | [npm-downloads-image]: http://img.shields.io/npm/dm/@supercollider/scapi.svg?style=flat 69 | 70 | [travis-url]: http://travis-ci.org/crucialfelix/supercolliderjs 71 | [travis-image]: https://travis-ci.org/crucialfelix/supercolliderjs.svg?branch=master 72 | -------------------------------------------------------------------------------- /docs/packages/scapi/SCAPI.md: -------------------------------------------------------------------------------- 1 | # SCAPI 2 | Package: @supercollider/scapi 3 | 4 |

class SCAPI

extends EventEmitter
Constructor

new SCAPI(schost: string = "localhost", scport: number = 57120): SCAPI

Property

schost string

scport number

defaultMaxListeners number

Method

call(requestId: string | undefined, oscpath: string, args: any, ok: undefined | any, err: any | null | undefined): Promise<any>

connect(): void

disconnect(): void

5 | -------------------------------------------------------------------------------- /docs/packages/scapi/api.md: -------------------------------------------------------------------------------- 1 | # @supercollider/scapi 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/packages/server-plus/AudioBus.md: -------------------------------------------------------------------------------- 1 | # AudioBus 2 | Package: @supercollider/server-plus 3 | 4 |

class AudioBus

scsynth audio bus

See `server.audioBus(...)` 5 | 6 | These bus numbers (ids) and numChannels are allocated here in the client. 7 | The server only gets bus ids for reading and writing to. 8 |

Constructor

new AudioBus(server: ServerPlus, id: number, numChannels: number): AudioBus

Property

id number

numChannels number

server ServerPlus

Method

free(): void

Deallocate the AudioBus, freeing it for resuse.

9 | -------------------------------------------------------------------------------- /docs/packages/server-plus/Buffer.md: -------------------------------------------------------------------------------- 1 | # Buffer 2 | Package: @supercollider/server-plus 3 | 4 |

class Buffer

scsynth Buffer

See `server.buffer(...)` and `server.readBuffer(...)` 5 |

Constructor

new Buffer(server: ServerPlus, id: number, numFrames: number, numChannels: number): Buffer

Property

id number

numChannels number

numFrames number

server ServerPlus

Method

free(): Promise<void>

Deallocate the Buffer, freeing memory on the server.

6 | -------------------------------------------------------------------------------- /docs/packages/server-plus/ControlBus.md: -------------------------------------------------------------------------------- 1 | # ControlBus 2 | Package: @supercollider/server-plus 3 | 4 |

class ControlBus

extends AudioBus

scsynth control bus

See `server.controlBus(...)` 5 | 6 | These bus numbers (ids) and numChannels are allocated here in the client. 7 | The server only gets bus ids for reading and writing to. 8 |

Constructor
Property

id number

numChannels number

server ServerPlus

Method

free(): void

Deallocate the ControlBus, freeing it for resuse.

9 | -------------------------------------------------------------------------------- /docs/packages/server-plus/Group.md: -------------------------------------------------------------------------------- 1 | # Group 2 | Package: @supercollider/server-plus 3 | 4 |

class Group

scsynth Group

See `server.group(...)` 5 |

Constructor

new Group(server: ServerPlus, id: number): Group

Property

id number

server ServerPlus

Method

free(): Promise<number>

Stop the Group and remove it from the play graph on the server.

set(settings: Params): void

Update control parameters on the Synth.

6 | -------------------------------------------------------------------------------- /docs/packages/server-plus/Synth.md: -------------------------------------------------------------------------------- 1 | # Synth 2 | Package: @supercollider/server-plus 3 | 4 |

class Synth

extends Group

Created with `server.synth(...)`

Extends Group 5 |

Constructor
Property

id number

server ServerPlus

Method
6 | -------------------------------------------------------------------------------- /docs/packages/server-plus/SynthDef.md: -------------------------------------------------------------------------------- 1 | # SynthDef 2 | Package: @supercollider/server-plus 3 | 4 |

class SynthDef

scsynth SynthDef

See `server.synthDefs(...)` 5 | 6 | These are currently compiled using sclang, 7 | and the synthDefResult holds metadata about the compiled 8 | synthdef and the raw compiled bytes. 9 | 10 | The SynthDef may have been compiled from a sourceCode string 11 | or compiled from a file at path. 12 |

Constructor

new SynthDef(server: ServerPlus, defName: string, synthDefResult: SynthDefResultType, sourceCode: undefined | string, path: undefined | string): SynthDef

Property

name string

path undefined | string

server ServerPlus

sourceCode undefined | string

synthDefResult SynthDefResultType

13 | -------------------------------------------------------------------------------- /docs/packages/server-plus/api.md: -------------------------------------------------------------------------------- 1 | # @supercollider/server-plus 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/packages/server-plus/boot.md: -------------------------------------------------------------------------------- 1 | # boot 2 | Package: @supercollider/server-plus 3 | 4 |

boot(options: ServerArgs, store: Store): Promise<ServerPlus>

Start the scsynth server with options:

```js 5 | let server = await sc.server.boot({device: 'Soundflower (2ch)'}); 6 | ``` 7 |

8 | -------------------------------------------------------------------------------- /docs/packages/server/boot.md: -------------------------------------------------------------------------------- 1 | # boot 2 | Package: @supercollider/server 3 | 4 |

boot(options: ServerArgs = {}, store: any = null): Promise<Server>

Boot a server with options and connect

Returns - resolves with the Server
5 | -------------------------------------------------------------------------------- /docs/packages/server/deltaTimeTag.md: -------------------------------------------------------------------------------- 1 | # deltaTimeTag 2 | Package: @supercollider/server 3 | 4 |

deltaTimeTag(delta: number, now: Date): [number, number]

Make NTP timetag array relative to the current time.

5 | -------------------------------------------------------------------------------- /docs/packages/server/onNodeEnd.md: -------------------------------------------------------------------------------- 1 | # onNodeEnd 2 | Package: @supercollider/server 3 | 4 |

onNodeEnd(server: Server, id: string, nodeID: number, handler: Function): Function

Call a function when the server sends an `/n_end` message 5 | One callback allowed per id and nodeID.

Returns - cancel function
6 | -------------------------------------------------------------------------------- /docs/packages/server/onNodeGo.md: -------------------------------------------------------------------------------- 1 | # onNodeGo 2 | Package: @supercollider/server 3 | 4 |

onNodeGo(server: Server, id: string, nodeID: number, handler: Function): Function

Call a function when the server sends an `/n_go` message 5 | One callback allowed per id and node 6 | The id is usually a context id but could be a random guid

Returns - cancel function
7 | -------------------------------------------------------------------------------- /docs/packages/server/resolveOptions.md: -------------------------------------------------------------------------------- 1 | # resolveOptions 2 | Package: @supercollider/server 3 | 4 |

resolveOptions(options: ServerArgs = {}): ServerOptions

5 | -------------------------------------------------------------------------------- /docs/packages/server/updateNodeState.md: -------------------------------------------------------------------------------- 1 | # updateNodeState 2 | Package: @supercollider/server 3 | 4 |
5 | -------------------------------------------------------------------------------- /docs/packages/server/watchNodeNotifications.md: -------------------------------------------------------------------------------- 1 | # watchNodeNotifications 2 | Package: @supercollider/server 3 | 4 |

watchNodeNotifications(server: Server): Disposable

Watch server OSC receive for any n_XXX messages:

- `n_go` 5 | - `n_end` 6 | - `n_on` 7 | - `n_off` 8 | - `n_move` 9 | - `n_info` 10 | 11 | Save all of the supplied info for the node 12 | and call any registered callbacks. 13 | 14 | Initially there is no need to unwatch unless you are 15 | creating and discarding Server objects which can happen 16 | during testing. 17 | 18 | TODO: add Server.destroy 19 |

Returns - sub.dispose(); to turn it off.
20 | -------------------------------------------------------------------------------- /docs/packages/server/whenNodeEnd.md: -------------------------------------------------------------------------------- 1 | # whenNodeEnd 2 | Package: @supercollider/server 3 | 4 |

whenNodeEnd(server: Server, id: string, nodeID: number): Promise<number>

Returns a Promise that resolves when the server sends an `/n_end` message.

5 | -------------------------------------------------------------------------------- /docs/packages/server/whenNodeGo.md: -------------------------------------------------------------------------------- 1 | # whenNodeGo 2 | Package: @supercollider/server 3 | 4 |

whenNodeGo(server: Server, id: string, nodeID: number): Promise<number>

Returns a Promise that resolves when the server sends an 5 | `/n_go` message.

The id is usually a context id (dryadic) but could be any random guid. 6 | It can be anything you want to supply as long as it is unique. 7 |

Returns - resolves with nodeID
8 | -------------------------------------------------------------------------------- /docs/packages/supercolliderjs/SCLangError.md: -------------------------------------------------------------------------------- 1 | # SCLangError 2 | Package: supercolliderjs 3 | 4 |

class SCLangError

extends SCError

SCLangError - syntax errors while interpreting code, interpret code execution errors, and asynchronous errors.

Constructor

new SCLangError(message: string, type: string, error: object, data: any = {}): SCLangError

Property

data object

error object

message string

stack string

type string

Method
5 | -------------------------------------------------------------------------------- /docs/packages/supercolliderjs/exports.md: -------------------------------------------------------------------------------- 1 | # supercolliderjs exports 2 | 3 |
module index
4 | 5 | 6 | 7 | 8 | server 9 | 10 | http://localhost:3000/#/packages/server/Server 11 | http://localhost:3000/#/packages/server-plus/ServerPlus 12 | 13 | boot 14 | 15 | 16 | dryads 17 | dryads index 18 | 19 | lang 20 | lang index 21 | 22 | map 23 | http://localhost:3000/#/packages/server/mapping 24 | 25 | msg 26 | http://localhost:3000/#/packages/server/osc_msg 27 | 28 | server: { 29 | ...server, 30 | boot, 31 | server: ServerPlus, 32 | }, 33 | dryads, 34 | lang, 35 | map, 36 | msg, 37 | // why is this exported separately? 38 | SCLangError, 39 | // @deprecated 40 | resolveOptions, 41 | 42 | -------------------------------------------------------------------------------- /docs/packages/supercolliderjs/resolveOptions.md: -------------------------------------------------------------------------------- 1 | # resolveOptions 2 | Package: supercolliderjs 3 | 4 |

resolveOptions(options: ServerArgs = {}): ServerOptions

5 | -------------------------------------------------------------------------------- /docs/public/apple-touch-icon-144-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crucialfelix/supercolliderjs/2e7c2b4819cd19346656755d1cf37bf6cea37d04/docs/public/apple-touch-icon-144-precomposed.png -------------------------------------------------------------------------------- /docs/public/apple-touch-icon-256-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crucialfelix/supercolliderjs/2e7c2b4819cd19346656755d1cf37bf6cea37d04/docs/public/apple-touch-icon-256-precomposed.png -------------------------------------------------------------------------------- /docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crucialfelix/supercolliderjs/2e7c2b4819cd19346656755d1cf37bf6cea37d04/docs/public/favicon.ico -------------------------------------------------------------------------------- /docs/src/_coverpage.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | # supercollider.js {{version}} 7 | 8 | > The JavaScript client library for SuperCollider. 9 | 10 | [GitHub](https://github.com/crucialfelix/supercolliderjs/) 11 | [Docs](#supercolliderjs) 12 | 13 | 14 | 15 | ![color](#202020) 16 | 17 | -------------------------------------------------------------------------------- /docs/src/packages/dryads/README.md: -------------------------------------------------------------------------------- 1 | {{> header }} 2 | 3 | > A dryad (/ˈdraɪ.æd/; Greek: Δρυάδες, sing.: Δρυάς) is a tree nymph, or female tree spirit, in Greek mythology 4 | 5 | Dryadic is a framework for writing components that encapsulate all the complexities of loading, state management, dependencies and execution order. 6 | 7 | https://github.com/crucialfelix/dryadic 8 | 9 | Just as SuperCollider synths have a call graph or UGens, Dryadic has a call graph of higher level components. In Dryadic this is referred to as a tree. 10 | 11 | Dryadic is declarative: you specify the resources you want and how they are connected. The framework does the rest. 12 | Because it is declarative, you can update your tree while performing and the resources (servers, sounds, synth defs, settings) update live. 13 | 14 | ## Examples 15 | 16 | More extensive examples will come with dryadic 1.0 17 | 18 | {{#example}}examples/dryads-synth-event-list.js{{/example}} 19 | 20 | {{> footer }} -------------------------------------------------------------------------------- /docs/src/packages/dryads/api.md: -------------------------------------------------------------------------------- 1 | # @supercollider/dryads 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/src/packages/lang/README.md: -------------------------------------------------------------------------------- 1 | {{> header}} 2 | 3 | - Spawns and manages one or more `sclang` processes. 4 | - Interpret SuperCollider code and return results as equivalent JavaScript objects. 5 | - Compile SynthDefs written in the SuperCollider language and return byte code. 6 | - Used by atom-supercollider 7 | 8 | If you are building something that just needs to communicate with sclang then you can install just this package. 9 | 10 | ## Usage 11 | 12 | ### Boot 13 | 14 | Start the `sclang` executable as a subprocess, returning a Promise. 15 | 16 | {{#example}}examples/boot-lang.js{{/example}} 17 | 18 | ```js 19 | const Lang = require("supercolliderjs").lang.default; 20 | const l = new Lang(options); 21 | l.boot(); 22 | ``` 23 | 24 | `sclang` will compile it's class library, and this may result in syntax or compile errors. 25 | 26 | Resolves with a list of SuperCollider class file directories that were compiled: 27 | 28 | ```typescript 29 | {dirs: [/*compiled directories*/]} 30 | ``` 31 | 32 | or rejects with: 33 | 34 | ```typescript 35 | { 36 | dirs: [], 37 | compileErrors: [], 38 | parseErrors: [], 39 | duplicateClasses: [], 40 | errors[], 41 | extensionErrors: [], 42 | stdout: 'compiling class library...etc.' 43 | } 44 | ``` 45 | 46 | See `SclangCompileResult` in `packages/lang/src/internals/sclang-io.ts` for full details. 47 | 48 | ### Interpret simple async await style 49 | 50 | {{#example}}examples/lang-interpret.js{{/example}} 51 | 52 | ### Interpret with full error handling 53 | 54 | {{#example}}examples/lang-interpret-the-long-way.js{{/example}} 55 | 56 | 57 | ### Options 58 | 59 | ```typescript 60 | sc.lang.boot(options) 61 | // or 62 | const Lang = require("supercolliderjs").lang.default; 63 | const l = new Lang(options); 64 | l.boot(); 65 | ``` 66 | 67 | ```typescript 68 | { 69 | // post verbose messages to console 70 | debug: boolean; 71 | // echo all commands sent TO sclang to console 72 | echo: boolean; 73 | // provide an alternate console like object for logging. eg. winston 74 | log?: Console; 75 | // path to sclang executable 76 | sclang: string; 77 | // To start sclang and immediately execute one file 78 | executeFile?: string; 79 | // path to existing non-default conf file 80 | sclang_conf?: string; 81 | 82 | // post sclang stdin to console 83 | stdin: boolean; 84 | // if specifying a non-default conf file then you may wish to fail if you got the path wrong 85 | // rather than fall back to the default one 86 | failIfSclangConfIsMissing: boolean; 87 | // pass in a configuration without having to write it to a file 88 | conf: SCLangConf; 89 | } 90 | ``` 91 | 92 | See: packages/lang/src/options.ts 93 | 94 | 95 | ### executeFile 96 | 97 | ```js 98 | await lang.executeFile("./some-supercollider-piece.scd"); 99 | ``` 100 | 101 | {{> footer }} -------------------------------------------------------------------------------- /docs/src/packages/lang/api.md: -------------------------------------------------------------------------------- 1 | # @supercollider/lang 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/src/packages/logger/README.md: -------------------------------------------------------------------------------- 1 | {{> header}} 2 | 3 | This is used internally by other `@supercollider` packages. 4 | 5 | ## Usage 6 | 7 | {{#example}}examples/logger.js{{/example}} 8 | 9 | {{> footer }} -------------------------------------------------------------------------------- /docs/src/packages/logger/api.md: -------------------------------------------------------------------------------- 1 | # @supercollider/logger 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/src/packages/osc/README.md: -------------------------------------------------------------------------------- 1 | {{> header}} 2 | 3 | - Packs OSC messages and bundles into a Node `Buffer` for sending 4 | - Unpacks received OSC messages and bundles from a Node `Buffer` 5 | 6 | It does not concern itself with network connections. 7 | 8 | The OSC support is limited to the types and features of SuperCollider server. 9 | This means it does not support inline arrays `[f]` 10 | 11 | This is used internally by `@supercollider/server` 12 | 13 | ## Usage 14 | 15 | ```js 16 | const osc = require('@supercollider/osc'); 17 | 18 | // TODO: DEMONSTRATE API 19 | ``` 20 | 21 | {{> footer }} -------------------------------------------------------------------------------- /docs/src/packages/osc/api.md: -------------------------------------------------------------------------------- 1 | # @supercollider/osc 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/src/packages/scapi/README.md: -------------------------------------------------------------------------------- 1 | {{> header}} 2 | 3 | This works together with the 'API' quark to implement a simple two-way communication protocol for node <-> SuperCollider. 4 | 5 | It connects with an sclang process using UDP OSC and then sends OSC messages to '/API/call' 6 | 7 | The SuperCollider quark is here: 8 | https://github.com/supercollider-quarks/API 9 | 10 | And this package is the nodejs side. 11 | 12 | Sent messages return a promise, the responses are received here from sclang and the promises are resolved (or rejected if there was an error). 13 | 14 | This requires writing named handlers in SuperCollider and registering them with the API. From the node side, you make a call using that name and pass it some args and get back your response. 15 | 16 | This was an older solution. Probably just using `@supercollider/lang` is easier now. 17 | 18 | Note: this is not included in the [`supercolliderjs`](https://npmjs.org/package/supercolliderjs) package. 19 | 20 | ## Install 21 | 22 | ```shell 23 | npm install @supercollider/scapi 24 | ``` 25 | 26 | Start SuperCollider 27 | Install the API quark ( > 2.0 ) 28 | 29 | ## Usage 30 | 31 | Start SuperCollider and activate the OSC responders: 32 | 33 | ```supercollider 34 | API.mountDuplexOSC 35 | ``` 36 | 37 | {{> footer }} -------------------------------------------------------------------------------- /docs/src/packages/scapi/api.md: -------------------------------------------------------------------------------- 1 | # @supercollider/scapi 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/src/packages/server-plus/README.md: -------------------------------------------------------------------------------- 1 | {{> header}} 2 | 3 | This extends the `Server` class from `@supercollider/server`, adding methods for commonly used constructs. 4 | 5 | 6 | Each method returns a Promise that resolves when the resource is successfully created. Each method accepts Promises as arguments. 7 | 8 | {{#example}}examples/server-plus-promises.js{{/example}} 9 | 10 | ## synth 11 | Spawn a synth 12 | ```js 13 | synth( 14 | synthDef: SynthDef, 15 | args: Params = {}, 16 | group?: Group, 17 | addAction: number = msg.AddActions.TAIL, 18 | ): Promise; 19 | ``` 20 | 21 | ## group 22 | A collection of other nodes organized as a linked list. The 23 | Nodes within a Group may be controlled together, and may be both Synths and 24 | other Groups. Groups are thus useful for controlling a number of nodes at once, 25 | and when used as targets can be very helpful in controlling order of execution. 26 | 27 | ```js 28 | group(group?: Group, addAction: number = msg.AddActions.TAIL): Promise; 29 | ``` 30 | 31 | ## synthDefs 32 | Compile multiple SynthDefs either from source or path. 33 | If you have more than one to compile then always use this 34 | as calling `server.synthDef` multiple times will start up 35 | multiple supercollider interpreters. This is harmless, but 36 | very inefficient. 37 | 38 | defs - An object with `{defName: spec, ...}` where spec is 39 | an object like `{source: "SynthDef('noise', { ...})"}` 40 | or `{path: "./noise.scd"}` 41 | 42 | Returns an object with the synthDef names as keys and Promises as values. 43 | Each Promise will resolve with a SynthDef. 44 | Each Promises can be supplied directly to `server.synth()` 45 | 46 | ```js 47 | synthDefs(defs: { [defName: string]: SynthDefCompileRequest }): { [defName: string]: Promise } 48 | ``` 49 | 50 | ## loadSynthDef 51 | Load and compile a SynthDef from path and send it to the server. 52 | ```js 53 | loadSynthDef(defName: string, path: string): Promise; 54 | ``` 55 | 56 | ## synthDef 57 | Compile a SynthDef from supercollider source code and send it to the server. 58 | ```js 59 | synthDef(defName: string, sourceCode: string): Promise; 60 | ``` 61 | 62 | ## buffer 63 | Allocate a Buffer on the server. 64 | ```js 65 | buffer(numFrames: number, numChannels = 1): Promise; 66 | ``` 67 | 68 | ## audioBus 69 | Allocate an audio bus. 70 | ```js 71 | audioBus(numChannels = 1): AudioBus; 72 | ``` 73 | 74 | ## controlBus 75 | Allocate a control bus. 76 | ```js 77 | controlBus(numChannels = 1): ControlBus; 78 | ``` 79 | 80 | ## readBuffer 81 | Allocate a Buffer on the server and load a sound file into it. 82 | Problem: scsynth uses however many channels there are in the sound file, 83 | but the client (sclang or supercolliderjs) doesn't know how many there are. 84 | 85 | ```js 86 | readBuffer(path: string, numChannels = 2, startFrame = 0, numFramesToRead = -1): Promise; 87 | ``` 88 | 89 | 90 | ### Kitchen sink 91 | 92 | {{#example}}examples/server-plus.js{{/example}} 93 | 94 | {{> footer }} -------------------------------------------------------------------------------- /docs/src/packages/server-plus/api.md: -------------------------------------------------------------------------------- 1 | # @supercollider/server-plus 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/src/packages/supercolliderjs/README.md: -------------------------------------------------------------------------------- 1 | {{> header}} 2 | 3 | `supercollider.js` is a full-featured, batteries included client library for the `SuperCollider` audio synthesis server and the SuperCollider language interpreter. 4 | 5 | It can be used for algorithmic composition, live coding, playing sounds with MIDI, audio processing, sound file rendering, data sonification and more. 6 | 7 | It is written in TypeScript and compiled for release as ES2018 (Node >= 10) JavaScript and can be used in Node applications for either JavaScript or TypeScript. 8 | 9 | 30 | 31 | ## Install 32 | 33 | 1. Install SuperCollider: 34 | https://supercollider.github.io/ 35 | 36 | 2. Install supercolliderjs: 37 | ```shell 38 | npm install supercolliderjs 39 | ``` 40 | 41 | ## Examples 42 | 43 | 44 | There are several interfaces, ranging from low-level (tedious, error-prone) control to higher level constructs. 45 | 46 | {{#example}}examples/server-plus.js{{/example}} 47 | 48 | 49 | ### SynthDef and Synth 50 | 51 | A SuperCollider SynthDef defines a graph of [Unit generators](https://en.wikipedia.org/wiki/Unit_generator). It wires together inputs and outputs, oscillators and filters. Once it is compiled and sent to the server, then you can create Synths that play that sound. 52 | 53 | Currently supercollider.js uses `sclang` to compile synth defs. Full support for writing and compiling SynthDefs from JavaScript is planned. 54 | 55 | {{#example}}examples/synthdef-and-synth.js{{/example}} 56 | 57 | {{#example}}examples/bubbles.js{{/example}} 58 | 59 | [Server Plus](./packages/server-plus/README.md) 60 | [Server](./packages/server/README.md) 61 | 62 | ### lang 63 | 64 | - Spawns the language interpreter, `sclang` 65 | - Call SuperCollider code from JavaScript 66 | 67 | {{#example}}examples/lang-interpret.js{{/example}} 68 | 69 | {{> footer }} 70 | -------------------------------------------------------------------------------- /docs/src/partials/footer.md: -------------------------------------------------------------------------------- 1 | Documentation 2 | ------------- 3 | 4 | [Documentation]({{{docsRoot}}}/packages/{{{short}}}/api) 5 | 6 | Compatibility 7 | ------------- 8 | 9 | Works on Node 10+ 10 | 11 | Source code is written in TypeScript and is usable in JavaScript [es2018](https://2ality.com/2017/02/ecmascript-2018.html) or [TypeScript](https://www.typescriptlang.org/docs/home.html) projects. 12 | 13 | Contribute 14 | ---------- 15 | 16 | - Issue Tracker: {{{repository}}}/issues 17 | - Source Code: {{{repository}}} 18 | 19 | License 20 | ------- 21 | 22 | MIT license 23 | 24 | [license-image]: http://img.shields.io/badge/license-MIT-blue.svg?style=flat 25 | [license-url]: LICENSE 26 | 27 | [npm-url]: https://npmjs.org/package/{{{name}}} 28 | [npm-version-image]: http://img.shields.io/npm/v/{{{name}}}.svg?style=flat 29 | [npm-downloads-image]: http://img.shields.io/npm/dm/{{{name}}}.svg?style=flat 30 | 31 | [travis-url]: http://travis-ci.org/crucialfelix/supercolliderjs 32 | [travis-image]: https://travis-ci.org/crucialfelix/supercolliderjs.svg?branch=master 33 | -------------------------------------------------------------------------------- /docs/src/partials/header.md: -------------------------------------------------------------------------------- 1 | # {{{name}}} 2 | [![NPM downloads][npm-downloads-image]][npm-url] [![MIT License][license-image]][license-url] 3 | 4 | {{{description}}} 5 | -------------------------------------------------------------------------------- /examples/boot-lang.js: -------------------------------------------------------------------------------- 1 | const sc = require("supercolliderjs"); 2 | 3 | sc.lang.boot().then( 4 | function(lang) { 5 | // Up and ready for action 6 | console.log(lang); 7 | 8 | // quit the process programmatically 9 | lang.quit(); 10 | }, 11 | // Error handler if it fails to start or fails to compile 12 | error => console.error, 13 | ); 14 | -------------------------------------------------------------------------------- /examples/bubbles.js: -------------------------------------------------------------------------------- 1 | const sc = require("supercolliderjs"); 2 | 3 | sc.server.boot().then(server => { 4 | const def = server.synthDef( 5 | "bubbles", 6 | ` 7 | SynthDef("bubbles", { arg out=0, wobble=0.4, innerWobble=8, releaseTime=4; 8 | var f, zout; 9 | f = LFSaw.kr(wobble, 0, 24, LFSaw.kr([innerWobble, innerWobble / 1.106], 0, 3, 80)).midicps; 10 | zout = CombN.ar(SinOsc.ar(f, 0, 0.04), 0.2, 0.2, 4); // echoing sine wave 11 | zout = zout * EnvGen.kr(Env.linen(releaseTime: releaseTime), doneAction: 2); 12 | Out.ar(out, zout); 13 | }); 14 | `, 15 | ); 16 | 17 | setInterval(() => { 18 | server.synth(def, { 19 | wobble: Math.random() * 10, 20 | innerWobble: Math.random() * 16, 21 | releaseTime: Math.random() * 4 + 2, 22 | }); 23 | }, 4000); 24 | }); 25 | -------------------------------------------------------------------------------- /examples/dryads-synth-event-list.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This example generates a random sequence and plays the first 3 | * 4 seconds in a loop. 4 | * 5 | * SynthEventList has other tricks not yet shown here yet. 6 | * You can reschedule or alter the event list while playing. 7 | * 8 | * The scheduler can be given different scheduling functions, 9 | * one of which is this sequential event list. 10 | * 11 | * Other versions could use tempo, mathematical formula, 12 | * non-deterministic, event-by-event scheduling. 13 | */ 14 | const { play, SCSynthDef, SynthEventList, SCServer } = require("supercolliderjs").dryads; 15 | 16 | function randomEvent(totalDuration) { 17 | return { 18 | time: Math.random() * totalDuration, 19 | defName: "saw", 20 | args: { 21 | freq: Math.random() * 500 + 100, 22 | }, 23 | }; 24 | } 25 | 26 | function randomEvents(totalDuration = 60, density = 2) { 27 | const n = Math.floor(totalDuration * density); 28 | const events = []; 29 | for (let index = 0; index < n; index++) { 30 | events.push(randomEvent(totalDuration)); 31 | } 32 | return events; 33 | } 34 | 35 | // Currently this has to be expressed as a tree of nested dependencies. 36 | // dryadic 2 will make it much cleaner and simpler. 37 | const out = new SCServer({ numInputBusChannels: 0 }, [ 38 | new SCSynthDef( 39 | { 40 | source: ` 41 | SynthDef("saw", { arg freq; 42 | Out.ar(0, EnvGen.kr(Env.perc, doneAction: 2) * Saw.ar(freq)) 43 | }); 44 | `, 45 | }, 46 | [ 47 | new SynthEventList({ 48 | events: randomEvents(60, 4), 49 | loopTime: 65, 50 | }), 51 | ], 52 | ), 53 | ]); 54 | 55 | play(out); 56 | -------------------------------------------------------------------------------- /examples/formant.scd: -------------------------------------------------------------------------------- 1 | 2 | // Used in group.js and bus.js examples 3 | Spec.specs.put(\fundfreq, ControlSpec.new(40, 10240, 'exp', 0, 0)); 4 | Spec.specs.put(\formantfreq, ControlSpec.new(40, 10240, 'exp', 0, 0)); 5 | Spec.specs.put(\bwfreq, ControlSpec.new(40, 10240, 'exp', 0, 0)); 6 | 7 | SynthDef(\formant, { arg out=0, fundfreq=440, formantfreq=440, bwfreq=100, timeScale=1, pan=0; 8 | var saw, envd, panned; 9 | 10 | saw = Formant.ar(fundfreq, formantfreq, bwfreq); 11 | 12 | envd = saw * EnvGen.kr(Env.sine(0.1, 0.2), timeScale: timeScale, doneAction: 2); 13 | panned = Pan2.ar(envd * AmpCompA.kr(fundfreq, 0.2, 0.7), pan); 14 | 15 | OffsetOut.ar(out, panned); 16 | }).add; 17 | -------------------------------------------------------------------------------- /examples/klang.scd: -------------------------------------------------------------------------------- 1 | SynthDef("klang", { 2 | var nPartials = 12, nChans = 5; 3 | var n = nPartials * nChans; 4 | Splay.ar(Klang.ar(`[ { { rrand(200.0, 2000.0) } ! nPartials } ! nChans, nil, nil ], 1, 0)) 5 | * EnvGen.kr(Env.sine(4), 1, 0.02, doneAction: Done.freeSelf); 6 | }); -------------------------------------------------------------------------------- /examples/lang-interpret-the-long-way.js: -------------------------------------------------------------------------------- 1 | const sc = require("supercolliderjs"); 2 | 3 | function makePyramid(lang) { 4 | lang.interpret("(1..8).pyramid").then( 5 | function(result) { 6 | // result is a native javascript array 7 | console.log("= " + result); 8 | lang.quit(); 9 | }, 10 | function(error) { 11 | // syntax or runtime errors 12 | // are returned as javascript objects 13 | console.error(error); 14 | }, 15 | ); 16 | } 17 | 18 | // Verbose example to show Promises and full error handling 19 | sc.lang.boot().then( 20 | // ok booted 21 | lang => { 22 | makePyramid(lang); 23 | }, 24 | // failed to boot 25 | error => { 26 | console.error(error); 27 | // Either: 28 | // 1. The executable may be missing, incorrect path etc. 29 | // 2. The class library may have failed with compile errors 30 | }, 31 | ); 32 | -------------------------------------------------------------------------------- /examples/lang-interpret.js: -------------------------------------------------------------------------------- 1 | const sc = require("supercolliderjs"); 2 | 3 | sc.lang.boot().then(async function(lang) { 4 | // This function is declared as `async` 5 | // so for any function calls that return a Promise we can `await` the result. 6 | 7 | // This is an `async` function, so we can `await` the results of Promises. 8 | const pyr8 = await lang.interpret("(1..8).pyramid"); 9 | console.log(pyr8); 10 | 11 | const threePromises = [16, 24, 32].map(n => { 12 | return lang.interpret(`(1..${n}).pyramid`); 13 | }); 14 | 15 | // `interpret` many at the same time and wait until all are fulfilled. 16 | // Note that `lang` is single threaded, 17 | // so the requests will still be processed by the interpreter one at a time. 18 | const pyrs = await Promise.all(threePromises); 19 | console.log(pyrs); 20 | 21 | // Get a list of all UGen subclasses 22 | const allUgens = await lang.interpret("UGen.allSubclasses"); 23 | 24 | // Post each one to STDOUT 25 | allUgens.forEach(ugenClass => console.log(ugenClass)); 26 | 27 | await lang.quit(); 28 | }); 29 | -------------------------------------------------------------------------------- /examples/logger.js: -------------------------------------------------------------------------------- 1 | const Logger = require("@supercollider/logger").default; 2 | 3 | const debug = true; 4 | const echo = true; 5 | 6 | const log = new Logger(debug, echo); 7 | // Log an error. 8 | log.err("Oh no!"); 9 | // Log debugging information but only if this.debug is true 10 | log.dbug({ log: "log", some: 1, context: 2, for: "The problem" }); 11 | // Log messages that were sent to stdin or sclang. 12 | log.stdin("1 + 1"); 13 | // Log messages that were received from stdout of sclang/scsynth. 14 | log.stdout("2"); 15 | // Log messages that were emitted from stderr of sclang/scsynth. 16 | log.stderr("ERROR: ..."); 17 | // Log OSC messages sent to scsynth. 18 | log.sendosc({ address: "/ping" }); 19 | // Log OSC messages received from scsynth. 20 | log.rcvosc({ value: "pong" }); 21 | -------------------------------------------------------------------------------- /examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples", 3 | "version": "1.0.0", 4 | "description": "supercollider.js examples", 5 | "author": "Chris Sattinger ", 6 | "license": "ISC", 7 | "dependencies": { 8 | "supercolliderjs": "1.0.0-alpha.1" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/server-plus-promises.js: -------------------------------------------------------------------------------- 1 | const sc = require("supercolliderjs"); 2 | 3 | sc.server.boot().then(async server => { 4 | // Compile synthDef from a file, returning a Promise 5 | const synthDef = server.loadSynthDef("formant", "./formant.scd"); 6 | 7 | // Map 0..1 to an exponential frequency range from 100..8000 8 | const randFreq = () => 9 | sc.map.mapWithSpec(Math.random(), { 10 | minval: 100, 11 | maxval: 8000, 12 | warp: "exp", 13 | }); 14 | 15 | const synthPromise = server.synth( 16 | // The promise will be resolved before the command to create the synth 17 | // is sent. 18 | synthDef, 19 | { 20 | fundfreq: randFreq(), 21 | formantfreq: randFreq(), 22 | bwfreq: randFreq(), 23 | pan: sc.map.linToLin(0, 1, -1, 1, Math.random()), 24 | }, 25 | ); 26 | 27 | // await a promise to get it's value 28 | const synth = await synthPromise; 29 | 30 | // This continues execution after the "node is playing" response is received. 31 | console.log(synth); 32 | }); 33 | -------------------------------------------------------------------------------- /examples/server-plus.js: -------------------------------------------------------------------------------- 1 | // @supercollider/server-plus interface 2 | const sc = require("supercolliderjs"); 3 | 4 | sc.server.boot().then(async server => { 5 | // Compile a SynthDef from inline SuperCollider language code and send it to the server 6 | const def = await server.synthDef( 7 | "formant", 8 | `{ |out=0, fundfreq=440, formantfreq=440, bwfreq=100, timeScale=1, pan=0| 9 | var saw, envd, panned; 10 | 11 | saw = Formant.ar(fundfreq, formantfreq, bwfreq); 12 | 13 | envd = saw * EnvGen.kr(Env.sine(0.1, 0.2), timeScale: timeScale, doneAction: 2); 14 | panned = Pan2.ar(envd * AmpCompA.kr(fundfreq, 0.2, 0.7), pan); 15 | 16 | OffsetOut.ar(out, panned); 17 | }`, 18 | ); 19 | 20 | // Create group at the root 21 | const group = server.group(); 22 | 23 | const freqSpec = { 24 | minval: 100, 25 | maxval: 8000, 26 | warp: "exp", 27 | }; 28 | 29 | // Map 0..1 to an exponential frequency range from 100..8000 30 | const randFreq = () => sc.map.mapWithSpec(Math.random(), freqSpec); 31 | 32 | // function to spawn one synth event 33 | const spawn = dur => { 34 | server.synth( 35 | def, 36 | { 37 | fundfreq: randFreq(), 38 | formantfreq: randFreq(), 39 | bwfreq: randFreq(), 40 | pan: sc.map.linToLin(0, 1, -1, 1, Math.random()), 41 | timeScale: dur, 42 | // spawn each synth into the same group 43 | }, 44 | group, 45 | ); 46 | 47 | const next = Math.random() * 0.25; 48 | 49 | // Schedule this function again: 50 | setTimeout(() => spawn(next), next * 1000); 51 | }; 52 | 53 | // spawn the first event 54 | spawn(Math.random()); 55 | }, console.error); 56 | -------------------------------------------------------------------------------- /examples/server.js: -------------------------------------------------------------------------------- 1 | const sc = require("supercolliderjs"); 2 | 3 | sc.server.boot().then(async sc => { 4 | // Allocate an 8 second stereo audio buffer 5 | const b = await sc.buffer(44 * 1024 * 8, 2); 6 | }); 7 | -------------------------------------------------------------------------------- /examples/serverp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // @supercollider/server-plus interface 4 | const sc = require('supercolliderjs'); 5 | 6 | sc.server.boot().then((server) => { 7 | 8 | // Compile a SynthDef from a supercollider language file. 9 | // It will also watch the file and recompile and send to server when you save. 10 | // You can live code the synth def while this example plays. 11 | let def = server.loadSynthDef('formant', './formant.scd'); 12 | 13 | // Create group at the root 14 | let group = server.group(); 15 | 16 | let freqSpec = { 17 | minval: 100, 18 | maxval: 8000, 19 | warp: 'exp' 20 | }; 21 | 22 | // Map 0..1 to an exponential frequency range from 100..8000 23 | let randFreq = () => sc.map.mapWithSpec(Math.random(), freqSpec); 24 | 25 | let spawn = (dur) => { 26 | server.synth(def, { 27 | fundfreq: randFreq(), 28 | formantfreq: randFreq(), 29 | bwfreq: randFreq(), 30 | pan: sc.map.linToLin(0, 1, -1, 1, Math.random()), 31 | timeScale: dur 32 | // spawn each synth into the same group 33 | }, group); 34 | 35 | let next = Math.random() * 0.25; 36 | 37 | // Schedule this function again: 38 | setTimeout(() => spawn(next), next * 1000); 39 | }; 40 | 41 | spawn(Math.random()); 42 | 43 | }); 44 | // @supercollider/server-plus interface 45 | const sc = require('supercolliderjs'); 46 | 47 | sc.server.boot().then((server) => { 48 | 49 | // Compile a SynthDef from a supercollider language file. 50 | // It will also watch the file and recompile and send to server when you save. 51 | // You can live code the synth def while this example plays. 52 | let def = server.loadSynthDef('formant', './formant.scd'); 53 | 54 | // Create group at the root 55 | let group = server.group(); 56 | 57 | let freqSpec = { 58 | minval: 100, 59 | maxval: 8000, 60 | warp: 'exp' 61 | }; 62 | 63 | // Map 0..1 to an exponential frequency range from 100..8000 64 | let randFreq = () => sc.map.mapWithSpec(Math.random(), freqSpec); 65 | 66 | let spawn = (dur) => { 67 | server.synth(def, { 68 | fundfreq: randFreq(), 69 | formantfreq: randFreq(), 70 | bwfreq: randFreq(), 71 | pan: sc.map.linToLin(0, 1, -1, 1, Math.random()), 72 | timeScale: dur 73 | // spawn each synth into the same group 74 | }, group); 75 | 76 | let next = Math.random() * 0.25; 77 | 78 | // Schedule this function again: 79 | setTimeout(() => spawn(next), next * 1000); 80 | }; 81 | 82 | spawn(Math.random()); 83 | 84 | }); 85 | -------------------------------------------------------------------------------- /examples/sine-wave.js: -------------------------------------------------------------------------------- 1 | const { SCLang, SCServer, Synth, dryadic } = require("supercolliderjs").dryads; 2 | 3 | /** 4 | * Minimal Sin wave example. 5 | * 6 | * To compile the SuperCollider language synth def it requires a 7 | * SCLang. To play the Synth it requires a SCServer. 8 | */ 9 | const out = new SCLang({}, [ 10 | new SCServer({}, [ 11 | Synth.fromSource(`|freq| SinOsc.ar(freq)`, { 12 | freq: 440, 13 | }), 14 | ]), 15 | ]); 16 | 17 | dryadic(out).play(); 18 | -------------------------------------------------------------------------------- /examples/synthdef-and-synth.js: -------------------------------------------------------------------------------- 1 | const sc = require("supercolliderjs"); 2 | 3 | sc.server.boot().then(async server => { 4 | // Compile and send to server from inline SuperCollider code 5 | const pulse = await server.synthDef( 6 | "pulse", 7 | `{ arg out = 0, freq=200; 8 | Out.ar(out, Pulse.ar(200, Line.kr(0.01,0.99,8), 0.2)) 9 | }`, 10 | ); 11 | 12 | await server.synth(pulse, { freq: 300 }); 13 | 14 | // Load and compile from a SuperCollider language file 15 | const klang = await server.loadSynthDef("klang", "./klang.scd"); 16 | 17 | await server.synth(klang); 18 | 19 | // Compile or load many at once 20 | const defs = await server.synthDefs({ 21 | clipNoise: { 22 | source: `{ arg out = 0; 23 | var clip = LFClipNoise.ar(MouseX.kr(200, 10000, 1), 0.125); 24 | Out.ar(out, clip); 25 | }`, 26 | }, 27 | lpf: { 28 | source: `{ arg in = 0, out = 0; 29 | var input = In.ar(in); 30 | var lpf = RLPF.ar(input, MouseY.kr(1e2, 2e4, 1), 0.2, 0.2); 31 | ReplaceOut.ar(out, lpf); 32 | }`, 33 | }, 34 | }); 35 | 36 | // Some people pre-compile synth defs to a binary format. 37 | // Load a pre-compiled synth def from a binary file. 38 | const defLoadMsg = sc.server.msg.defLoad("./formant.scsyndef"); 39 | // This object has two properties: 40 | // .call - the OSCMessage to send 41 | // .response - the expected OSCMessage that the server will reply with 42 | // Call and wait for the response: 43 | await server.callAndResponse(defLoadMsg); 44 | 45 | // Load an entire directory of pre-compiled synth defs 46 | // await server.callAndResponse(sc.server.msg.defLoadDir("./synthdefs/")); 47 | }); 48 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "independent" 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supercolliderjs", 3 | "description": "JavaScript library for the SuperCollider music language and synthesis server", 4 | "version": "1.0.1", 5 | "author": "Chris Sattinger ", 6 | "contributors": [ 7 | { 8 | "name": "Chris Sattinger", 9 | "email": "crucialfelix@gmail.com" 10 | } 11 | ], 12 | "private": true, 13 | "dependencies": {}, 14 | "devDependencies": { 15 | "@types/jest": "24.0.23", 16 | "@types/js-yaml": "3.12.1", 17 | "@types/lodash": "4.14.149", 18 | "@types/rx": "4.1.1", 19 | "@types/temp": "0.8.34", 20 | "@typescript-eslint/eslint-plugin": "2.10.0", 21 | "@typescript-eslint/parser": "2.10.0", 22 | "docsify-cli": "4.4.0", 23 | "eslint": "6.7.2", 24 | "eslint-config-prettier": "6.7.0", 25 | "eslint-plugin-prettier": "3.1.1", 26 | "jest": "^24.9.0", 27 | "lerna": "3.19.0", 28 | "mustache": "3.1.0", 29 | "prettier": "1.19.1", 30 | "rimraf": "3.0.0", 31 | "ts-jest": "24.2.0", 32 | "typedoc": "0.15.3", 33 | "typescript": "3.7.3" 34 | }, 35 | "license": "MIT", 36 | "keywords": [ 37 | "supercollider", 38 | "synthesis", 39 | "music", 40 | "sound", 41 | "creative-coding", 42 | "creative" 43 | ], 44 | "repository": { 45 | "type": "git", 46 | "url": "git://github.com/crucialfelix/supercolliderjs" 47 | }, 48 | "scripts": { 49 | "postinstall": "npm run bootstrap && npm run build", 50 | "bootstrap": "lerna bootstrap --hoist", 51 | "build": "lerna exec -- tsc -b .", 52 | "test": "npm run jest && npm run lint", 53 | "jest": "lerna exec -- jest --rootDir=. --config=../jest.config.json --passWithNoTests", 54 | "lint": "eslint --ext .ts packages/", 55 | "clean": "lerna exec -- rimraf lib && lerna exec -- rm -f tsconfig.tsbuildinfo", 56 | "docs:api": "./tasks/typedocs.sh", 57 | "docs:md": "node ./tasks/make-docs.js", 58 | "docs:build": "npm run build && npm run docs:api && npm run docs:export-index && npm run docs:md", 59 | "docs:serve": "docsify serve docs", 60 | "docs:export-index": "lerna exec -- node $(PWD)/tasks/export-index.js" 61 | }, 62 | "eslintIgnore": [ 63 | "node_modules", 64 | "lib" 65 | ], 66 | "engines": { 67 | "node": ">= 10.0.0" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /packages/dryads/.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !src/**/* 3 | src/**/__tests__/**/* 4 | !lib/**/* 5 | !package.json 6 | !README.md 7 | !CHANGELOG.md 8 | !LICENSE -------------------------------------------------------------------------------- /packages/dryads/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@supercollider/dryads", 3 | "version": "1.0.1", 4 | "description": "Dryadic components for SuperCollider", 5 | "keywords": [ 6 | "supercollider", 7 | "dryadic" 8 | ], 9 | "author": "Chris Sattinger ", 10 | "homepage": "https://crucialfelix.github.io/supercolliderjs/", 11 | "license": "MIT", 12 | "main": "lib/index.js", 13 | "typings": "lib/index.d.ts", 14 | "directories": { 15 | "lib": "lib" 16 | }, 17 | "publishConfig": { 18 | "access": "public" 19 | }, 20 | "dependencies": { 21 | "@supercollider/lang": "^1.0.1", 22 | "@supercollider/logger": "^1.0.0", 23 | "@supercollider/server": "^1.0.0", 24 | "baconjs": "^0.7.93", 25 | "dryadic": "^1.0.0", 26 | "lodash": "^4.17.15" 27 | }, 28 | "devDependencies": { 29 | "@types/baconjs": "0.7.33" 30 | }, 31 | "repository": { 32 | "type": "git", 33 | "url": "git+https://github.com/crucialfelix/supercolliderjs.git" 34 | }, 35 | "bugs": { 36 | "url": "https://github.com/crucialfelix/supercolliderjs/issues" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/dryads/src/AudioBus.ts: -------------------------------------------------------------------------------- 1 | import { Dryad, Command, CallOrder } from "dryadic"; 2 | import Server from "@supercollider/server"; 3 | 4 | interface Properties { 5 | numChannels: number; 6 | } 7 | interface Context { 8 | // parent 9 | scserver: Server; 10 | // state 11 | out: number; 12 | } 13 | 14 | /** 15 | * Allocates an audio bus, making it available in the children's context as .out (integer) 16 | * and .numChannels (integer) 17 | */ 18 | export default class AudioBus extends Dryad { 19 | defaultProperties(): Properties { 20 | return { 21 | numChannels: 1, 22 | }; 23 | } 24 | 25 | /** 26 | * If there is no SCServer in the parent context, 27 | * then this will wrap itself in an SCServer 28 | */ 29 | requireParent(): string { 30 | return "SCServer"; 31 | } 32 | 33 | prepareForAdd(): Command { 34 | return { 35 | callOrder: CallOrder.SELF_THEN_CHILDREN, 36 | updateContext: (context: Context, properties: Properties) => ({ 37 | out: context.scserver.state.allocAudioBus(properties.numChannels), 38 | numChannels: properties.numChannels, 39 | }), 40 | }; 41 | } 42 | 43 | remove(): Command { 44 | return { 45 | run: (context: Context, properties: Properties) => 46 | context.scserver.state.freeAudioBus(context.out, properties.numChannels), 47 | }; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/dryads/src/Group.ts: -------------------------------------------------------------------------------- 1 | import { Dryad, Command, CallOrder } from "dryadic"; 2 | 3 | import Server, { msg, whenNodeEnd, whenNodeGo } from "@supercollider/server"; 4 | 5 | const { AddActions, groupNew, nodeFree } = msg; 6 | 7 | interface Context { 8 | nodeID: number; 9 | group: number; 10 | parentGroup: number; 11 | scserver: Server; 12 | id: string; 13 | } 14 | /** 15 | * Creates a group on the server; sets .group in context for its children, 16 | * so any Synths or Groups will be spawned inside this group. 17 | */ 18 | export default class Group extends Dryad { 19 | /** 20 | * If there is no SCServer in the parent context, 21 | * then this will wrap itself in an SCServer 22 | */ 23 | requireParent(): string { 24 | return "SCServer"; 25 | } 26 | 27 | prepareForAdd(): Command { 28 | return { 29 | callOrder: CallOrder.SELF_THEN_CHILDREN, 30 | updateContext: (context: Context /*, properties*/) => { 31 | const nodeID = context.scserver.state.nextNodeID(); 32 | return { 33 | nodeID, 34 | // TODO: but this overwrites my own group ! 35 | // what if parent is a group ? 36 | // I need to create this group within that group 37 | // This should just be childContext, 38 | // but that is only called when creating the tree. 39 | group: nodeID, 40 | // for now, save it to parentGroup 41 | parentGroup: context.group || 0, 42 | }; 43 | }, 44 | }; 45 | } 46 | 47 | add(): Command { 48 | return { 49 | scserver: { 50 | msg: (context: Context) => groupNew(context.nodeID, AddActions.TAIL, context.parentGroup), 51 | }, 52 | run: (context: Context) => whenNodeGo(context.scserver, context.id, context.nodeID), 53 | }; 54 | } 55 | 56 | remove(): Command { 57 | return { 58 | scserver: { 59 | // children do not have to free their nodes 60 | // as they get freed by freeing this parent 61 | // so remove for children needs to communicate that somehow 62 | // but buffers and busses do need to free 63 | msg: (context: Context) => nodeFree(context.nodeID), 64 | }, 65 | run: (context: Context) => whenNodeEnd(context.scserver, context.id, context.nodeID), 66 | }; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /packages/dryads/src/SCLang.ts: -------------------------------------------------------------------------------- 1 | import { Dryad, CallOrder, Command } from "dryadic"; 2 | import _ from "lodash"; 3 | 4 | import LanguageServer, { boot, SCLangArgs } from "@supercollider/lang"; 5 | import Logger from "@supercollider/logger"; 6 | 7 | const defaultOptions: SCLangArgs = { 8 | debug: true, 9 | echo: false, 10 | stdin: false, 11 | }; 12 | 13 | interface Properties { 14 | options: SCLangArgs; 15 | } 16 | 17 | interface Context { 18 | sclang?: LanguageServer; 19 | log?: Logger; 20 | } 21 | 22 | /** 23 | * Boots a new SuperCollider language interpreter (sclang) making it available for all children as context.sclang 24 | * 25 | * Always boots a new one, ignoring any possibly already existing one in the parent context. 26 | * 27 | * `options` are the command line options supplied to sclang (note: not all options are passed through yet) 28 | * see {@link lang/SCLang} 29 | * 30 | * Not to be confused with the other class named SCLang which does all the hard work. 31 | * This Dryad class is just a simple wrapper around that. 32 | */ 33 | export default class SCLang extends Dryad { 34 | defaultProperties(): Properties { 35 | return { 36 | options: defaultOptions, 37 | }; 38 | } 39 | 40 | prepareForAdd(): Command { 41 | return { 42 | callOrder: CallOrder.SELF_THEN_CHILDREN, 43 | updateContext: (context: Context, properties: Properties) => ({ 44 | sclang: boot(_.defaults(properties.options, { log: context.log })), 45 | }), 46 | }; 47 | } 48 | 49 | remove(): Command { 50 | return { 51 | run: (context: Context) => { 52 | return context.sclang && context.sclang.quit(); 53 | }, 54 | }; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/dryads/src/SCServer.ts: -------------------------------------------------------------------------------- 1 | import { Dryad, CallOrder, Command, Properties } from "dryadic"; 2 | import _ from "lodash"; 3 | 4 | import Server, { boot, ServerArgs } from "@supercollider/server"; 5 | import Logger from "@supercollider/logger"; 6 | 7 | const defaultOptions = { 8 | debug: false, 9 | }; 10 | 11 | interface ServerProperties extends Properties { 12 | options: ServerArgs; 13 | } 14 | 15 | interface Context { 16 | out: number; 17 | group: number; 18 | log?: Logger; 19 | scserver?: Server; 20 | } 21 | 22 | /** 23 | * Boots a new SuperCollider server (scsynth) making it available for all children as `context.scserver` 24 | * 25 | * Always boots a new one, ignoring any possibly already existing one in the parent context. 26 | * 27 | * `options` are the command line options supplied to scsynth (note: not all options are passed through yet) 28 | * see {@link Server} 29 | */ 30 | export default class SCServer extends Dryad { 31 | defaultProperties(): ServerProperties { 32 | return { 33 | options: defaultOptions, 34 | }; 35 | } 36 | 37 | initialContext(): Context { 38 | return { 39 | out: 0, 40 | group: 0, 41 | }; 42 | } 43 | 44 | prepareForAdd(): Command { 45 | return { 46 | callOrder: CallOrder.SELF_THEN_CHILDREN, 47 | updateContext: (context: Context, properties: ServerProperties) => ({ 48 | scserver: boot(_.defaults(properties.options, { log: context.log })), 49 | }), 50 | }; 51 | } 52 | 53 | remove(): Command { 54 | return { 55 | run: (context: Context) => { 56 | if (context.scserver) { 57 | return context.scserver.quit(); 58 | } 59 | }, 60 | }; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/dryads/src/SynthControl.ts: -------------------------------------------------------------------------------- 1 | import { msg, OscType } from "@supercollider/server"; 2 | import { EventStream } from "baconjs"; 3 | import { Dryad, DryadPlayer, Command } from "dryadic"; 4 | import _ from "lodash"; 5 | 6 | const { nodeSet } = msg; 7 | 8 | interface Params { 9 | [name: string]: OscType; 10 | } 11 | 12 | interface Properties { 13 | stream: EventStream; 14 | } 15 | 16 | interface Context { 17 | id: string; 18 | nodeID?: number; 19 | subscription?: any; 20 | } 21 | /** 22 | * Sends nodeSet messages to the Synth in the parent context. 23 | * 24 | * This takes a Bacon.js stream which should return objects 25 | * {param: value, ...} and sends `nodeSet` messages to the parent Synth. 26 | * 27 | * SynthControl should be a child of a Synth, Group or other Dryad that 28 | * sets context.nodeID 29 | */ 30 | export default class SynthControl extends Dryad { 31 | /** 32 | * If there is no SCServer in the parent context, 33 | * then this will wrap itself in an SCServer 34 | */ 35 | requireParent(): string { 36 | return "SCServer"; 37 | } 38 | 39 | add(player: DryadPlayer): Command { 40 | return { 41 | run: (context: Context, properties: Properties) => { 42 | if (properties.stream) { 43 | const subscription = properties.stream.subscribe(event => { 44 | // This assumes a Bacon event. 45 | // Should validate that event.value is object 46 | const msg = nodeSet(context.nodeID || -1, event.value()); 47 | player.callCommand(context.id, { 48 | scserver: { 49 | bundle: { 50 | time: 0.03, 51 | packets: [msg], 52 | }, 53 | }, 54 | }); 55 | }); 56 | player.updateContext(context, { subscription }); 57 | } 58 | }, 59 | }; 60 | } 61 | 62 | remove(): Command { 63 | return { 64 | run: (context: Context) => { 65 | if (context.subscription) { 66 | if (_.isFunction(context.subscription)) { 67 | // baconjs style 68 | context.subscription(); 69 | } else { 70 | // Rx style 71 | context.subscription.dispose(); 72 | } 73 | } 74 | }, 75 | }; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /packages/dryads/src/__tests__/SynthEventList.spec.ts: -------------------------------------------------------------------------------- 1 | import Bacon from "baconjs"; 2 | import _ from "lodash"; 3 | 4 | import SynthEventList from "../SynthEventList"; 5 | 6 | describe("SynthEventList", function() { 7 | const events = [{ defName: "blip", args: { freq: 440 }, time: 1.0 }]; 8 | const context = { 9 | group: 0, 10 | out: 0, 11 | epoch: 1460987712857, 12 | id: "id", 13 | }; 14 | 15 | // bad to mock this, it's fragile 16 | const player = { 17 | updateContext: (ctx, update) => _.assign({}, ctx, update), 18 | callCommand: function(/*id, command*/) {}, 19 | }; 20 | 21 | describe("_makeMsgs", function() { 22 | // context group epoch 23 | const sel = new SynthEventList(); 24 | const scheded = sel["_makeMsgs"](events, context); 25 | const first = scheded[0]; 26 | 27 | it("should have events in the packet", function() { 28 | expect(scheded.length).toEqual(1); 29 | }); 30 | 31 | it("should have a msgs array", function() { 32 | expect(_.isArray(first.msgs)).toBe(true); 33 | expect(_.isArray(first.msgs[0])).toBe(true); 34 | }); 35 | }); 36 | 37 | describe("spawn events in supplied list on .add", function() { 38 | const props = { events }; 39 | const sel = new SynthEventList(props); 40 | const commands: any = sel.add(player as any); 41 | it("should contain a function", function() { 42 | expect(typeof commands.scserver.schedLoop).toBe("function"); 43 | }); 44 | it("should schedule 1 event", function() { 45 | const fn = commands.scserver.schedLoop(context, props); 46 | const e = fn(0); 47 | expect(e.event.time).toEqual(1); 48 | }); 49 | }); 50 | 51 | describe("pass in updateStream", function() { 52 | let bus, sel: SynthEventList, dp, updated, called, properties; 53 | beforeEach(function() { 54 | bus = new Bacon.Bus(); 55 | properties = { updateStream: bus }; 56 | sel = new SynthEventList(properties); 57 | 58 | dp = { 59 | updateContext: function(ctx, update) { 60 | updated = update; 61 | }, 62 | callCommand: function(id, command) { 63 | called = command; 64 | }, 65 | }; 66 | }); 67 | 68 | it("should subscribe to stream on .add", function() { 69 | spyOn(player, "updateContext"); 70 | 71 | const commands = sel.add(dp); 72 | if (commands.run) { 73 | commands.run(context, properties); 74 | expect(updated).toBeTruthy(); // {subscription: bacon subscription} 75 | } else { 76 | throw new Error("command does not have 'run' defined"); 77 | } 78 | }); 79 | 80 | it("should get a new event when pushed to bus", function() { 81 | spyOn(player, "callCommand"); 82 | const commands = sel.add(dp); 83 | if (commands.run) { 84 | commands.run(context, properties); 85 | bus.push({ 86 | events: [{ defName: "nuevo-blip", args: { freq: 441 }, time: 2.0 }], 87 | }); 88 | expect(called).toBeDefined(); 89 | expect(called.scserver).toBeTruthy(); // scserver command 90 | } else { 91 | throw new Error("command does not have 'run' defined"); 92 | } 93 | }); 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /packages/dryads/src/__tests__/SynthStream.spec.ts: -------------------------------------------------------------------------------- 1 | import { Bus } from "baconjs"; 2 | import _ from "lodash"; 3 | 4 | import Server from "@supercollider/server"; 5 | import SynthStream, { Event } from "../SynthStream"; 6 | 7 | describe("SynthStream", function() { 8 | const stream = new Bus(); 9 | const properties = { 10 | stream, 11 | }; 12 | const ss = new SynthStream(properties); 13 | 14 | it("should construct", function() { 15 | expect(ss).toBeTruthy(); 16 | }); 17 | 18 | describe("commandsForEvent", function() { 19 | it("has 1 message with no event.id supplied", function() { 20 | const event = { 21 | type: "noteOn", 22 | defName: "sin", 23 | args: {}, 24 | }; 25 | const context = { 26 | id: "ss", 27 | scserver: new Server(), 28 | }; 29 | 30 | // cannot read state of undefined 31 | const cmds = ss.commandsForEvent(event, context, properties); 32 | expect(cmds.scserver.bundle.packets.length).toBe(1); 33 | }); 34 | 35 | it("noteOn with event.key should updateContext and s_new", function() { 36 | const event = { 37 | type: "noteOn", 38 | defName: "sin", 39 | key: 1, 40 | }; 41 | const context = { 42 | id: "ss", 43 | scserver: new Server(), 44 | }; 45 | 46 | const cmds = ss.commandsForEvent(event, context, properties); 47 | expect(cmds.updateContext).toBeTruthy(); 48 | // assumes that Server nextNode returned 1000 49 | expect(cmds.scserver.bundle.packets).toEqual([["/s_new", "sin", 1000, 1, 0, "out", 0]]); 50 | }); 51 | 52 | it("noteOff with event.key should updateContext and s_", function() { 53 | const noteOn = { 54 | type: "noteOn", 55 | defName: "sin", 56 | key: 1, 57 | }; 58 | 59 | const noteOff = { 60 | type: "noteOff", 61 | defName: "sin", 62 | key: 1, 63 | }; 64 | 65 | const context = { 66 | id: "ss", 67 | scserver: new Server(), 68 | }; 69 | 70 | // call noteOn and update the context 71 | const cmds = ss.commandsForEvent(noteOn, context, properties); 72 | _.assign(context, cmds.updateContext); 73 | 74 | // now call noteOff 75 | const cmds2 = ss.commandsForEvent(noteOff, context, properties); 76 | expect(cmds2.updateContext).toBeTruthy(); 77 | // assumes that Server nextNode returned 1000 78 | expect(cmds2.scserver.bundle.packets).toEqual([["/n_free", 1000]]); 79 | }); 80 | 81 | // no defName in event or default should not return anything 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /packages/dryads/src/__tests__/index-spec.ts: -------------------------------------------------------------------------------- 1 | import * as dryads from "../index"; 2 | import SCServer from "../SCServer"; 3 | 4 | describe("SCServer", function() { 5 | it("should load SCServer", function() { 6 | expect(dryads.SCServer).toBeTruthy(); 7 | }); 8 | 9 | it("should set default options of SCServer", function() { 10 | const s = new SCServer(); 11 | expect(s.properties).toEqual({ options: { debug: false } }); 12 | }); 13 | 14 | // it('should pass context.scserver to child Group', function() { 15 | // // mock import {boot} from '@supercollider/server'; 16 | // 17 | // let g = new dryads.Group(); 18 | // let s = new dryads.SCSynth({}, [g]); 19 | // // because scsynth was set during prepare 20 | // // it was not available during initial construction of contexts 21 | // // how to just call this ? 22 | // // s.prepareForAdd() 23 | // // dryadic(s).play() 24 | // }); 25 | }); 26 | 27 | // describe('Group', function() { 28 | // it('should pass .group to child', function() { 29 | // let g = new dryads.Group(); 30 | // let h = new dryads.Group([g]); 31 | // 32 | // // prepareForAdd invokes a lot of things to get that nodeID 33 | // // h.tree(); 34 | // }); 35 | // }); 36 | 37 | // should test that: 38 | // - all prepares are functions 39 | // - all add and remove return commands that are registered in a layer 40 | // - make a validator 41 | -------------------------------------------------------------------------------- /packages/dryads/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Dryadic components for SuperCollider 3 | * 4 | * @package dryads 5 | */ 6 | import scserver from "./middleware/scserver"; 7 | 8 | import SCServer from "./SCServer"; 9 | import SCLang from "./SCLang"; 10 | import Group from "./Group"; 11 | import Synth from "./Synth"; 12 | import AudioBus from "./AudioBus"; 13 | import SCSynthDef from "./SCSynthDef"; 14 | import SynthControl from "./SynthControl"; 15 | import SynthStream from "./SynthStream"; 16 | import SynthEventList from "./SynthEventList"; 17 | 18 | // confusing to swap the names like this 19 | // import { dryadic as makeDryadPlayer } from "dryadic"; 20 | import { Dryad, DryadPlayer, Middleware, dryadic as makeDryadPlayer, Layer } from "dryadic"; 21 | 22 | // re-export all the Dryad classes 23 | export { SCServer, SCLang, Group, Synth, AudioBus, SCSynthDef, SynthControl, SynthStream, SynthEventList }; 24 | 25 | const middleware: Middleware[] = [scserver]; 26 | const classes = [SCServer, SCLang, Group, Synth, AudioBus, SCSynthDef, SynthControl, SynthStream, SynthEventList]; 27 | 28 | /** 29 | * A layer adds Dryad classes and middleware for an external library. eg. for Supercolliderjs 30 | */ 31 | export const layer: Layer = { 32 | middleware, 33 | classes, 34 | }; 35 | 36 | // export const ServerMiddleware = scserver; 37 | 38 | export interface Context { 39 | [name: string]: any; 40 | } 41 | 42 | /** 43 | * Create a DryadPlayer from a Dryad or hyperscript definition. 44 | * 45 | * Automatically includes the supercollider.js layer 46 | * 47 | * usage: 48 | * ```js 49 | * var sc = require('supercolliderjs'); 50 | * var player = sc.dryadic([ 51 | * 'scserver', [ 52 | * ['group', [ 53 | * ['synth', { 54 | * defName: 'sinosc', 55 | * args: { 56 | * freq: 440 57 | * } 58 | * }] 59 | * ] 60 | * ]); 61 | * player.play(); 62 | * // ... 63 | * player.stop(); 64 | * ``` 65 | */ 66 | export function dryadic(rootDryad?: Dryad, moreLayers: Layer[] = [], rootContext: Context = {}): DryadPlayer { 67 | return makeDryadPlayer(rootDryad || new Dryad(), [layer, ...moreLayers], rootContext); 68 | } 69 | 70 | /** 71 | * Play a Dryad or hyperscript document. 72 | * 73 | * usage: 74 | * 75 | * ```js 76 | * var sc = require('supercolliderjs'); 77 | * var player = sc.play([ 78 | * 'scserver', [ 79 | * ['group', [ 80 | * ['synth', { 81 | * defName: 'sinosc', 82 | * args: { 83 | * freq: 440 84 | * } 85 | * }] 86 | * ] 87 | * ]); 88 | * ``` 89 | * @param {Dryad|Array} rootDryad - Dryad object or hyperscript document 90 | * @returns {DryadPlayer} 91 | */ 92 | export function play(rootDryad: Dryad): Promise { 93 | return dryadic(rootDryad).play(); 94 | } 95 | 96 | /** 97 | * Convert hyperscript object to a tree of Dryads. 98 | * 99 | * This lookups each class by lower class 'classname' 100 | * and creates an instance with properties and children. 101 | */ 102 | export function h(hgraph: any): Dryad { 103 | const player = dryadic(); 104 | return player.h(hgraph); 105 | } 106 | -------------------------------------------------------------------------------- /packages/dryads/src/middleware/__tests__/OSCSched.spec.ts: -------------------------------------------------------------------------------- 1 | import _ from "lodash"; 2 | 3 | import OSCSched from "../OSCSched"; 4 | 5 | describe("OSCSched", function() { 6 | let sched: OSCSched, sent, didClearTimeout, didSetTimeout; 7 | const timeoutId = 1; 8 | 9 | beforeEach(() => { 10 | sent = null; 11 | didClearTimeout = false; 12 | didSetTimeout = false; 13 | 14 | sched = new OSCSched( 15 | (...args) => { 16 | sent = args; 17 | }, 18 | 0.05, 19 | (fn /*, delta*/) => { 20 | fn(); 21 | didSetTimeout = true; 22 | return timeoutId; 23 | }, 24 | tid => { 25 | didClearTimeout = tid; 26 | }, 27 | ); 28 | }); 29 | 30 | const schedOne = (time: number): object | void => { 31 | sched.schedLoop((now, memo = { i: 0 }) => { 32 | if (memo.i === 0) { 33 | return { 34 | event: { 35 | time, 36 | msgs: [], 37 | }, 38 | memo: { i: memo.i + 1 }, 39 | }; 40 | } 41 | return undefined; 42 | }, _.now()); 43 | }; 44 | 45 | describe("empty sched", function() { 46 | it("should not have sent nothing", function() { 47 | sched.schedLoop(() => { 48 | return undefined; 49 | }, _.now()); 50 | expect(sent).toBe(null); 51 | }); 52 | 53 | it("should not have set timeout", function() { 54 | sched.schedLoop(() => { 55 | return undefined; 56 | }, _.now()); 57 | expect(didSetTimeout).toBe(false); 58 | }); 59 | }); 60 | 61 | describe("sched at 1", function() { 62 | it("should have set timeout", function() { 63 | schedOne(1); 64 | expect(didSetTimeout).toBe(true); 65 | }); 66 | }); 67 | 68 | describe("sched less than latency", function() { 69 | it("should have called send right away", function() { 70 | schedOne(0.01); 71 | expect(didSetTimeout).toBe(false); 72 | expect(sent).toBeTruthy(); 73 | }); 74 | }); 75 | 76 | describe("sched twice", function() { 77 | it("should have cleared timeout", function() { 78 | schedOne(1); 79 | schedOne(0.5); 80 | 81 | expect(didClearTimeout).toBe(timeoutId); 82 | }); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /packages/dryads/src/middleware/__tests__/scserver.spec.ts: -------------------------------------------------------------------------------- 1 | import scserver from "../scserver"; 2 | 3 | /** 4 | * mock context.scserver.send.bundle 5 | * context.scserver.callAndResponse 6 | */ 7 | describe("scserver", function() { 8 | // TODO use real objects 9 | const context = { 10 | scserver: { 11 | callAndResponse: jest.fn(), 12 | send: { 13 | bundle: jest.fn(), 14 | }, 15 | }, 16 | }; 17 | const properties = {}; 18 | 19 | describe("msg", function() { 20 | it("should send msg", function() { 21 | const cmd = { 22 | scserver: { 23 | msg: ["/s_new", "sin", 1000, 0, 1], 24 | }, 25 | }; 26 | 27 | scserver(cmd, context, properties, context => { 28 | return context; 29 | }); 30 | expect(context.scserver.send.bundle).toHaveBeenCalled(); 31 | }); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /packages/dryads/src/utils/test-utils.ts: -------------------------------------------------------------------------------- 1 | import each from "lodash/each"; 2 | import { dryadic } from "../index"; 3 | import { Dryad, DryadPlayer } from "dryadic"; 4 | 5 | type JSONType = any; 6 | 7 | export function makePlayer(dryad: Dryad): DryadPlayer { 8 | return dryadic(dryad); 9 | } 10 | 11 | export function expectPlayGraphToEqual(dryad: Dryad, expected: JSONType, ignoreFn?: Function): JSONType { 12 | const p = makePlayer(dryad); 13 | if (!p.tree) { 14 | // never 15 | throw new Error("p.tree is undefined"); 16 | } 17 | let g = p.tree.hyperscript(); 18 | 19 | if (ignoreFn) { 20 | g = ignoreFn(g); 21 | if (expected) { 22 | expected = ignoreFn(expected); 23 | } 24 | } 25 | 26 | if (expected) { 27 | expect(g).toEqual(expected); 28 | } 29 | 30 | return g; 31 | } 32 | 33 | /** 34 | * Get a command object to inspect it for testing. 35 | * Calls any function in scserver msg/bundle so the result 36 | * will be the actual OSC message/bundle. 37 | * 38 | * @param player 39 | * @param commandName 'add' 'remove' etc. 40 | * @param {Array} [childAt=[]] Index array to fetch a child command 41 | * for examining the command of a child. 42 | * Especially useful when the Dryad uses 43 | * requireParent or Properties so that the 44 | * command you are testing is not the 45 | * top level. 46 | * @return Command object 47 | */ 48 | export function getCommand(player: DryadPlayer, commandName: string, childAt: number[] = []): object { 49 | const cmd = player._collectCommands(commandName); 50 | // specify which child you want to get the command for with indices: 51 | // null top 52 | // [0] first child 53 | // [0, 0] first child first child 54 | let obj = cmd; 55 | each(childAt, i => { 56 | obj = obj.children[i]; 57 | }); 58 | 59 | return obj; 60 | } 61 | 62 | // export function getOSCMsg(player:DryadPlayer, commandName:string='add', childAt:[number]=[]) 63 | // let cmd = getCommand(player, commandName, childAt); 64 | // // this is actually calling the CommandNode 65 | // console.log('getCommand got', cmd); 66 | // 67 | // if (cmd.commands.scserver) { 68 | // // msg 69 | // 70 | // // value them with context and properties 71 | // let properties = cmd.resolveProperties(); 72 | // let msg = cmd. 73 | // obj.commands.scserver = resolveFuncs(obj.commands.scserver, obj.context, obj.properties); 74 | // } 75 | // 76 | // return obj; 77 | // } 78 | -------------------------------------------------------------------------------- /packages/dryads/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "lib", 6 | "esModuleInterop": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/jest.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "roots": ["/src"], 3 | "transform": { 4 | "^.+\\.ts$": "ts-jest" 5 | }, 6 | "moduleFileExtensions": ["js", "ts", "json"], 7 | "collectCoverage": true 8 | } 9 | -------------------------------------------------------------------------------- /packages/lang/.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !src/**/* 3 | src/**/__tests__/**/* 4 | !lib/**/* 5 | !package.json 6 | !README.md 7 | !CHANGELOG.md 8 | !LICENSE -------------------------------------------------------------------------------- /packages/lang/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@supercollider/lang", 3 | "version": "1.0.1", 4 | "description": "Client library for the SuperCollider language: sclang. This package enables calling SuperCollider code from JavaScript.", 5 | "keywords": [ 6 | "supercollider" 7 | ], 8 | "author": "Chris Sattinger ", 9 | "homepage": "https://crucialfelix.github.io/supercolliderjs/", 10 | "license": "MIT", 11 | "main": "lib/index.js", 12 | "typings": "lib/index.d.ts", 13 | "directories": { 14 | "lib": "lib" 15 | }, 16 | "publishConfig": { 17 | "access": "public" 18 | }, 19 | "dependencies": { 20 | "@supercollider/logger": "^1.0.0", 21 | "@supercollider/server": "^1.0.0", 22 | "commander": "^2.9.0", 23 | "cuid": "^2.1.6", 24 | "js-yaml": "3.13.1", 25 | "lodash": "^4.17.15", 26 | "temp": "~0.9.0", 27 | "tslib": "1.10.0", 28 | "untildify": "^4.0.0" 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "git+https://github.com/crucialfelix/supercolliderjs.git" 33 | }, 34 | "bin": { 35 | "supercollider": "./lib/bin/sclang.js", 36 | "compile-synthdefs": "./lib/bin/compile-synthdefs.js" 37 | }, 38 | "scripts": { 39 | "build": "tsc -b . && npm run copy", 40 | "copy": "cp -R src/supercollider-js lib/", 41 | "post-install": "npm run check-install", 42 | "check-install": "node ./lib/checkInstall.js" 43 | }, 44 | "bugs": { 45 | "url": "https://github.com/crucialfelix/supercolliderjs/issues" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/lang/src/Errors.ts: -------------------------------------------------------------------------------- 1 | // http://www.2ality.com/2011/12/subtyping-builtins.html 2 | function copyOwnFrom(target: object, source: object): object { 3 | Object.getOwnPropertyNames(source).forEach(function(propName: string) { 4 | const prop = Object.getOwnPropertyDescriptor(source, propName); 5 | if (prop !== undefined) { 6 | Object.defineProperty(target, propName, prop); 7 | } 8 | }); 9 | return target; 10 | } 11 | 12 | class ExtendableError { 13 | message = ""; 14 | stack = ""; 15 | 16 | constructor(message: string) { 17 | const superInstance = new Error(message); 18 | copyOwnFrom(this, superInstance); 19 | if (typeof Error.captureStackTrace === "function") { 20 | Error.captureStackTrace(this, this.constructor); 21 | } else { 22 | if (superInstance.stack !== undefined) { 23 | this.stack = superInstance.stack; 24 | } 25 | } 26 | } 27 | } 28 | 29 | /** 30 | * A custom error class that adds a data field for passing structured error data. 31 | */ 32 | export class SCError extends ExtendableError { 33 | data: object; 34 | 35 | constructor(message: string, data: object) { 36 | super(message); 37 | this.data = data; 38 | } 39 | 40 | /** 41 | * Update message and data with additional information. 42 | * Used when passing the error along but when you want 43 | * to add additional contextual debugging information. 44 | */ 45 | annotate(message: string, data: object) { 46 | this.message = message; 47 | this.data = { 48 | ...this.data, 49 | ...data, 50 | }; 51 | } 52 | } 53 | 54 | /** 55 | * SCLangError - syntax errors while interpreting code, interpret code execution errors, and asynchronous errors. 56 | * 57 | * @param type - SyntaxError | Error : Tells which format the error object will be in. 58 | * @param error - The error data object 59 | * An Error will have a stack trace and all of the fields of the sclang error 60 | * that it is generated from. 61 | * SyntaxError is created by parsing the posted output of sclang. 62 | * 63 | * See SuperColliderJS-encodeError 64 | * 65 | * @param data - optional additional debug information supplied from supercollider.js 66 | */ 67 | export class SCLangError extends SCError { 68 | type: string; 69 | error: object; 70 | 71 | constructor(message: string, type: string, error: object, data: any = {}) { 72 | super(message, data); 73 | this.type = type; 74 | this.error = error; 75 | this.data = data; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /packages/lang/src/__tests__/fixtures/sclang_test_conf.yaml: -------------------------------------------------------------------------------- 1 | includePaths: 2 | - /path/include/one 3 | - /path/include/two 4 | excludePaths: 5 | - /path/exclude 6 | postInlineWarnings: false 7 | -------------------------------------------------------------------------------- /packages/lang/src/bin/compile-synthdefs.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* eslint no-console: 0 */ 3 | import path from "path"; 4 | import { boot } from "../index"; 5 | import program from "commander"; 6 | import { promises as fs } from "fs"; 7 | import { JSONObjectType } from "@supercollider/server"; 8 | 9 | /* eslint @typescript-eslint/no-var-requires: 0 */ 10 | const pkg = require(path.join(__dirname, "../../package.json")); 11 | 12 | const help = []; 13 | 14 | program.on("--help", function() { 15 | help.forEach(function(line) { 16 | console.info(" " + line); 17 | }); 18 | }); 19 | 20 | program.version(pkg.version); 21 | program.usage(" "); 22 | program.parse(process.argv); 23 | 24 | if (program.args.length < 2) { 25 | program.help(); 26 | } 27 | 28 | // the shell will expand any globs 29 | const lastArg = program.args[program.args.length - 1]; 30 | const dest = path.resolve(lastArg); 31 | const sources = program.args.map(function(p) { 32 | return path.resolve(p); 33 | }); 34 | // an invalid source glob will not be expanded 35 | // and resolve will just return it as is. 36 | // Should probably warn and skip 37 | // and warn and skip synthdefs/ rather than synthdefs/* 38 | 39 | async function main(): Promise { 40 | const sclang = await boot({ stdin: false, debug: false }); 41 | 42 | function removeAll(): Promise { 43 | return sclang.interpret("SynthDescLib.default.synthDescs.removeAll();"); 44 | } 45 | 46 | function interpretFiles(): Promise { 47 | return Promise.all( 48 | sources.map(src => { 49 | return sclang.executeFile(src).catch(error => { 50 | console.error(`${src} ${error}`, error); 51 | throw error; 52 | }); 53 | }), 54 | ); 55 | } 56 | 57 | // returns SynthDescs as a JSON-ready dict 58 | function writeDefs(): Promise { 59 | return sclang.interpret( 60 | ` 61 | var descs = Dictionary.new; 62 | SynthDescLib.default.synthDescs 63 | .keysValuesDo({ arg defName, synthDesc; 64 | synthDesc.def.writeDefFile("` + 65 | dest + 66 | `"); 67 | descs[defName] = synthDesc.asJSON(); 68 | }); 69 | descs 70 | `, 71 | ); 72 | } 73 | 74 | function writeDescs(descs: JSONObjectType): Promise { 75 | return fs.writeFile(path.join(dest, "synthDefs.json"), JSON.stringify(descs, null, 2)); 76 | } 77 | 78 | await removeAll(); 79 | await interpretFiles(); 80 | const descs = await writeDefs(); 81 | await writeDescs(descs); 82 | } 83 | 84 | main().then( 85 | () => process.exit(0), 86 | (error: Error) => { 87 | console.error(error); 88 | process.exit(1); 89 | }, 90 | ); 91 | -------------------------------------------------------------------------------- /packages/lang/src/bin/sclang.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* eslint no-console: 0 */ 3 | import program from "commander"; 4 | import path from "path"; 5 | 6 | import SCLang, { SCLangArgs } from ".."; 7 | /* eslint @typescript-eslint/no-var-requires: 0 */ 8 | const pkg = require("../../package.json"); 9 | 10 | const help = [ 11 | "Run sclang (the supercollider language interpreter) using the configuration defined in the nearest .supercollider.yaml searching up from the current working directory.", 12 | "", 13 | "By default evaluates STDIN and posts to STDOUT. This is a simple command line repl without multi-line support.", 14 | "", 15 | 'package.json specifies this as an executable with the name of "supercollider" so installing supercollider.js with the global flag will add this to your shell executable path.', 16 | "", 17 | "Examples:", 18 | "", 19 | "supercollider", 20 | "supercollider run-this-file.scd", 21 | "supercollider --config=/path/to/a/custom/config.yaml", 22 | "supercollider --stdin=false --echo=false --sclang=/path/to/sclang", 23 | "", 24 | ]; 25 | 26 | const options: SCLangArgs = {}; 27 | 28 | function truthy(input: string): boolean { 29 | return input + "" !== "false"; 30 | } 31 | 32 | program 33 | .version(pkg.version) 34 | .option("--config ", "Configuration file eg. .supercollider.yaml") 35 | .option("--sclang ", "Path to sclang executable") 36 | .option("--langPort ", "UDP port for the interpreter to listen on") 37 | .option("--stdin ", "Interpret STDIN (default: true)", truthy, true) 38 | .option("--echo ", "Echo STDIN to STDOUT (default: true)", truthy, true) 39 | .option("-v, --verbose", "Post debugging messages (default: false)", truthy, false); 40 | 41 | program.on("--help", function() { 42 | help.forEach(function(line) { 43 | console.info(" " + line); 44 | }); 45 | }); 46 | 47 | program.parse(process.argv); 48 | 49 | ["config", "sclang", "langPort", "stdin", "echo", "verbose"].forEach(function(k) { 50 | if (k in program) { 51 | options[k] = program[k]; 52 | } 53 | }); 54 | 55 | // pass a filename for sclang to execute 56 | if (program.args.length) { 57 | options.executeFile = path.resolve(program.args[0]); 58 | } 59 | 60 | SCLang.boot(options).then( 61 | function(sclang) { 62 | // sclang exited 63 | sclang.on("exit", function() { 64 | console.warn("sclang exited"); 65 | console.info(options); 66 | process.exit(1); 67 | }); 68 | }, 69 | function(error) { 70 | // failure to startup 71 | console.error(error); 72 | console.trace(); 73 | process.exit(1); 74 | }, 75 | ); 76 | -------------------------------------------------------------------------------- /packages/lang/src/checkInstall.ts: -------------------------------------------------------------------------------- 1 | /* eslint no-console: 0 */ 2 | import { resolveOptions } from "./options"; 3 | import fs from "fs"; 4 | 5 | /** 6 | * Check that scsynth executable exists. 7 | * 8 | * This can be called in a postInstall step for a package 9 | * to inform the user if it can or cannot find scsynth. 10 | * 11 | * Posts the options to console. 12 | * Posts errors and any information it can find to help 13 | * the user. 14 | */ 15 | export default function checkInstall(): Promise { 16 | console.log("Checking supercollider.js@lang install..."); 17 | const options = resolveOptions({}); 18 | // console.log("Default options:"); 19 | // console.log(JSON.stringify(options, null, 2)); 20 | 21 | function check(binName: string): Promise { 22 | return new Promise((resolve, reject) => { 23 | const binPath = options[binName]; 24 | fs.stat(binPath, (err) => { 25 | err ? reject(err) : resolve(binPath); 26 | }); 27 | }); 28 | } 29 | 30 | return check("sclang").then( 31 | path => { 32 | console.log(`Executable OK: ${path}`); 33 | return true; 34 | }, 35 | error => { 36 | console.error(`\nExecutable not found: ${error.path}`); 37 | console.error(error); 38 | console.log(` 39 | Install SuperCollider if needed: 40 | http://supercollider.github.io/download 41 | If you already have it installed but it is in a non-standard location then edit .supercollider.yaml and specify the path to sclang there. 42 | Then run this test again: 43 | npm run check-install 44 | ` 45 | ); 46 | return false; 47 | }, 48 | 49 | ); 50 | 51 | } 52 | 53 | if (require.main === module) { 54 | checkInstall().then(() => process.exit(0), () => process.exit(1)); 55 | } 56 | -------------------------------------------------------------------------------- /packages/lang/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module lang 3 | */ 4 | 5 | export { default, boot, SCLangArgs } from "./sclang"; 6 | export { resolveOptions } from "./options"; 7 | export { default as SynthDefCompiler, SynthDefResultType, SynthDefCompileRequest } from "./SynthDefCompiler"; 8 | export { SCLangError } from "./Errors"; 9 | -------------------------------------------------------------------------------- /packages/lang/src/internals/__tests__/fixtures/forward-stdout.txt: -------------------------------------------------------------------------------- 1 | SUPERCOLLIDERJS:citjy45o00002xpxp9dapi2tt:CAPTURE:START 2 | 3 | ERROR: Quarks-install: path does not exist /Users/crucial/wrong 4 | 5 | SUPERCOLLIDERJS:citjy45o00002xpxp9dapi2tt:CAPTURE:END 6 | 7 | SUPERCOLLIDERJS:citjy45o00002xpxp9dapi2tt:START:Result 8 | SUPERCOLLIDERJS:citjy45o00002xpxp9dapi2tt:CHUNK:"nil" 9 | SUPERCOLLIDERJS:citjy45o00002xpxp9dapi2tt:END:Result 10 | -------------------------------------------------------------------------------- /packages/lang/src/internals/__tests__/fixtures/io-class-syntax-error-2.txt: -------------------------------------------------------------------------------- 1 | compiling class library... 2 | NumPrimitives = 710 3 | compiling dir: '/Users/crucial/code/supercollider/build/Install/SuperCollider/SuperCollider.app/Contents/Resources/SCClassLibrary' 4 | ERROR: Parse error 5 | in file '/Common/Quarks/Git.sc' 6 | line 58 char 39: 7 | 8 | match = out.findRegexp("tag: ([^ ,\)]+)"); 9 | 10 | if(match.size > 0, { 11 | ----------------------------------- 12 | opening bracket was a '[', but found a ')' 13 | in file '/Common/Quarks/Git.sc' line 58 char 39 14 | ERROR: Parse error 15 | in file '/Common/Quarks/Git.sc' 16 | line 58 char 40: 17 | 18 | match = out.findRegexp("tag: ([^ ,\)]+)"); 19 | ^ 20 | if(match.size > 0, { 21 | ----------------------------------- 22 | opening bracket was a '(', but found a ']' 23 | in file '/Common/Quarks/Git.sc' line 58 char 40 24 | Expected class name. got token: ']' 280 25 | in file '/Common/Quarks/Git.sc' 26 | line 58 char 40: 27 | 28 | match = out.findRegexp("tag: ([^ ,\)]+)"); 29 | ^ 30 | if(match.size > 0, { 31 | ----------------------------------- 32 | compiling dir: '/Users/crucial/Library/Application Support/SuperCollider/downloaded-quarks/crucial-library' 33 | pass 1 done 34 | -------------------------------------------------------------------------------- /packages/lang/src/internals/__tests__/fixtures/io-class-syntax-error.txt: -------------------------------------------------------------------------------- 1 | compiling class library... 2 | NumPrimitives = 710 3 | compiling dir: '/Users/crucial/code/supercollider/build/Install/SuperCollider/SuperCollider.app/Contents/Resources/SCClassLibrary' 4 | Expected ':' or {. got token: 'var' 268 5 | in file '/Common/Quarks/Git.sc' 6 | line 3 char 4: 7 | 8 | var <>localPath, >url, tag, sha, remoteLatest, tags; 9 | ^^^ 10 | 11 | ----------------------------------- 12 | compiling dir: '/Users/crucial/Library/Application Support/SuperCollider/downloaded-quarks/crucial-library' 13 | pass 1 done 14 | -------------------------------------------------------------------------------- /packages/lang/src/internals/__tests__/fixtures/io-compile-success.txt: -------------------------------------------------------------------------------- 1 | init_OSC 2 | empty 3 | compiling class library... 4 | NumPrimitives = 710 5 | compiling dir: '/Users/crucial/code/supercollider/build/Install/SuperCollider/SuperCollider.app/Contents/Resources/SCClassLibrary' 6 | compiling dir: '/Users/crucial/github/atom-supercollider/node_modules/supercolliderjs/lib/supercollider-js' 7 | compiling dir: '/Users/crucial/Library/Application Support/SuperCollider/downloaded-quarks/Steno' 8 | pass 1 done 9 | numentries = 819694 / 11593660 = 0.071 10 | 5185 method selectors, 2236 classes 11 | method table size 12622504 bytes, big table size 92749280 12 | Number of Symbols 11816 13 | Byte Code Size 359053 14 | compiled 322 files in 0.43 seconds 15 | 16 | Info: 8 methods are currently overwritten by extensions. To see which, execute: 17 | MethodOverride.printAll 18 | 19 | compile done 20 | Class tree inited in 0.01 seconds 21 | Cleaning up temp synthdefs... 22 | 23 | 24 | *** Welcome to SuperCollider 3.7alpha1. *** For help press Cmd-D. 25 | SCDoc: Indexing help-files... 26 | SCDoc: Indexed 1347 documents in 1.3 seconds 27 | -------------------------------------------------------------------------------- /packages/lang/src/internals/__tests__/fixtures/io-duplicate-class.txt: -------------------------------------------------------------------------------- 1 | empty 2 | compiling class library... 3 | NumPrimitives = 710 4 | compiling dir: '/Users/crucial/code/supercollider/build/Install/SuperCollider/SuperCollider.app/Contents/Resources/SCClassLibrary' 5 | compiling dir: '/Users/crucial/Library/Application Support/SuperCollider/downloaded-quarks/crucial-library' 6 | ERROR: duplicate Class found: 'Crucial' 7 | /Users/crucial/Library/Application Support/SuperCollider/downloaded-quarks/crucial-library/Crucial.sc 8 | /Users/crucial/Library/Application Support/SuperCollider/downloaded-quarks/crucial-library/ServerTools/BusPool.sc 9 | 10 | pass 1 done 11 | ERROR: There is a discrepancy. 12 | numClassDeps 1323 gNumClasses 2644 13 | -------------------------------------------------------------------------------- /packages/lang/src/internals/__tests__/fixtures/io-extension-for-non-existent-class.txt: -------------------------------------------------------------------------------- 1 | Class extension for nonexistent class 'Document' 2 | In file:'/deprecated/3.7/deprecated-3.7.sc' 3 | numentries = 1028128 / 14762880 = 0.07 4 | 5592 method selectors, 2640 classes 5 | method table size 16451808 bytes, big table size 118103040 6 | Number of Symbols 12979 7 | Byte Code Size 404479 8 | compiled 397 files in 0.34 seconds 9 | -------------------------------------------------------------------------------- /packages/lang/src/internals/__tests__/fixtures/io-na.txt: -------------------------------------------------------------------------------- 1 | // just some text with no meaning 2 | 1 + 1 3 | 4 | x 5 | -------------------------------------------------------------------------------- /packages/lang/src/internals/__tests__/fixtures/io-programmatic-compile.txt: -------------------------------------------------------------------------------- 1 | *** Welcome to SuperCollider 3.7alpha1. *** For help press Cmd-D. 2 | SCDoc: Indexing help-files... 3 | SCDoc: Indexed 1347 documents in 1.3 seconds 4 | empty 5 | compiling class library... 6 | NumPrimitives = 710 7 | -------------------------------------------------------------------------------- /packages/lang/src/internals/__tests__/fixtures/routine-postln.txt: -------------------------------------------------------------------------------- 1 | SUPERCOLLIDERJS:c1c4f120-4346-11e6-9df0-edccf25b99df:CAPTURE:START 2 | 3 | 4 | SUPERCOLLIDERJS:c1c4f120-4346-11e6-9df0-edccf25b99df:CAPTURE:END 5 | 6 | SUPERCOLLIDERJS:c1c4f120-4346-11e6-9df0-edccf25b99df:START:Result 7 | SUPERCOLLIDERJS:c1c4f120-4346-11e6-9df0-edccf25b99df:CHUNK:"a Routine" 8 | SUPERCOLLIDERJS:c1c4f120-4346-11e6-9df0-edccf25b99df:END:Result 9 | SUPERCOLLIDERJS.interpreted 10 | -> 11 | hi 12 | -------------------------------------------------------------------------------- /packages/lang/src/internals/__tests__/fixtures/script.scd: -------------------------------------------------------------------------------- 1 | "hallo welt".postln; 2 | 3 | Library.at(\supercolliderjs, \sclang_conf).debug("conf path"); 4 | -------------------------------------------------------------------------------- /packages/lang/src/internals/__tests__/fixtures/trig-not-defined.txt: -------------------------------------------------------------------------------- 1 | ERROR: Variable 'trig' not defined. 2 | in file 'selected text' 3 | line 9 char 47: 4 | 5 | Klank.ar(z, Decay.ar(Impulsar.ar(trig), decay, WhiteNoise.ar(noise)), freqScale, 0.0, timeScale 6 | -------------------------------------------------------------------------------- /packages/lang/src/supercollider-js/SystemOverwrites/plusError.sc: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | These overide the normal error reporting methods 4 | by posting a JSON dump of the error call stack. 5 | 6 | That dump is parsed from stdout by sclang which converts 7 | it into a structured error response. 8 | 9 | This requires the Library functions that interpreter.scd would have loaded, 10 | so that file should have been run. 11 | **/ 12 | 13 | + Object { 14 | reportError { 15 | error(this.asString); 16 | this.dumpBackTrace; 17 | } 18 | } 19 | 20 | 21 | + Exception { 22 | reportError { 23 | if (SuperColliderJS.isInitialized.not, { 24 | "Class Library failed to initialize".error; 25 | this.errorString.postln; 26 | if(protectedBacktrace.notNil, { this.postProtectedBacktrace }); 27 | this.dumpBackTrace; 28 | this.halt; 29 | }); 30 | { 31 | var error = SuperColliderJS.encodeError(this, true, nil); 32 | SuperColliderJS.return("0", "Error", error); 33 | }.try({ 34 | this.dump; 35 | }); 36 | } 37 | } 38 | 39 | + MethodError { 40 | reportError { 41 | ^super.reportError 42 | } 43 | } 44 | 45 | + PrimitiveFailedError { 46 | reportError { 47 | ^super.reportError 48 | } 49 | } 50 | 51 | + SubclassResponsibilityError { 52 | reportError { 53 | ^super.reportError 54 | } 55 | } 56 | 57 | + ShouldNotImplementError { 58 | reportError { 59 | ^super.reportError 60 | } 61 | } 62 | 63 | + DoesNotUnderstandError { 64 | reportError { 65 | ^super.reportError 66 | } 67 | } 68 | 69 | + OutOfContextReturnError { 70 | reportError { 71 | ^super.reportError 72 | } 73 | } 74 | 75 | + ImmutableError { 76 | reportError { 77 | ^super.reportError 78 | } 79 | } 80 | 81 | + DeprecatedError { 82 | reportError { 83 | ^super.reportError 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /packages/lang/src/supercollider-js/SystemOverwrites/plusQuarks.sc: -------------------------------------------------------------------------------- 1 | 2 | + Quarks { 3 | 4 | /** 5 | * because supercollider-js passes in a dynamically generated config file 6 | * store to that must happen to the original config file 7 | * and to the current (dynamically generated) one 8 | */ 9 | *clear { 10 | this.installed.do({ |quark| 11 | LanguageConfig.removeIncludePath(quark.localPath); 12 | }); 13 | LanguageConfig.store(this.configPath); 14 | LanguageConfig.store(LanguageConfig.currentPath); 15 | this.clearCache; 16 | } 17 | *link { |path| 18 | path = path.withoutTrailingSlash; 19 | if(LanguageConfig.includePaths.includesEqual(path).not, { 20 | path.debug("Adding path"); 21 | LanguageConfig.addIncludePath(path); 22 | this.prSaveConfig(); 23 | ^true 24 | }); 25 | ^false 26 | } 27 | *unlink { |path| 28 | path = path.withoutTrailingSlash; 29 | if(LanguageConfig.includePaths.includesEqual(path), { 30 | path.debug("Removing path"); 31 | LanguageConfig.removeIncludePath(path); 32 | this.prSaveConfig(); 33 | ^true 34 | }); 35 | ^false 36 | } 37 | 38 | *configPath { 39 | // The permanent original sclang_config path is saved to here 40 | // on startup. 41 | ^SuperColliderJS.sclangConf 42 | } 43 | 44 | *prSaveConfig { 45 | var scjs = LanguageConfig.includePaths.detect({ arg p; 46 | p.contains("lib/supercollider-js"); 47 | }); 48 | 49 | if (scjs.notNil, { 50 | LanguageConfig.removeIncludePath(scjs); 51 | }); 52 | 53 | // save to the permanent config file 54 | LanguageConfig.store(this.configPath); 55 | 56 | // save to the temp runtime config file 57 | if (scjs.notNil, { 58 | LanguageConfig.addIncludePath(scjs); 59 | }); 60 | LanguageConfig.store(LanguageConfig.currentPath); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/lang/src/supercollider-js/SystemOverwrites/plusSynthDesc.sc: -------------------------------------------------------------------------------- 1 | 2 | + SynthDesc { 3 | asJSON { 4 | var sourceCode; 5 | { 6 | sourceCode = def.asCompileString; 7 | }.protect; 8 | 9 | ^( 10 | name: name, 11 | controlNames: controlNames, 12 | controls: controls.collect(_.asJSON), 13 | inputs: inputs.collect(_.asJSON), 14 | output: outputs.collect(_.asJSON), 15 | hasGate: hasGate, 16 | hasArrayArgs: hasArrayArgs, 17 | hasVariants: hasVariants, 18 | canFreeSynth: canFreeSynth, 19 | sourceCode: sourceCode 20 | ) 21 | } 22 | } 23 | 24 | + ControlName { 25 | asJSON { 26 | var spec = Spec.specs.at(name); 27 | ^( 28 | name: name, 29 | index: index, 30 | rate: rate, 31 | defaultValue: defaultValue, 32 | argNum: argNum, 33 | lag: lag, 34 | spec: if(spec.isNil, nil, { spec.asJSON }) 35 | ) 36 | } 37 | } 38 | 39 | + IODesc { 40 | asJSON { 41 | ^( 42 | rate: rate, 43 | numberOfChannels: numberOfChannels, 44 | startingChannel: startingChannel, 45 | type: type 46 | ) 47 | } 48 | } 49 | 50 | + Spec { 51 | asJSON { 52 | ^( 53 | // rate: this.rate, 54 | class: this.class.name 55 | ) 56 | } 57 | } 58 | 59 | + ControlSpec { 60 | asJSON { 61 | ^( 62 | // rate: this.rate, 63 | class: this.class.name, 64 | minval: this.minval, 65 | maxval: this.maxval, 66 | warp: this.warp.asSpecifier, 67 | step: this.step, 68 | default: this.default, 69 | units: this.units 70 | ) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /packages/lang/src/supercollider-js/supercollider-js.quark: -------------------------------------------------------------------------------- 1 | ( 2 | 'summary': "supercollider.js adds this quark to support two-way communication between supercollider and the node.js host. It also overides methods of the Error classes so that they print errors as JSON data.", 3 | 'version': "0.11.1" 4 | ) 5 | -------------------------------------------------------------------------------- /packages/lang/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "lib", 6 | "esModuleInterop": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/logger/.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !src/**/* 3 | src/**/__tests__/**/* 4 | !lib/**/* 5 | !package.json 6 | !README.md 7 | !CHANGELOG.md 8 | !LICENSE -------------------------------------------------------------------------------- /packages/logger/LICENSE: -------------------------------------------------------------------------------- 1 | MIT -------------------------------------------------------------------------------- /packages/logger/README.md: -------------------------------------------------------------------------------- 1 | # @supercollider/logger 2 | [![NPM downloads][npm-downloads-image]][npm-url] [![MIT License][license-image]][license-url] 3 | 4 | Console logging utility for supercollider.js for debugging with color support and special formatting for OSC messages. 5 | 6 | This is used internally by other `@supercollider` packages. 7 | 8 | ## Usage 9 | 10 | ```js 11 | const Logger = require("@supercollider/logger").default; 12 | 13 | const debug = true; 14 | const echo = true; 15 | 16 | const log = new Logger(debug, echo); 17 | // Log an error. 18 | log.err("Oh no!"); 19 | // Log debugging information but only if this.debug is true 20 | log.dbug({ log: "log", some: 1, context: 2, for: "The problem" }); 21 | // Log messages that were sent to stdin or sclang. 22 | log.stdin("1 + 1"); 23 | // Log messages that were received from stdout of sclang/scsynth. 24 | log.stdout("2"); 25 | // Log messages that were emitted from stderr of sclang/scsynth. 26 | log.stderr("ERROR: ..."); 27 | // Log OSC messages sent to scsynth. 28 | log.sendosc({ address: "/ping" }); 29 | // Log OSC messages received from scsynth. 30 | log.rcvosc({ value: "pong" }); 31 | 32 | ``` 33 | source 34 | 35 | 36 | Documentation 37 | ------------- 38 | 39 | [Documentation](https://crucialfelix.github.io/supercolliderjs/#/packages/logger/api) 40 | 41 | Compatibility 42 | ------------- 43 | 44 | Works on Node 10+ 45 | 46 | Source code is written in TypeScript and is usable in JavaScript [es2018](https://2ality.com/2017/02/ecmascript-2018.html) or [TypeScript](https://www.typescriptlang.org/docs/home.html) projects. 47 | 48 | Contribute 49 | ---------- 50 | 51 | - Issue Tracker: https://github.com/crucialfelix/supercolliderjs/issues 52 | - Source Code: https://github.com/crucialfelix/supercolliderjs 53 | 54 | License 55 | ------- 56 | 57 | MIT license 58 | 59 | [license-image]: http://img.shields.io/badge/license-MIT-blue.svg?style=flat 60 | [license-url]: LICENSE 61 | 62 | [npm-url]: https://npmjs.org/package/@supercollider/logger 63 | [npm-version-image]: http://img.shields.io/npm/v/@supercollider/logger.svg?style=flat 64 | [npm-downloads-image]: http://img.shields.io/npm/dm/@supercollider/logger.svg?style=flat 65 | 66 | [travis-url]: http://travis-ci.org/crucialfelix/supercolliderjs 67 | [travis-image]: https://travis-ci.org/crucialfelix/supercolliderjs.svg?branch=master 68 | -------------------------------------------------------------------------------- /packages/logger/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@supercollider/logger", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-styles": { 8 | "version": "3.2.1", 9 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 10 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 11 | "requires": { 12 | "color-convert": "^1.9.0" 13 | } 14 | }, 15 | "chalk": { 16 | "version": "2.4.2", 17 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 18 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 19 | "requires": { 20 | "ansi-styles": "^3.2.1", 21 | "escape-string-regexp": "^1.0.5", 22 | "supports-color": "^5.3.0" 23 | } 24 | }, 25 | "color-convert": { 26 | "version": "1.9.3", 27 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 28 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 29 | "requires": { 30 | "color-name": "1.1.3" 31 | } 32 | }, 33 | "color-name": { 34 | "version": "1.1.3", 35 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 36 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 37 | }, 38 | "escape-string-regexp": { 39 | "version": "1.0.5", 40 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 41 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 42 | }, 43 | "has-flag": { 44 | "version": "3.0.0", 45 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 46 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 47 | }, 48 | "supports-color": { 49 | "version": "5.5.0", 50 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 51 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 52 | "requires": { 53 | "has-flag": "^3.0.0" 54 | } 55 | }, 56 | "tslib": { 57 | "version": "1.10.0", 58 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", 59 | "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/logger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@supercollider/logger", 3 | "version": "1.0.0", 4 | "description": "Console logging utility for supercollider.js for debugging with color support and special formatting for OSC messages.", 5 | "keywords": [ 6 | "supercollider" 7 | ], 8 | "author": "Chris Sattinger ", 9 | "homepage": "https://crucialfelix.github.io/supercolliderjs/", 10 | "license": "MIT", 11 | "main": "lib/index.js", 12 | "typings": "lib/index.d.ts", 13 | "directories": { 14 | "lib": "./lib", 15 | "src": "./src" 16 | }, 17 | "publishConfig": { 18 | "access": "public" 19 | }, 20 | "dependencies": { 21 | "chalk": "^2.4.2", 22 | "tslib": "1.10.0" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git+https://github.com/crucialfelix/supercolliderjs.git" 27 | }, 28 | "bugs": { 29 | "url": "https://github.com/crucialfelix/supercolliderjs/issues" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/logger/src/__tests__/logger-test.ts: -------------------------------------------------------------------------------- 1 | // jest.autoMockOff(); 2 | 3 | import Logger from "../"; 4 | 5 | describe("Logger", function() { 6 | const l = new Logger(true, false); 7 | 8 | it("dbug", function() { 9 | l.dbug("testing dbug"); 10 | }); 11 | 12 | it("should handle JSON type object", function() { 13 | l.dbug({ testing: { object: ["dbug", 3] } }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/logger/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "lib", 6 | "esModuleInterop": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/osc/.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !src/**/* 3 | src/**/__tests__/**/* 4 | !lib/**/* 5 | !package.json 6 | !README.md 7 | !CHANGELOG.md 8 | !LICENSE -------------------------------------------------------------------------------- /packages/osc/README.md: -------------------------------------------------------------------------------- 1 | # @supercollider/osc 2 | [![NPM downloads][npm-downloads-image]][npm-url] [![MIT License][license-image]][license-url] 3 | 4 | Open Sound Control packet, bundle and buffer utilities for supercollider 5 | 6 | - Packs OSC messages and bundles into a Node `Buffer` for sending 7 | - Unpacks received OSC messages and bundles from a Node `Buffer` 8 | 9 | It does not concern itself with network connections. 10 | 11 | The OSC support is limited to the types and features of SuperCollider server. 12 | This means it does not support inline arrays `[f]` 13 | 14 | This is used internally by `@supercollider/server` 15 | 16 | ## Usage 17 | 18 | ```js 19 | const osc = require('@supercollider/osc'); 20 | 21 | // TODO: DEMONSTRATE API 22 | ``` 23 | 24 | Documentation 25 | ------------- 26 | 27 | [Documentation](https://crucialfelix.github.io/supercolliderjs/#/packages/osc/api) 28 | 29 | Compatibility 30 | ------------- 31 | 32 | Works on Node 10+ 33 | 34 | Source code is written in TypeScript and is usable in JavaScript [es2018](https://2ality.com/2017/02/ecmascript-2018.html) or [TypeScript](https://www.typescriptlang.org/docs/home.html) projects. 35 | 36 | Contribute 37 | ---------- 38 | 39 | - Issue Tracker: https://github.com/crucialfelix/supercolliderjs/issues 40 | - Source Code: https://github.com/crucialfelix/supercolliderjs 41 | 42 | License 43 | ------- 44 | 45 | MIT license 46 | 47 | [license-image]: http://img.shields.io/badge/license-MIT-blue.svg?style=flat 48 | [license-url]: LICENSE 49 | 50 | [npm-url]: https://npmjs.org/package/@supercollider/osc 51 | [npm-version-image]: http://img.shields.io/npm/v/@supercollider/osc.svg?style=flat 52 | [npm-downloads-image]: http://img.shields.io/npm/dm/@supercollider/osc.svg?style=flat 53 | 54 | [travis-url]: http://travis-ci.org/crucialfelix/supercolliderjs 55 | [travis-image]: https://travis-ci.org/crucialfelix/supercolliderjs.svg?branch=master 56 | -------------------------------------------------------------------------------- /packages/osc/__tests__/timetags-test.ts: -------------------------------------------------------------------------------- 1 | const { asNTPTimeTag, deltaTimeTag } = require("../lib/timetags"); 2 | 3 | const SECONDS_FROM_1900_to_1970 = 2208988800; 4 | 5 | describe("@supercollider/osc", () => { 6 | describe("asNTPTimeTag", () => { 7 | it("NTP epoch", () => { 8 | const d = new Date(Date.UTC(1970, 0)); 9 | const tt = asNTPTimeTag(d); 10 | expect(tt).toEqual([SECONDS_FROM_1900_to_1970, 0]); 11 | }); 12 | 13 | it("should convert a number", () => { 14 | const d = Date.UTC(1970, 0) / 1000; 15 | const tt = asNTPTimeTag(d); 16 | expect(tt).toEqual([SECONDS_FROM_1900_to_1970, 0]); 17 | }); 18 | it("should convert a Date", () => { 19 | const d = new Date(Date.UTC(1970, 0)); 20 | const tt = asNTPTimeTag(d); 21 | expect(tt).toEqual([SECONDS_FROM_1900_to_1970, 0]); 22 | }); 23 | 24 | it("should pass a time tag through", () => { 25 | const d = [1, 1]; 26 | const tt = asNTPTimeTag(d); 27 | expect(tt).toEqual(d); 28 | }); 29 | 30 | it("should make time tag from undefined (now)", () => { 31 | const tt = asNTPTimeTag(); 32 | expect(tt.length).toEqual(2); 33 | }); 34 | 35 | it("should make time tag from null (now)", () => { 36 | const tt = asNTPTimeTag(null); 37 | expect(tt.length).toEqual(2); 38 | }); 39 | }); 40 | 41 | describe("deltaTimeTag", () => { 42 | it("should make an NTP timetag array from a number", function() { 43 | const ntp = deltaTimeTag(100); 44 | expect(Array.isArray(ntp)).toBe(true); 45 | expect(ntp.length).toBe(2); 46 | }); 47 | 48 | it("should make an NTP timetag array from a date", function() { 49 | const ntp = deltaTimeTag(new Date()); 50 | expect(Array.isArray(ntp)).toBe(true); 51 | expect(ntp.length).toBe(2); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /packages/osc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@supercollider/osc", 3 | "version": "1.0.0", 4 | "description": "Open Sound Control packet, bundle and buffer utilities for supercollider", 5 | "keywords": [ 6 | "OSC", 7 | "supercollider.js", 8 | "supercollider" 9 | ], 10 | "author": "Chris Sattinger ", 11 | "homepage": "https://github.com/crucialfelix/supercolliderjs#readme", 12 | "license": "MIT", 13 | "main": "lib/index.js", 14 | "typings": "lib/index.d.ts", 15 | "directories": { 16 | "lib": "lib", 17 | "test": "__tests__" 18 | }, 19 | "publishConfig": { 20 | "access": "public" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/crucialfelix/supercolliderjs.git" 25 | }, 26 | "scripts": { 27 | "test": "echo \"Error: run tests from root\" && exit 1" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/crucialfelix/supercolliderjs/issues" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/osc/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./packing"; 2 | export * from "./types"; 3 | export * from "./timetags"; 4 | -------------------------------------------------------------------------------- /packages/osc/src/timetags.ts: -------------------------------------------------------------------------------- 1 | import { NTPTimeTag, OSCTimeType } from "./types"; 2 | 3 | const UNIX_EPOCH = 0; 4 | const NTP_EPOCH = Date.UTC(1900, 0) / 1000; 5 | const SECONDS_FROM_1900_to_1970 = UNIX_EPOCH - NTP_EPOCH; 6 | // const _SECONDS_FROM_1900_to_1970 = 2208988800; 7 | 8 | const MAX_UINT32 = Math.pow(2, 32); 9 | 10 | /** 11 | * Convert a JavaScript Date to a NTP timetag array `[secondsSince1970, fractionalSeconds]`. 12 | * 13 | * `toBuffer` already accepts Dates for timetags so you might not need this function. 14 | * If you need to schedule bundles with sub-millisecond accuracy then you 15 | * could use this to help assemble the NTP array. 16 | */ 17 | export function dateToTimetag(date: Date): NTPTimeTag { 18 | return timestampToTimetag(date.getTime() / 1000); 19 | } 20 | 21 | /** 22 | * Unix timestamp to NTP Time Tag 23 | * @param secs 24 | */ 25 | const timestampToTimetag = (secs: number): NTPTimeTag => { 26 | const totalSecs = SECONDS_FROM_1900_to_1970 + secs; 27 | const wholeSecs = Math.trunc(totalSecs); 28 | const fracSeconds = totalSecs - wholeSecs; 29 | const tt = makeTimetag(wholeSecs, fracSeconds); 30 | return tt; 31 | }; 32 | 33 | function makeTimetag(wholeSeconds: number, fracSeconds: number): NTPTimeTag { 34 | return [wholeSeconds, Math.round(MAX_UINT32 * fracSeconds)]; 35 | } 36 | 37 | export function timetagToDate(timetag: NTPTimeTag): Date { 38 | const seconds = timetag[0] - UNIX_EPOCH; 39 | 40 | const fracs = ntpToFractionalSeconds(timetag[1]); 41 | const date = new Date(); 42 | date.setTime(seconds * 1000 + fracs * 1000); 43 | 44 | const dd = new Date(); 45 | dd.setUTCFullYear(date.getUTCFullYear()); 46 | dd.setUTCMonth(date.getUTCMonth()); 47 | dd.setUTCDate(date.getUTCDate()); 48 | dd.setUTCHours(date.getUTCHours()); 49 | dd.setUTCMinutes(date.getUTCMinutes()); 50 | dd.setUTCSeconds(date.getUTCSeconds()); 51 | dd.setUTCMilliseconds(fracs * 1000); 52 | return dd; 53 | } 54 | 55 | /** 56 | * Make NTP timetag array relative to the current time. 57 | * 58 | * @param seconds - seconds relative to now 59 | * @param now - JavaScript timestamp in milliseconds 60 | * @return `[ntpSecs, ntpFracs]` 61 | */ 62 | export function deltaTimeTag(seconds: number, now?: number | Date): NTPTimeTag { 63 | let milliseconds: number; 64 | if (now) { 65 | milliseconds = typeof now === "number" ? now : now.getTime(); 66 | } else { 67 | milliseconds = new Date().getTime(); 68 | } 69 | 70 | const delta = milliseconds / 1000 + seconds; 71 | return timestampToTimetag(delta); 72 | } 73 | 74 | function ntpToFractionalSeconds(fracSeconds: number): number { 75 | return fracSeconds / MAX_UINT32; 76 | } 77 | 78 | export function asNTPTimeTag(time: OSCTimeType): NTPTimeTag { 79 | let tt: NTPTimeTag; 80 | if (typeof time === "number") { 81 | tt = timestampToTimetag(time); 82 | } else if (Array.isArray(time) && time.length === 2) { 83 | tt = time; 84 | } else if (time instanceof Date) { 85 | tt = dateToTimetag(time); 86 | } else if (!time) { 87 | tt = dateToTimetag(new Date()); 88 | } else { 89 | throw new Error("Invalid timetag: " + time); 90 | } 91 | return tt; 92 | } 93 | -------------------------------------------------------------------------------- /packages/osc/src/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * OSC 3 | */ 4 | export type OscType = string | number | Buffer | CompletionMsg | boolean | null; 5 | /** 6 | * Some scsynth messages accept another OSC message as the last argument, 7 | * and will execute that message after the first message is completed. 8 | */ 9 | type CompletionMsgItem = string | number | Buffer | null; 10 | export type CompletionMsg = [string, ...CompletionMsgItem[]]; 11 | 12 | /** 13 | * An array of values of OscType 14 | */ 15 | export type OscValues = OscType[]; 16 | 17 | /** 18 | * NTP / OSC time tag 19 | * [secondsSince1900, fractionalSeconds] 20 | */ 21 | export type NTPTimeTag = [number, number]; 22 | 23 | /** 24 | * OSCTimeType 25 | * 26 | * Many scheduling functions take various ways of specifying time, 27 | * which are then converted to an NTPTimeTag. 28 | * 29 | * - null: now, immediately 30 | * - number: unix timestamp in seconds 31 | * - Array: `[secondsSince1900Jan1, fractionalSeconds]` Most precise 32 | * - Date 33 | */ 34 | export type OSCTimeType = NTPTimeTag | null | number | Date; 35 | 36 | /** 37 | * An OSC message is [address, val, val, val...] 38 | */ 39 | export type MsgType = [string, ...OscValues]; 40 | 41 | /** 42 | * An OSC bundle has a timetag and contains messages or bundles 43 | * that should be executed at that scheduled time. 44 | */ 45 | export interface OSCBundle { 46 | timetag: OSCTimeType; 47 | elements: (OSCMessage | OSCBundle)[]; 48 | oscType?: string; 49 | } 50 | 51 | export interface OSCMessage { 52 | address: string; 53 | args: OscType[]; 54 | oscType?: string; 55 | } 56 | 57 | export type BundleOrMessage = OSCBundle | OSCMessage; 58 | -------------------------------------------------------------------------------- /packages/osc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "lib", 6 | "esModuleInterop": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/scapi/.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !src/**/* 3 | src/**/__tests__/**/* 4 | !lib/**/* 5 | !package.json 6 | !README.md 7 | !CHANGELOG.md 8 | !LICENSE -------------------------------------------------------------------------------- /packages/scapi/README.md: -------------------------------------------------------------------------------- 1 | # @supercollider/scapi 2 | [![NPM downloads][npm-downloads-image]][npm-url] [![MIT License][license-image]][license-url] 3 | 4 | Node to SuperCollider communication using OSC and the API quark. 5 | 6 | This works together with the 'API' quark to implement a simple two-way communication protocol for node <-> SuperCollider. 7 | 8 | It connects with an sclang process using UDP OSC and then sends OSC messages to '/API/call' 9 | 10 | The SuperCollider quark is here: 11 | https://github.com/supercollider-quarks/API 12 | 13 | And this package is the nodejs side. 14 | 15 | Sent messages return a promise, the responses are received here from sclang and the promises are resolved (or rejected if there was an error). 16 | 17 | This requires writing named handlers in SuperCollider and registering them with the API. From the node side, you make a call using that name and pass it some args and get back your response. 18 | 19 | This was an older solution. Probably just using `@supercollider/lang` is easier now. 20 | 21 | Note: this is not included in the [`supercolliderjs`](https://npmjs.org/package/supercolliderjs) package. 22 | 23 | ## Install 24 | 25 | ```shell 26 | npm install @supercollider/scapi 27 | ``` 28 | 29 | Start SuperCollider 30 | Install the API quark ( > 2.0 ) 31 | 32 | ## Usage 33 | 34 | Start SuperCollider and activate the OSC responders: 35 | 36 | ```supercollider 37 | API.mountDuplexOSC 38 | ``` 39 | 40 | Documentation 41 | ------------- 42 | 43 | [Documentation](https://crucialfelix.github.io/supercolliderjs/#/packages/scapi/api) 44 | 45 | Compatibility 46 | ------------- 47 | 48 | Works on Node 10+ 49 | 50 | Source code is written in TypeScript and is usable in JavaScript [es2018](https://2ality.com/2017/02/ecmascript-2018.html) or [TypeScript](https://www.typescriptlang.org/docs/home.html) projects. 51 | 52 | Contribute 53 | ---------- 54 | 55 | - Issue Tracker: https://github.com/crucialfelix/supercolliderjs/issues 56 | - Source Code: https://github.com/crucialfelix/supercolliderjs 57 | 58 | License 59 | ------- 60 | 61 | MIT license 62 | 63 | [license-image]: http://img.shields.io/badge/license-MIT-blue.svg?style=flat 64 | [license-url]: LICENSE 65 | 66 | [npm-url]: https://npmjs.org/package/@supercollider/scapi 67 | [npm-version-image]: http://img.shields.io/npm/v/@supercollider/scapi.svg?style=flat 68 | [npm-downloads-image]: http://img.shields.io/npm/dm/@supercollider/scapi.svg?style=flat 69 | 70 | [travis-url]: http://travis-ci.org/crucialfelix/supercolliderjs 71 | [travis-image]: https://travis-ci.org/crucialfelix/supercolliderjs.svg?branch=master 72 | -------------------------------------------------------------------------------- /packages/scapi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@supercollider/scapi", 3 | "version": "1.0.0", 4 | "description": "Node to SuperCollider communication using OSC and the API quark.", 5 | "keywords": [ 6 | "supercollider" 7 | ], 8 | "author": "Chris Sattinger ", 9 | "homepage": "https://crucialfelix.github.io/supercolliderjs/", 10 | "license": "MIT", 11 | "main": "lib/index.js", 12 | "typings": "lib/index.d.ts", 13 | "directories": { 14 | "lib": "lib" 15 | }, 16 | "publishConfig": { 17 | "access": "public" 18 | }, 19 | "dependencies": { 20 | "@supercollider/logger": "^1.0.0", 21 | "@supercollider/osc": "^1.0.0", 22 | "cuid": "^2.1.6", 23 | "lodash": "^4.17.15", 24 | "tslib": "1.10.0" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git+https://github.com/crucialfelix/supercolliderjs.git" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/crucialfelix/supercolliderjs/issues" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/scapi/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "lib", 6 | "esModuleInterop": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/server-plus/.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !src/**/* 3 | src/**/__tests__/**/* 4 | !lib/**/* 5 | !package.json 6 | !README.md 7 | !CHANGELOG.md 8 | !LICENSE -------------------------------------------------------------------------------- /packages/server-plus/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@supercollider/server-plus", 3 | "version": "1.0.1", 4 | "description": "Server class with added methods for Group, Synth and SynthDef creation", 5 | "keywords": [ 6 | "supercollider", 7 | "supercollider.js" 8 | ], 9 | "author": "Chris Sattinger ", 10 | "homepage": "https://github.com/crucialfelix/supercolliderjs#readme", 11 | "license": "MIT", 12 | "main": "lib/ServerPlus.js", 13 | "directories": { 14 | "lib": "lib" 15 | }, 16 | "files": [ 17 | "lib" 18 | ], 19 | "dependencies": { 20 | "@supercollider/lang": "^1.0.1", 21 | "@supercollider/server": "^1.0.0", 22 | "lodash": "^4.17.15", 23 | "tslib": "1.10.0" 24 | }, 25 | "publishConfig": { 26 | "access": "public" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/crucialfelix/supercolliderjs.git" 31 | }, 32 | "bugs": { 33 | "url": "https://github.com/crucialfelix/supercolliderjs/issues" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/server-plus/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "lib", 6 | "esModuleInterop": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/server/.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !src/**/* 3 | src/**/__tests__/**/* 4 | !lib/**/* 5 | !package.json 6 | !README.md 7 | !CHANGELOG.md 8 | !LICENSE -------------------------------------------------------------------------------- /packages/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@supercollider/server", 3 | "version": "1.0.0", 4 | "description": "Client library for the SuperCollider scsynth audio engine", 5 | "keywords": [ 6 | "supercollider" 7 | ], 8 | "author": "Chris Sattinger ", 9 | "homepage": "https://crucialfelix.github.io/supercolliderjs/", 10 | "license": "MIT", 11 | "main": "lib/index.js", 12 | "typings": "lib/index.d.ts", 13 | "directories": { 14 | "lib": "lib" 15 | }, 16 | "publishConfig": { 17 | "access": "public" 18 | }, 19 | "scripts": { 20 | "post-install": "npm run check-install", 21 | "check-install": "node ./lib/checkInstall.js" 22 | }, 23 | "bin": { 24 | "supercollider-server": "./lib/bin/scsynth.js" 25 | }, 26 | "dependencies": { 27 | "@supercollider/logger": "^1.0.0", 28 | "@supercollider/osc": "^1.0.0", 29 | "commander": "^2.9.0", 30 | "immutable": "^3.8.1", 31 | "js-yaml": "3.13.1", 32 | "lodash": "^4.17.15", 33 | "rx": "^4.1.0", 34 | "tslib": "1.10.0", 35 | "untildify": "^4.0.0" 36 | }, 37 | "repository": { 38 | "type": "git", 39 | "url": "git+https://github.com/crucialfelix/supercolliderjs.git" 40 | }, 41 | "bugs": { 42 | "url": "https://github.com/crucialfelix/supercolliderjs/issues" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/server/src/__tests__/ServerState-test.ts: -------------------------------------------------------------------------------- 1 | import Server from '../server'; 2 | import ServerState from '../ServerState'; 3 | 4 | describe('ServerState', function() { 5 | let state; 6 | beforeEach(function() { 7 | const server = new Server(); 8 | state = new ServerState(server); 9 | }); 10 | 11 | describe('default constructor', function() { 12 | it('should exist', function() { 13 | expect(state).toBeDefined(); 14 | // console.log('initial state', state.store.state.toJS()); 15 | }); 16 | }); 17 | 18 | describe('keys', function() { 19 | it('should build a valid key list', function() { 20 | const keys = state._keys(['test']); 21 | expect(keys.length).toEqual(3); 22 | }); 23 | }); 24 | 25 | describe('resetState', function() { 26 | it('should make a new state', function() { 27 | const old = state.store.state; 28 | state.resetState(); 29 | const newState = state.store.state; 30 | // console.log(state.store.state); 31 | expect(old !== newState).toBe(true); 32 | }); 33 | }); 34 | 35 | describe('nextNodeID', function() { 36 | it('should increment', function() { 37 | const na = state.nextNodeID(); 38 | const nb = state.nextNodeID(); 39 | expect(nb).toEqual(na + 1); 40 | }); 41 | }); 42 | 43 | describe('allocAudioBus', function() { 44 | it('should alloc a bus', function() { 45 | const b = state.allocAudioBus(2); 46 | // 2 in 2 out, first bus is 4 47 | expect(b).toEqual(4); 48 | }); 49 | }); 50 | 51 | describe('allocControlBus', function() { 52 | it('should alloc a bus', function() { 53 | const b = state.allocControlBus(1); 54 | expect(b).toEqual(0); 55 | }); 56 | }); 57 | 58 | describe('allocBufferID', function() { 59 | it('should alloc a buffer', function() { 60 | const b = state.allocBufferID(2); 61 | expect(b).toEqual(0); 62 | }); 63 | }); 64 | 65 | describe('freeAudioBus', function() { 66 | it('should free a bus', function() { 67 | const b = state.allocAudioBus(2); 68 | state.freeAudioBus(b, 2); 69 | }); 70 | }); 71 | 72 | describe('allocControlBus', function() { 73 | it('should free a bus', function() { 74 | const b = state.allocControlBus(1); 75 | state.freeAudioBus(b, 1); 76 | }); 77 | }); 78 | 79 | describe('freeBuffer', function() { 80 | it('should free a buffer', function() { 81 | const b = state.allocBufferID(1); 82 | state.freeBuffer(b, 1); 83 | }); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /packages/server/src/__tests__/node-watcher-test.ts: -------------------------------------------------------------------------------- 1 | import _ from "lodash"; 2 | 3 | import Server from "../server"; 4 | import * as nw from "../node-watcher"; 5 | 6 | describe("node-watcher", function() { 7 | const nodeID = 1000; 8 | const id = "0.1.2"; 9 | const id2 = "0.1.3"; 10 | 11 | function expectEqualState(s, object) { 12 | const cs = s.state.getIn(["NODE_WATCHER"]).toJS(); 13 | const is = _.isEqual(cs, object); 14 | if (!is) { 15 | // as long as callbacks have the same keys list 16 | // the functions were copied and aren't the same ones now 17 | // which is annoying in itself 18 | if (!_.isEqual(cs["ON_NODE_GO"], object["ON_NODE_GO"])) { 19 | expect(cs).toEqual(object); 20 | } 21 | if (!_.isEqual(_.keys(cs["CALLBACKS"]), _.keys(object["CALLBACKS"]))) { 22 | expect(cs).toEqual(object); 23 | } 24 | } 25 | } 26 | 27 | describe("onNodeGo", function() { 28 | it("should register a callback", function() { 29 | function fn() {} 30 | const s = new Server(); 31 | nw.onNodeGo(s, id, nodeID, fn); 32 | 33 | expectEqualState(s, { 34 | ON_NODE_GO: { 35 | "1000": ["0.1.2:1000"], 36 | }, 37 | CALLBACKS: { 38 | "0.1.2:1000": fn, 39 | }, 40 | }); 41 | }); 42 | 43 | it("should add another callback on same node", function() { 44 | function fn() {} 45 | const s = new Server(); 46 | nw.onNodeGo(s, id, nodeID, fn); 47 | nw.onNodeGo(s, id2, nodeID, fn); 48 | 49 | expectEqualState(s, { 50 | ON_NODE_GO: { 51 | "1000": ["0.1.2:1000", "0.1.3:1000"], 52 | }, 53 | CALLBACKS: { 54 | "0.1.2:1000": fn, 55 | "0.1.3:1000": fn, 56 | }, 57 | }); 58 | }); 59 | 60 | it("should dispose of callback", function() { 61 | function fn() {} 62 | const s = new Server(); 63 | const dispose = nw.onNodeGo(s, id, nodeID, fn); 64 | expect(dispose).toBeTruthy(); 65 | dispose(); 66 | 67 | expectEqualState(s, { 68 | ON_NODE_GO: { 69 | "1000": [], 70 | }, 71 | CALLBACKS: {}, 72 | }); 73 | }); 74 | }); 75 | 76 | describe("watchNodeNotifications", function() { 77 | it("should fire a onNodeGo handler when server receives", function() { 78 | const s = new Server(); 79 | 80 | return new Promise(resolve => { 81 | nw.onNodeGo(s, id, nodeID, nid => { 82 | expect(nid).toBe(nodeID); 83 | resolve(); 84 | }); 85 | 86 | s.receive.onNext(["/n_go", nodeID, 0, -1, 3, 0]); 87 | }); 88 | }); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /packages/server/src/__tests__/resolve-options-test.ts: -------------------------------------------------------------------------------- 1 | import _ from "lodash"; 2 | import { resolveOptions } from "../options"; 3 | 4 | describe("resolveOptions", function() { 5 | // `pit` is `it` for promises 6 | it("should get default options with no undefines", function() { 7 | const opts = resolveOptions(); 8 | _.each(opts, function(val) { 9 | expect(val).toBeDefined(); 10 | }); 11 | }); 12 | 13 | // it("should reject if configPath does not exist", function() { 14 | // var badPath = "/---~no-way-do-you-have-this-path-on-your-computer~---/bad/path.yaml"; 15 | // const opts = resolveOptions(badPath, {}); 16 | // .then( 17 | // () => { 18 | // throw new Error("should not have resolved"); 19 | // }, 20 | // function(err) { 21 | // expect(err.message).toBeTruthy(); 22 | // expect(err.message).toContain(badPath); 23 | // }, 24 | // ); 25 | // }); 26 | 27 | it("should remove undefined values from supplied options", function() { 28 | const opts = resolveOptions({ scsynth: undefined }); 29 | 30 | _.each(opts, function(val) { 31 | expect(val).toBeDefined(); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /packages/server/src/bin/scsynth.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* eslint no-console: 0 */ 3 | import program from "commander"; 4 | import { join } from "path"; 5 | 6 | import Server, { ServerArgs } from ".."; 7 | 8 | const help = ` 9 | Run scsynth (the supercollider synthesis server) using the configuration defined in the nearest .supercollider.yaml searching up from the current working directory. 10 | 11 | Examples: 12 | 13 | supercollider-server 14 | supercollider-server --scsynth=/path/to/scsynth 15 | `; 16 | 17 | /* eslint @typescript-eslint/no-var-requires: 0 */ 18 | const pkg = require(join(__dirname, "../../package.json")); 19 | 20 | function truthy(input: string | undefined | boolean): boolean { 21 | return input + "" !== "false"; 22 | } 23 | 24 | program 25 | .version(pkg.version) 26 | .option("--scsynth ", "Path to scsynth executable") 27 | .option("--serverPort ", "UDP port for the server to listen on") 28 | .option("-v, --verbose", "Post debugging messages (default: false)", truthy, false); 29 | 30 | program.on("--help", () => help.split("\n").forEach(console.info)); 31 | 32 | program.parse(process.argv); 33 | 34 | const options: ServerArgs = {}; 35 | 36 | ["config", "scsynth", "serverPort", "verbose"].forEach(function(k) { 37 | if (k in program) { 38 | options[k] = program[k]; 39 | } 40 | }); 41 | 42 | const s = new Server(options); 43 | 44 | s.boot(); 45 | 46 | s.on("exit", function() { 47 | console.warn("scsynth exited"); 48 | console.info(options); 49 | process.exit(1); 50 | }); 51 | -------------------------------------------------------------------------------- /packages/server/src/checkInstall.ts: -------------------------------------------------------------------------------- 1 | /* eslint no-console: 0 */ 2 | import { resolveOptions } from "./options"; 3 | import fs from "fs"; 4 | 5 | /** 6 | * Check that scsynth executable exists. 7 | * 8 | * This can be called in a postInstall step for a package 9 | * to inform the user if it can or cannot find scsynth. 10 | * 11 | * Posts the options to console. 12 | * Posts errors and any information it can find to help 13 | * the user. 14 | */ 15 | export default function checkInstall(): Promise { 16 | console.log("Checking supercollider.js@server install..."); 17 | const options = resolveOptions({}); 18 | // console.log("Default options:"); 19 | // console.log(JSON.stringify(options, null, 2)); 20 | 21 | function check(binName: string): Promise { 22 | return new Promise((resolve, reject) => { 23 | const binPath = options[binName]; 24 | fs.stat(binPath, (err) => { 25 | err ? reject(err) : resolve(binPath); 26 | }); 27 | }); 28 | } 29 | 30 | return check("scsynth").then( 31 | path => { 32 | console.log(`Executable OK: ${path}`); 33 | return true; 34 | }, 35 | error => { 36 | console.error(`\nExecutable not found: ${error.path}`); 37 | console.error(error); 38 | console.log(` 39 | Install SuperCollider if needed: 40 | http://supercollider.github.io/download 41 | If you already have it installed but it is in a non-standard location then edit .supercollider.yaml and specify the path to scsynth there. 42 | Then run this test again: 43 | npm run check-install 44 | ` 45 | ); 46 | return false; 47 | }, 48 | 49 | ); 50 | 51 | } 52 | 53 | if (require.main === module) { 54 | checkInstall().then(() => process.exit(0), () => process.exit(1)); 55 | } 56 | -------------------------------------------------------------------------------- /packages/server/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module server 3 | */ 4 | 5 | export { default, boot } from "./server"; 6 | export { ServerArgs, resolveOptions } from "./options"; 7 | 8 | /** 9 | * OSC message construction 10 | */ 11 | import * as msg from "./osc/msg"; 12 | export { msg }; 13 | 14 | /** 15 | * Spec and number mapping functions 16 | */ 17 | import * as mapping from "./mapping"; 18 | export { mapping }; 19 | 20 | /** 21 | * Node allocators 22 | */ 23 | export { default as ServerState } from "./ServerState"; 24 | export * from "./osc-types"; 25 | export * from "./node-watcher"; 26 | export { deltaTimeTag } from "@supercollider/osc"; 27 | -------------------------------------------------------------------------------- /packages/server/src/internals/SendOSC.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "events"; 2 | import { Observable } from "rx"; 3 | import { makeBundle, makeMessage } from "../osc/utils"; 4 | import { deltaTimeTag, OSCTimeType, MsgType } from "@supercollider/osc"; 5 | import { Disposable } from "rx"; 6 | 7 | /** 8 | * Owned by the Server, this is an object that you call .msg or .bundle on 9 | * to send OSC. 10 | * 11 | * The Server subscribes to this and does the actual sending. 12 | * You may also subscribe to this for debugging, logging or entertainment purposes. 13 | */ 14 | export default class SendOSC extends EventEmitter { 15 | msg(message: MsgType) { 16 | this.emit("msg", makeMessage(message)); 17 | } 18 | 19 | /** 20 | * bundle 21 | * 22 | * Note that in SuperCollider language a number is interpreted 23 | * as relative seconds from 'now'; here is is interpreted as a 24 | * unix timestamp. See deltaTimeTag 25 | * 26 | * @param {null|Number|Array|Date} time 27 | * - null: now, immediately 28 | * - Number: if less than 10000 then it is interpreted 29 | * as number of seconds from now. 30 | * It it is larger then it is interpreted as a unix timestamp in seconds 31 | * - Array: `[secondsSince1900Jan1, fractionalSeconds]` 32 | * - Date 33 | * @param {Array} packets - osc messages as `[address, arg1, ...argN]` 34 | * or sub bundles as `[{timetag: , packets: }, ...]` 35 | */ 36 | bundle(time: OSCTimeType, packets: MsgType[]) { 37 | if (typeof time === "number" && time < 10000) { 38 | time = deltaTimeTag(time); 39 | } 40 | this.emit("bundle", makeBundle(time, packets)); 41 | } 42 | 43 | /** 44 | * Make NTP timetag array relative to the current time. 45 | * 46 | * @example: 47 | * 48 | * server.send.bundle(server.send.deltaTimeTag(1.0), [ ... msgs ]); 49 | * 50 | * @param {Number} delta 51 | * @param {Date} now - optional, default new Date 52 | */ 53 | deltaTimeTag(delta: number, now?: Date): [number, number] { 54 | // was just [number] 55 | return deltaTimeTag(delta, now); 56 | } 57 | 58 | /** 59 | * Subscribe to monitor OSC messages and bundles sent. 60 | * 61 | * Events are: `{type: msg|bundle: payload: Array}` 62 | * 63 | * @returns {Rx.Disposable} - `thing.dispose();` to unsubscribe 64 | */ 65 | subscribe( 66 | onNext: (value: { type: string; payload: any }) => void, 67 | onError?: (value: { type: string; payload: any }) => void, 68 | onComplete?: () => void, 69 | ): Disposable { 70 | const msgs = Observable.fromEvent(this, "msg", msg => { 71 | return { 72 | type: "msg", 73 | payload: msg, 74 | }; 75 | }); 76 | const bundles = Observable.fromEvent(this, "bundle", bundle => { 77 | return { 78 | type: "bundle", 79 | payload: bundle, 80 | }; 81 | }); 82 | const combo = msgs.merge(bundles); 83 | return combo.subscribe(onNext, onError, onComplete); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /packages/server/src/internals/Store.ts: -------------------------------------------------------------------------------- 1 | import { Map } from "immutable"; 2 | 3 | export type State = Map; 4 | 5 | /** 6 | * A Store that holds a state tree. This is used by ServerState as 7 | * its immutable memory storage. 8 | * 9 | * Holds an Immutable.Map 10 | * and offers functions to mutate sub-states 11 | * in that tree, and stores the new state. 12 | * 13 | * https://facebook.github.io/immutable-js/docs/#/Map 14 | */ 15 | export default class Store { 16 | state: State; 17 | 18 | constructor() { 19 | this.state = Map(); 20 | } 21 | 22 | getIn(keys: string[], notSetValue: any): any { 23 | return this.state.getIn(keys, notSetValue); 24 | } 25 | 26 | /** 27 | * Fetch the object at keys 28 | * pass it to the function which mutates it and returns new sub state. 29 | */ 30 | mutateState(keys: string[], fn: (value: any) => any) { 31 | this.state = this.state.updateIn(keys, Map(), fn); 32 | } 33 | 34 | /** 35 | * Fetch one part of the state, 36 | * mutate it with the callback, 37 | * which returns result, subState. 38 | * Save the subState back into state and return the result. 39 | * 40 | * @returns {any} result 41 | */ 42 | mutateStateAndReturn(keys: string[], fn: Function): any { 43 | const [result, subState] = fn(this.state.getIn(keys, Map())); 44 | this.state = this.state.setIn(keys, subState); 45 | return result; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/server/src/internals/side-effects.ts: -------------------------------------------------------------------------------- 1 | import { boot as _bootServer } from "../server"; 2 | // import { boot as _bootLang } from '../../lang/sclang'; 3 | 4 | /** 5 | * @private 6 | */ 7 | export function bootServer(options, store) { 8 | return _bootServer(options, store); 9 | } 10 | /** 11 | * @private 12 | */ 13 | // export function bootLang(options) { 14 | // return _bootLang(options); 15 | // } 16 | /** 17 | * @private 18 | */ 19 | export function sendMsg(context, msg) { 20 | return context.server.send.msg(msg); 21 | } 22 | /** 23 | * @private 24 | */ 25 | export function nextNodeID(context) { 26 | return context.server.state.nextNodeID(); 27 | } 28 | /** 29 | * @private 30 | */ 31 | export function interpret(context, code) { 32 | return context.lang.interpret(code, undefined, false, false, true); 33 | } 34 | -------------------------------------------------------------------------------- /packages/server/src/osc-types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * JSON 3 | */ 4 | export type JSONType = string | number | boolean | Date | null | JSONObjectType; 5 | 6 | export interface JSONObjectType { 7 | [x: string]: JSONType | JSONType[]; 8 | } 9 | 10 | // Exporting to avoid changing the API 11 | export { OscType, CompletionMsg, OscValues, OSCTimeType, MsgType } from "@supercollider/osc"; 12 | -------------------------------------------------------------------------------- /packages/server/src/osc/__tests__/msg.ts: -------------------------------------------------------------------------------- 1 | import * as msg from "../msg"; 2 | import _ from "lodash"; 3 | // import { CallAndResponse } from "../../osc-types"; 4 | 5 | describe("msg", function() { 6 | it("should evaluate each one without error", function() { 7 | _.each(msg, function(value) { 8 | // if (typeof value === 'function') { 9 | // var result = value(); 10 | // if (_.isArray(result)) { 11 | // expect(_.isArray(result)).toBeTruthy(); 12 | // } else if (_.isObject(result)) { 13 | // let r = result as CallAndResponse; 14 | // expect(r.call).toBeDefined(); 15 | // expect(r.response).toBeDefined(); 16 | // } else { 17 | // fail("wrong type:" + result); 18 | // } 19 | // } 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/server/src/osc/__tests__/utils.ts: -------------------------------------------------------------------------------- 1 | import _ from "lodash"; 2 | 3 | import * as utils from "../utils"; 4 | 5 | describe("parseMessage", function() { 6 | it("should parse a message", function() { 7 | const msg = { 8 | address: "/n_go", 9 | // this may be how osc min responds 10 | // in which case MsgType is wrong 11 | args: [1000, 0, -1, 3, 0], 12 | oscType: "message", 13 | }; 14 | const p = utils.parseMessage(msg); 15 | expect(_.isArray(p)).toBe(true); 16 | expect(p).toEqual(["/n_go", 1000, 0, -1, 3, 0]); 17 | expect(p.length).toBe(6); 18 | }); 19 | }); 20 | 21 | describe("makeMessage", function() { 22 | it("should format a message", function() { 23 | const msg = utils.makeMessage(["/n_go", 1000, 0, -1, 3, 0]); 24 | expect(msg).toBeTruthy(); 25 | }); 26 | }); 27 | 28 | describe("makeBundle", function() { 29 | it("should format a bundle", function() { 30 | const b = utils.makeBundle(0, [["/n_go", 1000, 0, -1, 3, 0]]); 31 | expect(b).toBeTruthy(); 32 | }); 33 | }); 34 | 35 | describe("asPacket", function() { 36 | const address = "/n_go"; 37 | const args = [1000, 0, -1, 3, 0]; 38 | 39 | it("should convert one array message to object style", function() { 40 | const obj = utils.asPacket([address, ...args]); 41 | expect(_.isObject(obj)).toBe(true); 42 | expect(obj.address).toBe(address); 43 | expect(obj.args).toEqual(args); 44 | }); 45 | 46 | // it("should convert object to bundle object", function() { 47 | // var bobj = { 48 | // timetag: 0, 49 | // packets: [[address, ...args]], 50 | // }; 51 | // var obj = utils.asPacket(bobj); 52 | // expect(_.isObject(obj)).toBe(true); 53 | // expect(obj.timetag).toBe(0); 54 | // expect(obj.elements.length).toBe(1); 55 | // expect(obj.elements[0].address).toBe(address); 56 | // expect(obj.elements[0].args).toEqual(args); 57 | // }); 58 | 59 | // no, that would be a message 60 | // 61 | // it('should map an array to packets', function() { 62 | // var packets = [ 63 | // [address, args] 64 | // ]; 65 | // var objs = _.map(packets, utils.asPacket); 66 | // expect(objs.length).toBe(1); 67 | // expect(_.isObject(objs[0])).toBe(true); 68 | // expect(objs[0].address).toBe(address); 69 | // console.log(objs); 70 | // expect(objs[0].args).toEqual(args); 71 | // }); 72 | }); 73 | -------------------------------------------------------------------------------- /packages/server/src/osc/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * OSC utilities 3 | */ 4 | import { MsgType, OSCBundle, OSCMessage, OSCTimeType } from "@supercollider/osc"; 5 | 6 | /** 7 | * Converts a message received from scsynth: 8 | * 9 | * ```js 10 | * {address: '/n_go', 11 | * args: 12 | * [ 1000 , 13 | * 0 , 14 | * -1 , 15 | * 3 , 16 | * 0 ], 17 | * oscType: 'message' } 18 | * ``` 19 | * 20 | * to a flat array format: 21 | * 22 | * ```js 23 | * ['/n_go', 1000, 0, -1, 3, 0] 24 | * ``` 25 | */ 26 | export function parseMessage(msg: OSCMessage): MsgType { 27 | return [msg.address, ...msg.args]; 28 | } 29 | 30 | /** 31 | * Convert a simple Msg array to OSCMessage object for osc packMessage 32 | */ 33 | export function makeMessage(msg: MsgType): OSCMessage { 34 | return { 35 | oscType: "message", 36 | // first arg of MsgType is always a string 37 | address: msg[0], 38 | args: msg.slice(1), 39 | }; 40 | } 41 | 42 | /** 43 | * Build an OSCBundle for osc packBundle 44 | * 45 | * @param {null|Number|Array|Date} time - 46 | * - null: now, immediately 47 | * - number: unix timestamp in seconds 48 | * - Array: `[secondsSince1900Jan1, fractionalSeconds]` 49 | * - Date 50 | * @param {Array} packets - osc messages as `[address, arg1, ...argN]` 51 | * or sub bundles as `[{timetag: , packets: }, ...]` 52 | */ 53 | export function makeBundle(time: OSCTimeType, packets: MsgType[]): OSCBundle { 54 | return { 55 | oscType: "bundle", 56 | timetag: time, 57 | elements: packets.map(asPacket), 58 | }; 59 | } 60 | 61 | /** 62 | * Format children of a bundle as either OSCMessage or OSCBundle objects. 63 | * TODO: not supporting nested bundles yet 64 | */ 65 | export function asPacket(thing: MsgType): OSCMessage { 66 | // not supporting nested bundles right now 67 | // if ("timetag" in thing) { 68 | // return makeBundle(thing.timetag, thing.packets); 69 | // } 70 | return makeMessage(thing); 71 | } 72 | 73 | export { dateToTimetag, deltaTimeTag } from "@supercollider/osc"; 74 | -------------------------------------------------------------------------------- /packages/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "lib", 6 | "esModuleInterop": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/supercolliderjs/.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !src/**/* 3 | src/**/__tests__/**/* 4 | !lib/**/* 5 | !package.json 6 | !README.md 7 | !CHANGELOG.md 8 | !LICENSE -------------------------------------------------------------------------------- /packages/supercolliderjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supercolliderjs", 3 | "version": "1.0.1", 4 | "description": "JavaScript library for the SuperCollider music language and synthesis server", 5 | "keywords": [ 6 | "supercollider" 7 | ], 8 | "author": "Chris Sattinger ", 9 | "homepage": "https://crucialfelix.github.io/supercolliderjs/", 10 | "license": "MIT", 11 | "main": "lib/index.js", 12 | "typings": "lib/index.d.ts", 13 | "directories": { 14 | "lib": "lib" 15 | }, 16 | "bin": { 17 | "export-supercollider": "./lib/bin/export-supercollider.js" 18 | }, 19 | "publishConfig": { 20 | "access": "public" 21 | }, 22 | "dependencies": { 23 | "@supercollider/dryads": "^1.0.1", 24 | "@supercollider/lang": "^1.0.1", 25 | "@supercollider/logger": "^1.0.0", 26 | "@supercollider/server": "^1.0.0", 27 | "@supercollider/server-plus": "^1.0.1", 28 | "commander": "^2.9.0", 29 | "ncp": "^2.0.0", 30 | "tslib": "1.10.0" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/crucialfelix/supercolliderjs.git" 35 | }, 36 | "bugs": { 37 | "url": "https://github.com/crucialfelix/supercolliderjs/issues" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/supercolliderjs/src/bin/export-supercollider.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* eslint no-console: 0 */ 3 | import { resolveOptions } from "@supercollider/server"; 4 | import program from "commander"; 5 | import { promises as fs } from "fs"; 6 | import { ncp } from "ncp"; 7 | import path from "path"; 8 | 9 | const help = ["Export a copy of scsynth for use as a standalone."]; 10 | 11 | // TODO sclang and all classfiles 12 | 13 | /* eslint @typescript-eslint/no-var-requires: 0 */ 14 | const pkg = require(path.join(__dirname, "../../package.json")); 15 | 16 | async function makeDir(dest: string): Promise { 17 | try { 18 | await fs.mkdir(dest); 19 | } catch (error) { 20 | if (error.code !== "EEXIST") { 21 | throw error; 22 | } 23 | } 24 | } 25 | 26 | async function makeExecScript(source: string, dest: string): Promise { 27 | const execScript = [ 28 | "#!/bin/bash", 29 | 'DIR="${BASH_SOURCE%/*}";', 30 | 'if [[ -z "$@" ]]; then', 31 | ' ARGS="-u 57110";', 32 | "else", 33 | ' ARGS="$@";', 34 | "fi", 35 | 'if [[ -z "$SC_SYNTHDEF_PATH" ]]; then', 36 | ' export SC_SYNTHDEF_PATH="$DIR/synthdefs/"', 37 | "fi", 38 | 'export SC_PLUGIN_PATH="$DIR/plugins/";', 39 | 'exec "$DIR/bin/scsynth" $ARGS;', 40 | ]; 41 | 42 | return fs.writeFile(dest, execScript.join("\n"), { mode: "0755" }); 43 | } 44 | 45 | const ncpp = (src: string, dest: string): Promise => { 46 | return new Promise((resolve, reject) => { 47 | ncp(src, dest, (err?: Error) => { 48 | err ? reject(err) : resolve(); 49 | }); 50 | }); 51 | }; 52 | 53 | async function exportScsynth(dest: string): Promise { 54 | const options = resolveOptions(); 55 | await makeDir(dest); 56 | await makeDir(path.join(dest, "bin")); 57 | 58 | const destScsynth = path.join(dest, "bin", "scsynth"); 59 | // The typing is wrong: resolveOptions does return this 60 | if (!options.scsynth) { 61 | throw new Error("No path set for scsynth executable"); 62 | } 63 | const srcPlugins = path.join(path.dirname(options.scsynth), "..", "Resources", "plugins"); 64 | 65 | await ncpp(options.scsynth, destScsynth); 66 | await ncpp(srcPlugins, path.join(dest, "plugins")); 67 | await makeDir(path.join(dest, "synthdefs")); 68 | await makeExecScript(destScsynth, path.join(dest, "scsynth")); 69 | } 70 | 71 | program 72 | .command("scsynth ") 73 | .description("Copy scsynth and plugins to the destination directory") 74 | .action(function(dest) { 75 | exportScsynth(path.resolve(dest)).then(function() { 76 | console.log("Finished"); 77 | }); 78 | }); 79 | 80 | program.on("--help", function() { 81 | help.forEach(function(line) { 82 | console.info(" " + line); 83 | }); 84 | }); 85 | 86 | program.version(pkg.version).parse(process.argv); 87 | -------------------------------------------------------------------------------- /packages/supercolliderjs/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module supercolliderjs 3 | */ 4 | import * as langLib from "@supercollider/lang"; 5 | import * as server from "@supercollider/server"; 6 | import ServerPlus, { boot } from "@supercollider/server-plus"; 7 | import * as dryads from "@supercollider/dryads"; 8 | import { SCLangError } from "@supercollider/lang"; 9 | import { mapping as map, msg, resolveOptions } from "@supercollider/server"; 10 | 11 | const lang = langLib; 12 | 13 | module.exports = { 14 | server: { 15 | ...server, 16 | boot, 17 | server: ServerPlus, 18 | }, 19 | dryads, 20 | lang, 21 | map, 22 | msg, 23 | // why is this exported separately? 24 | SCLangError, 25 | // @deprecated 26 | resolveOptions, 27 | }; 28 | -------------------------------------------------------------------------------- /packages/supercolliderjs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "lib", 6 | "esModuleInterop": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2018", "dom"], 4 | "module": "commonjs", 5 | "target": "es2018", 6 | "allowSyntheticDefaultImports": true, 7 | "baseUrl": "src", 8 | "composite": true, 9 | "declaration": true, 10 | "declarationMap": true, 11 | "esModuleInterop": true, 12 | "experimentalDecorators": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "importHelpers": true, 15 | "moduleResolution": "node", 16 | "noImplicitAny": false, 17 | "noImplicitReturns": true, 18 | "noImplicitThis": true, 19 | "noUnusedLocals": true, 20 | "resolveJsonModule": true, 21 | "sourceMap": true, 22 | "strict": true, 23 | "strictNullChecks": true 24 | }, 25 | "references": [ 26 | { 27 | "path": "logger" 28 | }, 29 | { 30 | "path": "server" 31 | }, 32 | { 33 | "path": "lang" 34 | }, 35 | { 36 | "path": "dryads" 37 | }, 38 | { 39 | "path": "supercolliderjs" 40 | }, 41 | { 42 | "path": "scapi" 43 | } 44 | ], 45 | "exclude": ["**/__tests__", "**/node_modules", "**/lib"] 46 | } 47 | -------------------------------------------------------------------------------- /tasks/export-index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Typedocs does not detect a module's exports. 3 | * I should see if I can fix that. 4 | * 5 | * This script loads the index file for a package 6 | * and converts the exports to a nested tree of names 7 | * and saves this as index.json 8 | */ 9 | const fs = require("fs"); 10 | const self = require(process.cwd() + "/"); 11 | 12 | const asNames = (object, name) => { 13 | if (!typeof object === "object") { 14 | return name; 15 | } 16 | 17 | if (Array.isArray(object)) { 18 | return object.map((x, i) => asNames(x, String(i))); 19 | } 20 | 21 | if (isPlainObject(object)) { 22 | const mapped = {}; 23 | for (const key in object) { 24 | if (object.hasOwnProperty(key)) { 25 | const element = object[key]; 26 | const name = asNames(element, key); 27 | // const emptyObject = name && (name.constructor === Object || Object.entries(name).length === 0); 28 | if (name) { 29 | mapped[key] = name; 30 | } 31 | } 32 | } 33 | return mapped; 34 | } 35 | // if has no name then it is an instrinsic 36 | return object.name; 37 | }; 38 | 39 | const result = asNames(self); 40 | const json = JSON.stringify(result, null, 2); 41 | fs.writeFileSync("./index.json", json); 42 | 43 | // https://github.com/jonschlinkert/is-plain-object/blob/master/index.js 44 | function isObjectObject(o) { 45 | return isObject(o) === true && Object.prototype.toString.call(o) === "[object Object]"; 46 | } 47 | 48 | function isPlainObject(o) { 49 | if (isObjectObject(o) === false) return false; 50 | 51 | // If has modified constructor 52 | const ctor = o.constructor; 53 | if (typeof ctor !== "function") return false; 54 | 55 | // If has modified prototype 56 | const prot = ctor.prototype; 57 | if (isObjectObject(prot) === false) return false; 58 | 59 | // If constructor does not have an Object-specific method 60 | if (prot.hasOwnProperty("isPrototypeOf") === false) { 61 | return false; 62 | } 63 | 64 | // Most likely a plain Object 65 | return true; 66 | } 67 | 68 | // https://github.com/jonschlinkert/isobject/blob/master/index.js 69 | function isObject(val) { 70 | return val != null && typeof val === "object" && Array.isArray(val) === false; 71 | } 72 | -------------------------------------------------------------------------------- /tasks/typedocs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Generate TypeDoc for each package to extract api.json 4 | 5 | set -e 6 | 7 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 8 | ROOT="$(dirname $DIR)" 9 | DOCS_ROOT="$ROOT/docs/packages" 10 | SRC="$ROOT/packages" 11 | 12 | PACKAGES=$(ls -1 -d "$SRC"/*/) 13 | 14 | for package in $PACKAGES 15 | do 16 | echo "*** ${package}" 17 | name=$(basename $package) 18 | dir=$(dirname $package) 19 | # Not using the generated typedocs at all 20 | DOCS="/tmp/typedocs/$name" 21 | # Only running it to get the API JSON file 22 | JSON="$ROOT/docs/src/packages/$name/api.json" 23 | rm -rf "$DOCS" 24 | mkdir -p "$DOCS" 25 | options="${package}typedoc.json" 26 | if [ ! -e "$options" ]; then 27 | echo "{}" > "$options" 28 | fi 29 | 30 | # includes all external modules: 31 | # --includeDeclarations 32 | # echo typedoc --options "$options" --out "$DOCS" "${package}src/" 33 | (cd $package; typedoc --options "$options" --mode modules --toc index --readme none --ignoreCompilerErrors --excludeNotExported --exclude '"**/__tests__/**"' --out "$DOCS" --json "$JSON" "${package}src/") 34 | done 35 | --------------------------------------------------------------------------------