WebMusicããã«ã½ã³ã§ä½¿ããããªä¿ºä¿ºã©ã¤ãã©ãª 5é¸
7æ25æ¥ã« WebMusicããã«ã½ã³ #4 @kyoto ã¨ããã®ãããã®ã§ãããã¡ããã©è¯ãæ©ä¼ãªã®ã§åãä½ã£ãã¦ã§ãé³æ¥½ç¨ç°¡åè¶ çµ¶ä¾¿å©ã©ã¤ãã©ãªãç´¹ä»ãããã¨æãã¾ãã
WEB AUDIO SCHEDULER
2 つの時計のお話 - Web Audio の正確なスケジューリングについて - HTML5 Rocks
Web Audio ã§å¿ é ãªå²ã«é£ããã®ãã¹ã±ã¸ã¥ã¼ã«ç®¡çã§ããã¡ããä¸ã®è¨äºã®ããæ¹ãæ¨å¥¨ãªã®ã§ããããã®ã¾ã¾ããã¨ããªãé¢åããããã®ã ãã©ããã®ã©ã¤ãã©ãªã使ãã¨é¢åãªé¨åã¯æ°ã«ããããã¤ä½ããããã®ããæ¸ãã ãã§è¯ããªãã以ä¸ã¯ç°¡åãªã¡ãããã¼ã ã®ä¾ã
var gcguard = []; var audioContext = new AudioContext(); var scheduler = new WebAudioScheduler({ context: audioContext }); function metronome(e) { // e.playbackTime ã WebAudio çãªæé // 0.5ç§éé㧠ticktack é¢æ°ã®å¼ã³åºããå¼æ°ä»ãã§ç»é² scheduler.insert(e.playbackTime + 0.000, ticktack, [ 880, 1.00 ]); scheduler.insert(e.playbackTime + 0.500, ticktack, [ 440, 0.05 ]); scheduler.insert(e.playbackTime + 1.000, ticktack, [ 440, 0.05 ]); scheduler.insert(e.playbackTime + 1.500, ticktack, [ 440, 0.05 ]); // 2ç§å¾ã«ãã®é¢æ°ãå¼ã³åºãããªãã scheduler.insert(e.playbackTime + 2.000, metronome); } function ticktack(e, frequency, duration) { var playbackTime = e.playbackTime; var osc = audioContext.createOscillator(); var amp = audioContext.createGain(); var t0 = playbackTime; var t1 = t0 + duration; osc.frequency.value = frequency; amp.gain.setValueAtTime(0.4, t0); amp.gain.linearRampToValueAtTime(0, t1); osc.start(t0); osc.stop(t1); osc.onended = function() { osc.disconnect(); amp.disconnect(); gcguard.splice(gcguard.indexOf(osc), 1); }; gcguard.push(osc); osc.connect(amp); amp.connect(audioContext.destination); } scheduler.start(metronome);
WEB AUDIO API SHIM
JavaScript - Web Audioの新しいAPIについてざっくり解説 - Qiita
Web Audio ã®æ°ãã API ã®ããªãã£ã«ãStereoPannerNode ã§ãªã¼ããã³ã£ãããgetFloatTimeDomainData 㧠ããããªæ³¢å½¢ãæç» ããããpromise-based API ã§ã¢ãã³ãªããã°ã©ãã³ã°ãã§ããããã«ãªããlightçãããããã以ä¸ã¯ fetch API ããã®ç°¡åãªãªã¼ããã³ã®ä¾ã
var gcguard = []; fetch("amen.wav").then(function(res) { return res.arrayBuffer(); }).then(function(audioData) { return audioContext.decodeAudioData(audioData); }).then(function(buffer) { var bufSrc = audioContext.createBufferSource(); var panLFO = audioContext.createOscillator(); var panner = audioContext.createStereoPanner(); var t0 = audioContext.currentTime; var t1 = t0 + 30; bufSrc.buffer = buffer; bufSrc.loop = true; panLFO.frequency.value = 2; bufSrc.start(t0); panLFO.start(t0); bufSrc.stop(t1); panLFO.stop(t1); bufSrc.onended = function() { bufSrc.disconnect(); panLFO.disconnect(); panner.disconnect(); gcguard.splice(gcguard.indexOf(bufSrc), 1); }; gcguard.push(bufSrc); bufSrc.connect(panner); panLFO.connect(panner.pan); panner.connect(audioContext.destination); });
MIDI KEYBOARD
MIDIãã¼ãã¼ãã®æ¼å¥æ å ±ãã¤ãã³ãã¨ããã¿ã¼ãªAPIã§åä¿¡ã§ãããã¤ãWeb MIDI API ã¯ãã¡ãããNode.js ã§ãåãã®ã§ãµã¼ãã¼ãµã¤ãã®ã³ã³ããã¼ã©ã¼ã¨ãã¦ã使ããã以ä¸ã¯ M-AUDIO ã® Keystation Mini 32 ãéãã¦ãæ¼å¥æ å ±ããã³ãããç°¡åãªä¾ã
// éãããããã¤ã¹ã®ååãæå®ã㦠new ãã var midiKey = new MIDIKeyboard("Keystation Mini 32"); midiKey.open().catch(function(e) { console.error(e); }); midiKey.on("message", function(e) { console.log("dataType : " + e.dataType); console.log("noteNumber: " + e.noteNumber); console.log("velocity : " + e.velocity); console.log("value : " + e.value); console.log("channel : " + e.channel); });
MIDIãã¼ãã¼ãã®ååã¯ä»¥ä¸ã®ããã«åå¾ã§ããã
MIDIKeyboard.requestDeviceNames().then(function(devies) { console.log(devices); });
MIDI DEVICE
MIDIãã¼ãã¼ã以å¤ã使ããã人åããç¶æ¿ãããªãã㦠_onmidimessage
ãä¸æ¸ãããã°ç°¡åã«MIDIã³ã³ããã¼ã©ã¼ã®ã©ã¤ãã©ãªãä½ãããä¸ã® MIDI ãã¼ãã¼ã以å¤ã« Novation LAUNCH CONTROL ç¨ã®ãããã®ã§åèã«ãªããããWeb MIDI API, Node.js 以å¤ã«ãã¹ãç¨ã¤ã³ã¿ã¼ãã§ã¼ã¹ãããã®ã§ CI ã¨ãå®ããã¤ã¹ããªãç¶æ
ã§ã使ããããããã以ä¸ã¯ãªããé©å½ã§ç°¡åãªä¾ã
var midiDevice = new MIDIDevice("Super MIDI Controller"); midiDevice.open().catch(function(e) { console.error(e); }); midiDevice._onmidimessage = function(e) { if (e.data[0] & 0x90 === 0x90) { this.emit("noteOn"); } }; midiDevice.bang = function() { this.send([ 0x90, 0x64, 0x64 ]); };
OSC MSG
ãã©ã¦ã¶ã§OSCãèªã¿æ¸ãããã©ã¤ãã©ãªããã¨ã㨠osc-min ã¨ããã©ã¤ãã©ãªã使ã£ã¦ããã®ã ãã©ãbrowserify ããã¨ãµã¤ãºã大ãããªãã®ã¨ãå£ãã OSC ãåä¿¡ããã¨ãã«ä¾å¤ãåºãã®ãå°ãã®ã§èªåã§æ¸ãããåºæ¬çãªAPI㯠osc-min ã¨äºææ§ããããããã¡ãã Node.js ã§ã使ããã以ä¸ã¯ Max/MSPã¨ãã©ã¦ã¶ã§ã®ç°¡åã§æå³ã®ãªãããåãã®ä¾ã
var path = require("path"); var express = require("express"); var socketIO = require("socket.io"); var http = require("http"); var dgram = require("dgram"); var app = express(); var server = http.createServer(app); var webSocket = socketIO(server); var oscSocket = dgram.createSocket("udp4"); app.use(express.static(path.join(__dirname, "./public"))); server.listen(8000, function() { console.log("Listening HTTP on port %d", server.address().port); }); webSocket.on("connect", function(socket) { // ãã©ã¦ã¶ãã Max/MSP ã«è»¢é socket.on("/osc", function(buffer) { oscSocket.send(buffer, 0, buffer.length, 7401, "127.0.0.1"); }); }); // Max/MSP ãããã©ã¦ã¶ã«è»¢é oscSocket.on("message", function(buffer) { webSocket.emit("/osc", buffer); }); oscSocket.bind(7400, function() { console.log("Listening OSC on port %d", oscSocket.address().port); });
<button id="bang">BANG</button> <script src="/socket.io/socket.io.js"></script> <script src="/osc-msg.js"></script> <script> window.onload = function() { var socket = io(); function receiveOSC(buffer) { var msg = OscMsg.fromBuffer(buffer); if (msg.elements) { msg = msg.elements[0]; } msg.args = msg.args.map(function(value) { return value.value; }); console.log(JSON.stringify(msg)); } // WebSocket çµç±ã§ Max/MSP ã«éä¿¡ function sendOSC(msg) { socket.emit("/osc", OscMsg.toBuffer(msg)); } function bang() { sendOSC({ address: "/noteOn", args: [ { type: "integer", value: ((Math.random() * 24) + 48)|0 }, Math.random() * 128, // åãæå®ããªãæ°å¤ã¯ Float ã«ãªã ], }); } // Max/MSP ãã WebSocketçµç±ã§åä¿¡ socket.on("/osc", receiveOSC); document.getElementById("bang").onclick = bang; } </script>
CISEAUX
AudioBuffer ãåãè²¼ãç·¨éãããã¤ãåå²ãããéå転ããããéãããè²ã ã§ãã ãwavãã¡ã¤ã«ãªã Node.js ã§ã使ããã以ä¸ã¯ AudioBuffer ã 100 åå²ã㦠stutter ãã¦é©å½ã«ä¸¦ã³æ¿ããç°¡åãªä¾ã
var gcguard = []; Ciseaux.from("amen.wav").then(function(tape) { // 100åå² var tapes = tape.split(100).map(function(tape) { // ããããã3åç¹°ãè¿ã return tape.repeat(3); }); // ãã£ã¤ãã¦ã¡ãã£ã¨æ©ããã tape = Ciseaux.concat(_.shuffle(tapes)).pitch(1.5); return tape.render(); }).then(function(buffer) { var bufSrc = audioContext.createBufferSource(); var t0 = audioContext.currentTime; var t1 = t0 + 30; bufSrc.buffer = buffer; bufSrc.loop = true; bufSrc.start(t0); bufSrc.stop(t1); bufSrc.onended = function() { bufSrc.disconnect(); gcguard.splice(gcguard.indexOf(bufSrc), 1); }; gcguard.push(bufSrc); bufSrc.connect(audioContext.destination); });
STEREO ANALYSER NODE
AnalyserNode ã®ã¹ãã¬ãªçãgetFloatFrequencyData
ãªã©ã®ã¡ã½ãããå·¦å³ 2ã¤æå®ã§ããã以ä¸ã¯ãªã¼ãã£ãªãã¡ã¤ã«ãèªã¿è¾¼ãã§ããã®å·¦å³ã®å¨æ³¢æ°ã¹ãã¯ãã©ã ã表示ããç°¡åãªä¾ã
var gcguard = {}; fetch("amen.wav").fetch(function(res) { return res.arrayBuffer(); }).then(function(audioData) { return new Promise(function(resolve, reject) { audioContext.decodeData(audioData, resolve, reject); }); }).then(function(buffer) { var bufSrc = audioContext.createBufferSource(); var analyser = new StereoAnalyserNode(audioContext); var t0 = audioContext.currentTime; var t1 = t0 + 30; var timerId = 0; bufSrc.buffer = buffer; bufSrc.loop = true; bufSrc.start(t0); bufSrc.stop(t1); bufSrc.onended = function() { bufSrc.disconnect(); analyser.disconnect(); clearInterval(timerId); gcguard.splice(gcguard.indexOf(bufSrc), 1); }; gcguard.push(bufSrc); var L = new Float32Array(analyser.frequencyBinCount); var R = new Float32Array(analyser.frequencyBinCount); timerId = setInterval(function() { analyser.getFloatFrequencyData(L, R); drawSpectrum(L, R); }, 100); bufSrc.connect(analyser); analyser.connect(audioContext.destination); });
MML EMITTER
MMLã§æ¸ããæ¼å¥æ å ±ãè¯ãæãã®ã¿ã¤ãã³ã°ã§ã¤ãã³ãçºç«ãã¦ããããã¤ã便å©ãªã®ã§è¯ãã®ã ãã©ãå¤ãªæ©è½ããã£ããã³ã¼ããé ·ãã®ã§ããã¡ãã£ã¨ç¶ºéºãªæãã«ç ´å£çã«æ¸ãç´ããããã以ä¸ã¯ãã¬ããã¡ã½ã©ã·ããæ¼å¥ããç°¡åãªä¾ã
var gcguard = []; var mml = new MMLEmitter(audioContext, "t120 l8 $ cdef gab<c c>bag fedc; t120 l2 o3 $ ccee>aa<dd"); mml.tracks[0].on("note", noteOn); mml.tracks[1].on("note", noteOn); function noteOn(e) { var frequency= e.frequency; var duration = e.duration; var playbackTime = e.playbackTime; var osc = audioContext.createOscillator(); var amp = audioContext.createGain(); var t0 = playbackTime; var t1 = t0 + duration; osc.frequency.value = frequency; amp.gain.setValueAtTime(0.4, t0); amp.gain.linearRampToValueAtTime(0, t1); osc.start(t0); osc.stop(t1); osc.onended = function() { osc.disconnect(); amp.disconnect(); gcguard.splice(gcguard.indexOf(osc), 1); }; gcguard.push(osc); osc.connect(amp); amp.connect(audioContext.destination); }