Skip to content

Instantly share code, notes, and snippets.

@mohayonao
Created June 3, 2017 23:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mohayonao/57cc73561c6fd41ba5a55ac6787f30cf to your computer and use it in GitHub Desktop.
Save mohayonao/57cc73561c6fd41ba5a55ac6787f30cf to your computer and use it in GitHub Desktop.
"use strict";
// $ scsynth -u 57110
const dgram = require("dgram");
const OSC = require("osc-msg");
const colors = require("colors");
const SynthDefEncoder = require("synthdef-json-encoder");
const SC_PORT = 57110;
const socket = dgram.createSocket("udp4");
socket.on("message", (buffer) => {
const msg = OSC.decode(buffer);
global.console.log(colors.yellow(`<< ${ JSON.stringify(msg) }`));
});
/**
* JS時間をOSC-timetagに変換する
* @param {number} time
* @return {number[]}
*/
function fromTime(time) {
time = time + 2208988800;
var hi = time >>> 0;
var lo = ((time - hi) * 4294967296) >>> 0;
return [ hi, lo ];
}
/**
* scsynthにOSCメッセージを送信する
* @param {object} msg
*/
function send(msg) {
const buffer = OSC.encode(msg);
socket.send(buffer, SC_PORT, "127.0.0.1", () => {
global.console.log(colors.cyan(`>> ${ JSON.stringify(msg) }`));
});
}
/**
* scsynthにOSCメッセージをタイムタグ付きで送信する
* @param {number} time
* @param {object} msg
*/
function sendAt(time, msg) {
const timetag = fromTime(time);
const buffer = OSC.encode({ timetag, elements: [ msg ] });
socket.send(buffer, SC_PORT, "127.0.0.1", () => {
global.console.log(colors.cyan(`>> ${ JSON.stringify(msg) }`));
});
}
////////////////////////////////////////////////////////////////////////////////
// scsynth向けOSCメッセージ
function dumpOSC(code) {
return {
address: "/dumpOSC",
args: [ { type: "integer", value: code|0 } ]
};
}
function notify(code) {
return {
address: "/notify",
args: [ { type: "integer", value: code|0 } ]
};
}
function clearSched() {
return {
address: "/clearSched"
};
}
function g_freeAll(nodeid) {
return {
address: "/g_freeAll",
args: [ { type: "integer", value: nodeid|0 } ]
};
}
function g_new(nodeid, action = 0, target = 0) {
return {
address: "/g_new",
args: [
{ type: "integer", value: nodeid|0 },
{ type: "integer", value: action|0 },
{ type: "integer", value: target|0 },
]
};
}
function d_recv(sdef) {
return {
address: "/d_recv",
args: [ { type: "blob", value: sdef } ],
};
}
function s_new(sdefname, nodeid, action = 0, target = 0, params=[]) {
return {
address: "/s_new",
args: [
{ type: "string", value: sdefname },
{ type: "integer", value: nodeid|0 },
{ type: "integer", value: action|0 },
{ type: "integer", value: target|0 },
].concat(params.map((value, index) => {
return { type: [ "integer", "float" ][index % 2], value };
}))
};
}
////////////////////////////////////////////////////////////////////////////////
// SynthDefの定義
// SynthDef(\beep, {|freq = 440, amp = 0.25|
// var beep = SinOsc.ar(freq) * Line.kr(amp, 0, 0.25, doneAction: 2);
// Out.ar(0, beep ! 2);
// });
const beep = SynthDefEncoder.encode({
version: 1,
name: "beep",
paramValues: [ 440, 0.25 ],
paramIndices: [
{ name: "freq", index: 0, length: 1 },
{ name: "amp" , index: 1, length: 1 },
],
consts: [ 0, 0.25, 2 ],
units: [
[ "Control" , 1, 0, [ ], [ 1, 1 ] ],
[ "SinOsc" , 2, 0, [ [ 0, 0 ], [ -1, 0 ] ], [ 2 ] ],
[ "Line" , 1, 0, [ [ 0, 1 ], [ -1, 0 ], [ -1, 1 ] ,[ -1, 2 ] ], [ 1 ] ],
[ "BinaryOpUGen", 2, 2, [ [ 1, 0 ], [ 2, 0 ] ], [ 2 ] ],
[ "Out" , 2, 0, [ [ -1, 0 ], [ 3, 0 ], [ 3, 0 ] ], [ ] ],
]
});
////////////////////////////////////////////////////////////////////////////////
// overtoneっぽいAPI
function metronome(bpm) {
const metroStart = Date.now() / 1000;
const beatTicks = 60 / bpm;
return beat => metroStart + beat * beatTicks;
}
function at(time, func, args = []) {
const timeDelta = time - (Date.now() / 1000);
const timerFunc = timeDelta <= 0.1 ? setImmediate : setTimeout;
timerFunc(() => func(...args), timeDelta * 1000);
}
////////////////////////////////////////////////////////////////////////////////
// メイン
function main() {
let synthId = 16777216;
const metro = metronome(120 / 4);
function player(beat = 0) {
const t0 = metro(beat + 0.00);
const t1 = metro(beat + 0.25);
const t2 = metro(beat + 0.50);
const t3 = metro(beat + 0.75);
const t4 = metro(beat + 1.00);
at(t0, () => sendAt(t0, s_new("beep", synthId++, 0, 1, [ 0, 1760, 1, 0.50 ])));
at(t1, () => sendAt(t1, s_new("beep", synthId++, 0, 1, [ 0, 440, 1, 0.25 ])));
at(t2, () => sendAt(t2, s_new("beep", synthId++, 0, 1, [ 0, 440, 1, 0.25 ])));
at(t3, () => sendAt(t3, s_new("beep", synthId++, 0, 1, [ 0, 440, 1, 0.25 ])));
at(t4, player, [ beat + 1 ]);
}
// scsynthの初期化
send(dumpOSC(1));
send(notify(1));
send(g_freeAll(0));
send(clearSched());
send(g_new(1));
// SynthDef[beep]を送信
send(d_recv(beep));
player();
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment