Skip to content

Commit

Permalink
Merge branch 'release/0.11.3'
Browse files Browse the repository at this point in the history
  • Loading branch information
crucialfelix committed Jul 6, 2016
2 parents a710447 + 5b761b8 commit 1fc79a4
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 93 deletions.
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "supercolliderjs",
"description": "Tools for working with the SuperCollider music language environment",
"version": "0.11.2",
"version": "0.11.3",
"author": "Chris Sattinger <crucialfelix@gmail.com>",
"contributors": [
{
Expand All @@ -27,12 +27,12 @@
"devDependencies": {
"babel": "^6.2.4",
"babel-cli": "^6.10.1",
"babel-jest": "^12.1.0",
"babel-jest": "^13.0.0",
"babel-preset-es2015": "^6.9.0",
"baconjs": "^0.7.84",
"eslint": "^2.13.1",
"jest-cli": "^12.1.1",
"jscs": "^3.0.5"
"eslint": "^3.0.1",
"jest-cli": "^13.1.0",
"jscs": "^3.0.6"
},
"license": "MIT",
"keywords": [
Expand Down Expand Up @@ -93,7 +93,8 @@
"node_modules/rx",
"node_modules/baconjs"
],
"collectCoverage": false
"collectCoverage": true,
"automock": true
},
"jshintConfig": {
"esnext": true
Expand Down
27 changes: 15 additions & 12 deletions src/dryads/SCSynthDef.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,31 @@ const StateKeys = {

/**
* Compile a SynthDef from sclang source code
* or load a precompiled .scsyndef
*
* If compilation is required then it will insert SCLang as a parent if necessary.
*
* properties:
* - source - sclang source code to compile
* - compileFrom - path of .scd file to compile
* - watch (Boolean) - watch compileFrom file and recompile on changes
* - saveToDir - path to save compiled .scsyndef to after compiling
* - loadFrom - path of previously compiled .scsyndef file to load to server
* This can be used to load SynthDefs without needing sclang running at all.
*
* `synthDef` is set in the context for children Dryads to access.
* It is an object:
* - .name
* - .bytes
* - .synthDesc object with descriptive meta data
*
* `synthDefName` is set in context for children Dryads
* `synthDefName` is also set in context for children Dryads
*
* The synthDefName is not known until after the source code is compiled.
*
* options:
* - source - sclang source code to compile
* - compileFrom - path of .scd file to compile
* - watch - watch compileFrom file and recompile on changes
* - saveToDir - path to save compiled .scsyndef to after compiling
* - loadFrom - path of .scsyndef file to load to server
*/
export default class SCSynthDef extends Dryad {

// saveToDir, watch=false,
// constructor(source, compileFrom, loadFrom, children=[]) {
// super({source, compileFrom, loadFrom}, children);
// }

/**
* If there is no SCLang in the parent context,
* then this will wrap itself in an SCLang (language interpreter).
Expand Down Expand Up @@ -78,6 +80,7 @@ export default class SCSynthDef extends Dryad {
}

_sendSynthDef(context, result) {
// ! alters context
// name bytes
// synthDefName should be set for child context
this.putSynthDef(context, result.name, result.synthDesc);
Expand Down
23 changes: 14 additions & 9 deletions src/dryads/Synth.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import * as _ from 'underscore';
/**
* Creates a synth on the server.
*
* Properties:
* - def
* - args
*/
export default class Synth extends Dryad {

Expand All @@ -20,6 +23,16 @@ export default class Synth extends Dryad {
}

subgraph() {
let def = this.properties.def;
if (def.isDryad) {
// wrap self as a child of SCSynthDef
let d = def.clone();
let m = this.clone();
m.properties.def = null; // will get synthDefName from context
d.children = [m];
return d;
}

var sg = [];
// clone and tag each one so they can be looked up during .add
_.each(this.properties.args, (v, k) => {
Expand All @@ -32,15 +45,6 @@ export default class Synth extends Dryad {

sg.push(this);

// wrap self in SynthDef
let def = this.properties.def;
if (def.isDryad) {
let d = def.clone();
d.tag = 'def';
d.children = d.children.concat(sg);
return d;
}

return new Dryad({}, sg);
}

Expand All @@ -54,6 +58,7 @@ export default class Synth extends Dryad {
}

synthDefName(context) {
// The parent SCSynthDef publishes both .synthDef (object) and .synthDefName to context
let name = _.isString(this.properties.def) ? this.properties.def : context.synthDef.name;
if (!name) {
throw new Error('No synthDefName supplied to Synth', context);
Expand Down
4 changes: 2 additions & 2 deletions src/dryads/__tests__/index-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ jest.dontMock('../SCServer');
jest.dontMock('../Group');

var dryads = require('../index');

var SCServer = require('../SCServer').default;

describe('SCServer', function() {

Expand All @@ -13,7 +13,7 @@ describe('SCServer', function() {
});

it('should set default options of SCServer', function() {
let s = new dryads.SCServer();
let s = new SCServer();
expect(s.properties).toEqual({options: {debug: false}});
});

Expand Down
11 changes: 11 additions & 0 deletions src/lang/internals/__tests__/fixtures/routine-postln.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
SUPERCOLLIDERJS:c1c4f120-4346-11e6-9df0-edccf25b99df:CAPTURE:START


SUPERCOLLIDERJS:c1c4f120-4346-11e6-9df0-edccf25b99df:CAPTURE:END

SUPERCOLLIDERJS:c1c4f120-4346-11e6-9df0-edccf25b99df:START:Result
SUPERCOLLIDERJS:c1c4f120-4346-11e6-9df0-edccf25b99df:CHUNK:"a Routine"
SUPERCOLLIDERJS:c1c4f120-4346-11e6-9df0-edccf25b99df:END:Result
SUPERCOLLIDERJS.interpreted
->
hi
14 changes: 14 additions & 0 deletions src/lang/internals/__tests__/sclang-io-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,18 @@ describe('sclang-io', function() {
});
});

describe('capture', function() {
it('should capture any immediate postln from end of CAPTURE', function() {
var io = new SclangIO();
io.setState(STATES.READY);
let output = [];
io.on('stdout', (o) => output.push(o));

feedIt('routine-postln.txt', io);
// should have emited stdout with 'hi'
expect(output.length > 0).toBeTruthy();
expect(output[0].match(/hi/)).toBeTruthy();
});
});

});
145 changes: 85 additions & 60 deletions src/lang/internals/sclang-io.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class SclangIO extends EventEmitter {
if (stf.fn(match, input) === true) {
echo = false;
}

// break if its not a /g regex with multiple results
if (!stf.re.global) {
break;
Expand All @@ -71,6 +72,15 @@ class SclangIO extends EventEmitter {
this.emit('stdout', input);
}

// anything left over should be emitted to stdout ?
// This might result in some content being emitted twice.
// Currently if there is anything after SUPERCOLLIDERJS.interpret
// it is emitted.
// if (last < input.length && (startState === this.state)) {
// console.log('leftovers:', input.substr(last));
// // this.parse(input.substr(last));
// }

// state has changed and there is still text to parse
if (last < input.length && (startState !== this.state)) {
// parse remainder with new state
Expand Down Expand Up @@ -174,6 +184,10 @@ class SclangIO extends EventEmitter {
// REPL is now active
ready: [
{
// There may be multiple SUPERCOLLIDERJS matches in a block of text.
// ie. this is a multi-line global regex
// This fn is called for each of them with a different match each time
// but the same text body.
re: /^SUPERCOLLIDERJS\:([0-9a-f\-]+)\:([A-Za-z]+)\:(.*)$/mg,
fn: (match, text) => {
var
Expand All @@ -187,75 +201,86 @@ class SclangIO extends EventEmitter {
started = false,
stopped = false;

if (type === 'CAPTURE') {
if (body === 'START') {
this.capturing[guid] = [];
}
if (body === 'START') {
lines = [];
// yuck
_.each(text.split('\n'), (l) => {
if (l.match(/SUPERCOLLIDERJS\:([0-9a-f\-]+)\:CAPTURE:START/)) {
started = true;
} else if (l.match(/SUPERCOLLIDERJS\:([0-9a-f\-]+)\:CAPTURE:END/)) {
stopped = true;
switch (type) {
case 'CAPTURE':
if (body === 'START') {
this.capturing[guid] = [];
lines = [];
// yuck
_.each(text.split('\n'), (l) => {
if (l.match(/SUPERCOLLIDERJS\:([0-9a-f\-]+)\:CAPTURE:START/)) {
started = true;
} else if (l.match(/SUPERCOLLIDERJS\:([0-9a-f\-]+)\:CAPTURE:END/)) {
stopped = true;
} else {
if (started && (!stopped)) {
lines.push(l);
}
}
});
this.capturing[guid].push(lines.join('\n'));
}
return true;

case 'START':
this.responseCollectors[guid] = {
type: body,
chunks: []
};
return true;

case 'CHUNK':
this.responseCollectors[guid].chunks.push(body);
return true;

case 'END':
response = this.responseCollectors[guid];
stdout = response.chunks.join('');
obj = JSON.parse(stdout);

if (guid in this.calls) {
if (response.type === 'Result') {
// anything posted during CAPTURE should be forwarded
// to stdout
stdout = this.capturing[guid].join('\n');
delete this.capturing[guid];
if (stdout) {
this.emit('stdout', stdout);
}
this.calls[guid].resolve(obj);
} else {
if (started && (!stopped)) {
lines.push(l);
if (response.type === 'SyntaxError') {
stdout = this.capturing[guid].join('\n');
obj = this.parseSyntaxErrors(stdout);
delete this.capturing[guid];
}
this.calls[guid].reject({type: response.type, error: obj});
}
});
this.capturing[guid].push(lines.join('\n'));
}
return true;
}
if (type === 'START') {
this.responseCollectors[guid] = {
type: body,
chunks: []
};
return true;
}
if (type === 'CHUNK') {
this.responseCollectors[guid].chunks.push(body);
return true;
}
if (type === 'END') {
response = this.responseCollectors[guid];
stdout = response.chunks.join('');
obj = JSON.parse(stdout);

if (guid in this.calls) {
if (response.type === 'Result') {
// anything posted during CAPTURE should be forwarded
// to stdout
stdout = this.capturing[guid].join('\n');
delete this.capturing[guid];
if (stdout) {
this.emit('stdout', stdout);
}
this.calls[guid].resolve(obj);
delete this.calls[guid];
} else {
if (response.type === 'SyntaxError') {
stdout = this.capturing[guid].join('\n');
obj = this.parseSyntaxErrors(stdout);
delete this.capturing[guid];
// I hope sc doesn't post multiple streams at the same time
if (guid === '0') {
// out of band error
this.emit('error', {type: response.type, error: obj});
}
this.calls[guid].reject({type: response.type, error: obj});
}
delete this.calls[guid];
} else {
// I hope sc doesn't post multiple streams at the same time
if (guid === '0') {
// out of band error
this.emit('error', {type: response.type, error: obj});
}
}
delete this.responseCollectors[guid];
return true;
delete this.responseCollectors[guid];
return true;

default:
}
}
},
{
re: /^SUPERCOLLIDERJS.interpreted$/mg,
fn: (match, text) => {
let rest = text.substr(match.index + 28);
// remove the prompt ->
rest = rest.replace(/-> \r?\n?/, '');
this.emit('stdout', rest);
return true;
}
},
{
// user compiled programmatically eg. with Quarks.gui button
re: /^compiling class library/m,
Expand Down
2 changes: 1 addition & 1 deletion src/utils/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export default class Logger {
case 'stdin':
case 'sendosc':
case 'rcvosc':
this.log.debug(clean);
this.log.info(clean);
break;
case 'stderr':
case 'error':
Expand Down
5 changes: 2 additions & 3 deletions src/utils/resolveOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,8 @@ function defaultOptions() {
opts.sclang_conf = join(defaultRoot, 'sclang_conf.yaml');
break;
case 'darwin':
defaultRoot = '/Applications/SuperCollider/SuperCollider.app/Contents/MacOS';
opts.sclang = join(defaultRoot, 'sclang');
opts.scsynth = join(defaultRoot, 'scsynth');
opts.sclang = '/Applications/SuperCollider/SuperCollider.app/Contents/MacOS/sclang';
opts.scsynth = '/Applications/SuperCollider/SuperCollider.app/Contents/Resources/scsynth';
opts.sclang_conf = `${getUserHome()}/Library/Application Support/SuperCollider/sclang_conf.yaml`;
break;
default:
Expand Down

0 comments on commit 1fc79a4

Please sign in to comment.