Created
June 3, 2017 23:14
-
-
Save mohayonao/57cc73561c6fd41ba5a55ac6787f30cf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"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