Skip to content

Instantly share code, notes, and snippets.

@sfpgmr sfpgmr/.gitignore
Last active Oct 20, 2015

Embed
What would you like to do?
Web Audio APIのモジュラー接続デモを作ってみる(3)
typings
src
.vscode

Web Audio APIのモジュラー接続デモを作ってみる(3)

WebAudioのモジュラー接続デモですが、現時点ではノードをドラッグできるくらいで何もできません。機能は徐々に実装していく予定です。

Chrome,Edge,Firefoxで動作を確認しています。

http://blog.sfpgmr.net/entry/2015/10/20/211950

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.js" ></script>
<script type="text/javascript" src="./script.js" ></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-15457703-11', 'auto');
ga('send', 'pageview');
</script>
</head>
<body>
</body>
</html>
{
"compilerOptions": {
"target": "ES6"
}
}
"use strict";
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x5, _x6, _x7) { var _again = true; _function: while (_again) { var object = _x5, property = _x6, receiver = _x7; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x5 = parent; _x6 = property; _x7 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var nodeHeight = 50;
var nodeWidth = 100;
var pointSize = 16;
var svg;
var counter = 0;
var audioNodes = [];
var audioConnections = [];
//aa
var NodeViewData = function NodeViewData() {
var x = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0];
var y = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1];
var width = arguments.length <= 2 || arguments[2] === undefined ? nodeWidth : arguments[2];
var height = arguments.length <= 3 || arguments[3] === undefined ? nodeHeight : arguments[3];
_classCallCheck(this, NodeViewData);
this.x = x;
this.y = y;
this.width = width;
this.height = height;
};
var AudioParam_ = (function (_NodeViewData) {
_inherits(AudioParam_, _NodeViewData);
function AudioParam_(audioNode_, name, param) {
_classCallCheck(this, AudioParam_);
_get(Object.getPrototypeOf(AudioParam_.prototype), 'constructor', this).call(this, 0, 0, pointSize, pointSize);
this.id = counter++;
this.name = name;
this.audioParam = param;
this.audioNode_ = audioNode_;
}
return AudioParam_;
})(NodeViewData);
var AudioNode_ = (function (_NodeViewData2) {
_inherits(AudioNode_, _NodeViewData2);
function AudioNode_(audioNode) {
var _this = this;
_classCallCheck(this, AudioNode_);
_get(Object.getPrototypeOf(AudioNode_.prototype), 'constructor', this).call(this);
this.id = counter++;
this.audioNode = audioNode;
this.name = audioNode.constructor.toString().match(/function\s(.*)\(/)[1];
this.audioParams = [];
var cy = 1;
for (var i in audioNode) {
if (typeof audioNode[i] === 'function') {
this[i] = audioNode[i].bind(audioNode);
} else {
if (typeof audioNode[i] === 'object') {
if (audioNode[i] instanceof AudioParam) {
this[i] = new AudioParam_(this, i, audioNode[i]);
this.audioParams.push(this[i]);
this[i].y = 20 * cy++;
} else {
this[i] = audioNode[i];
}
} else {
Object.defineProperty(this, i, {
get: (function (i) {
return _this.audioNode[i];
}).bind(null, i),
set: (function (i, v) {
_this.audioNode[i] = v;
}).bind(null, i),
enumerable: true,
configurable: false
});
}
}
}
this.inputStartY = cy * 20;
var inputHeight = (cy + this.numberOfInputs) * 20;
var outputHeight = this.numberOfOutputs * 20 + 20;
this.outputStartY = 20;
this.height = Math.max(this.height, inputHeight, outputHeight);
this.cache = {};
}
// 1つだけだとノードの削除で2つの場合はコネクションの削除
_createClass(AudioNode_, null, [{
key: 'remove',
value: function remove(node) {
// ノードの削除
for (var i = 0; i < AudioNode_.audioNodes.length; ++i) {
if (AudioNode_.audioNodes[i] === node) {
AudioNode_.audioNodes.splice(i--, 1);
}
}
for (var i = 0; i < AudioNode_.audioConnections.length; ++i) {
var n = AudioNode_.audioConnections[i];
var disconnected = false;
if (n.from.node === node) {
if (n.to.param) {
// toパラメータあり
if (n.to.param instanceof AudioParam_) {
// AUdioParam
if (n.from.param) {
// fromパラメータあり
n.from.node.disconnect(n.to.param.audioParam, n.from.param);
disconnected = true;
} else {
// fromパラメータなし
n.from.node.disconnect(n.to.param.audioParam);
disconnected = true;
}
} else {
// n.to.paramが数字
if (n.from.param) {
// fromパラメータあり
n.from.node.disconnect(n.to.node.audioNode, n.from.param, n.to.param);
disconnected = true;
} else {
// fromパラメータなし
n.from.node.disconnect(n.to.node.audioNode, 0, n.to.param);
disconnected = true;
}
}
} else {
// to パラメータなし
if (n.from.param) {
// fromパラメータあり
n.from.node.disconnect(n.to.node.audioNode, n.from.param);
disconnected = true;
} else {
// fromパラメータなし
n.from.node.disconnect(n.to.node.audioNode);
disconnected = true;
}
}
}
if (n.to.node === node) {
if (n.from.param) {
// from パラメータあり
if (n.to.param) {
// to パラメータあり
if (n.to.param instanceof AudioParam_) {
// to パラメータがAudioParam_
n.from.node.disconnect(n.to.param.audioParam, n.from.param);
disconnected = true;
} else {
// to パラメータが数字
n.from.node.disconnect(n.to.node.audioNode, n.from.param, n.to.param);
disconnected = true;
}
} else {
// to パラメータなし
n.from.node.disconnect(n.to.node.audioNode, n.from.param);
disconnected = true;
}
} else {
// from パラメータなし
if (n.to.param) {
// to パラメータあり
if (n.to.param instanceof AudioParam_) {
// to パラメータがAudioParam_
n.from.node.disconnect(n.to.param.audioParam);
disconnected = true;
} else {
// to パラメータが数字
n.from.node.disconnect(n.to.node.audioNode, 0, n.to.param);
disconnected = true;
}
} else {
// to パラメータなし
n.from.node.disconnect(n.to.node.audioNode);
disconnected = true;
}
}
}
if (disconnected) {
AudioNode_.audioConnections.splice(i--, 1);
}
}
}
}, {
key: 'disconnect',
value: function disconnect(from_, to_) {
if (from_ instanceof AudioNode_) {
from_ = { node: from_ };
}
if (to_ instanceof AudioNode_) {
to_ = { node: to_ };
}
if (to_ instanceof AudioParam_) {
to_ = { node: to_.audioNode_, param: to_ };
}
var con = { 'from': from_, 'to': to_ };
// コネクションの削除
for (var i = 0; i < AudioNode_.audioConnections.length; ++i) {
var n = AudioNode_.audioConnections[i];
if (con === n) {
AudioNode_.audioConnections.splice(i--, 1);
if (con.from.param) {
// fromパラメータあり
if (con.to.param) {
// to パラメータあり
if (con.to.param instanceof AudioParam_) {
// to AudioParam_
con.from.node.disconnect(con.to.param.audioParam, con.from.param);
} else {
// to 数字
con.from.node.disconnect(con.to.node.audioNode, con.from.param, con.to.param);
}
} else {
// to パラメータなし
con.from.node.disconnect(con.to.node.audioNode, con.from.param);
}
} else {
// fromパラメータなし
if (con.to.param) {
// to パラメータあり
if (con.to.param instanceof AudioParam_) {
// to AudioParam_
con.from.node.disconnect(con.to.param.audioParam);
} else {
// to 数字
con.from.node.disconnect(con.to.node.audioNode, 0, con.to.param);
}
} else {
// to パラメータなし
con.from.node.disconnect(con.to.node.audioNode);
}
}
}
}
}
}, {
key: 'create',
value: function create(audionode) {
var obj = new AudioNode_(audionode);
AudioNode_.audioNodes.push(obj);
return obj;
}
}, {
key: 'connect',
value: function connect(from_, to_) {
if (from_ instanceof AudioNode_) {
from_ = { node: from_ };
}
if (to_ instanceof AudioNode_) {
to_ = { node: to_ };
}
if (to_ instanceof AudioParam_) {
to_ = { node: to_.audioNode_, param: to_ };
}
// 存在チェック
for (var i = 0, l = AudioNode_.audioConnections.length; i < l; ++i) {
if (AudioNode_.audioConnections[i].from === from_ && AudioNode_.audioConnections[i].to === to_) {
throw new Error('接続が重複しています。');
}
}
if (from_.param) {
// fromパラメータあり
if (to_.param) {
// toパラメータあり
if (to_.param instanceof AudioParam_) {
// AudioParamの場合
from_.node.connect(to_.param.audioParam, from_.param);
} else {
// 数字の場合
from_.node.connect(to_.node.audioNode, from_.param, to_.param);
}
} else {
// toパラメータなし
from_.node.connect(to_.node.audioNode, from_.param);
}
} else {
// fromパラメータなし
if (to_.param) {
// toパラメータあり
if (to_.param instanceof AudioParam_) {
// AudioParamの場合
from_.node.connect(to_.param.audioParam);
} else {
// 数字の場合
from_.node.connect(to_.node.audioNode, 0, to_.param);
}
} else {
// toパラメータなし
from_.node.connect(to_.node.audioNode);
}
//throw new Error('Connection Error');
}
AudioNode_.audioConnections.push({
'from': from_,
'to': to_
});
}
}]);
return AudioNode_;
})(NodeViewData);
AudioNode_.audioNodes = [];
AudioNode_.audioConnections = [];
var nodeGroup, lineGroup;
var drag;
window.onload = function () {
var ctx = new AudioContext();
d3.select(window).on('resize', function () {
if (svg) {
svg.attr({
width: window.innerWidth,
height: window.innerHeight
});
}
});
// for(var i in ctx){
// if(i.match(/create/)){
// try {
// var o = ctx[i]();
// for(var j in o){
// if(o[j] instanceof AudioParam){
// for(var k in o[j]){
// console.log(' ',k,o[j][k] instanceof AudioParam,o[j][k]);
// }
// }
// }
//
// } catch (e){
// console.log(e);
// }
//
// }
// }
//
var osc = AudioNode_.create(ctx.createOscillator());
osc.x = 100;
osc.y = 200;
var gain = AudioNode_.create(ctx.createGain());
gain.x = 400;
gain.y = 200;
var filter = AudioNode_.create(ctx.createBiquadFilter());
filter.x = 250;
filter.y = 330;
var out = AudioNode_.create(ctx.destination);
out.x = 750;
out.y = 300;
var osc2 = AudioNode_.create(ctx.createOscillator());
osc2.x = 100;
osc2.y = 600;
var splitter = AudioNode_.create(ctx.createChannelSplitter());
splitter.x = 250;
splitter.y = 600;
var merger = AudioNode_.create(ctx.createChannelMerger());
merger.x = 500;
merger.y = 600;
// for(var i in osc){
// console.log(i + ':' + osc[i]);
// for(var j in osc[i]){
// console.log(' ' + j + ':' + osc[i][j]);
// }
// }
AudioNode_.connect(osc, filter);
AudioNode_.connect(osc, gain.audioParams[0]);
AudioNode_.connect(filter, gain);
AudioNode_.connect(gain, out);
AudioNode_.connect(merger, out);
AudioNode_.connect({ node: splitter, param: 0 }, { node: merger, param: 0 });
AudioNode_.connect({ node: splitter, param: 1 }, { node: merger, param: 1 });
AudioNode_.connect({ node: splitter, param: 2 }, { node: merger, param: 3 });
AudioNode_.connect({ node: splitter, param: 3 }, { node: merger, param: 2 });
AudioNode_.connect({ node: splitter, param: 5 }, { node: merger, param: 5 });
AudioNode_.connect({ node: splitter, param: 4 }, { node: merger, param: 4 });
AudioNode_.connect(osc2, splitter);
console.log(AudioNode_.audioNodes.length);
console.log(AudioNode_.audioConnections.length);
console.log(AudioNode_.audioConnections[0]);
// osc.audioNode.type = 'sawtooth';
osc.type = 'sawtooth';
console.log(osc.type);
osc.frequency.value = 440;
// osc.start();
// osc.stop(ctx.currentTime + 0.1);
drag = d3.behavior.drag().origin(function (d) {
return d;
}).on("drag", function (d) {
if (d3.event.sourceEvent.shiftKey) {
return;
}
d.x += d3.event.dx;
d.y += d3.event.dy;
d3.select(this).attr('transform', 'translate(' + (d.x - d.width / 2) + ',' + (d.y - d.height / 2) + ')');
draw();
});
svg = d3.select('body').append('svg').attr({ 'width': window.innerWidth, 'height': window.innerHeight });
nodeGroup = svg.append('g');
lineGroup = svg.append('g');
draw();
// window.setTimeout(() => {
// //AudioNode_.remove(osc);
// //AudioNode_.remove(out);
// console.log(AudioNode_.audioNodes.length);
// console.log(AudioNode_.audioConnections.length);
// draw();
// }, 2000);
//
// draw();
};
function draw() {
// nodeGroup.selectAll('g').remove();
var gd = nodeGroup.selectAll('g').data(AudioNode_.audioNodes, function (d) {
return d.id;
});
var g = gd.enter().append('g').attr('transform', function (d) {
return 'translate(' + (d.x - d.width / 2) + ',' + (d.y - d.height / 2) + ')';
}).call(drag);
g.append('rect');
g.append('text');
gd.select('rect').attr({ 'width': function width(d) {
return d.width;
}, 'height': function height(d) {
return d.height;
}, 'class': 'audioNode' });
gd.each(function (d) {
var sel = d3.select(this);
var gp = sel.append('g');
var gpd = gp.selectAll('g').data(d.audioParams, function (d) {
return d.id;
});
var gpdg = gpd.enter().append('g');
gpdg.append('circle').attr({ 'r': function r(d) {
return d.width / 2;
}, cx: 0, cy: function cy(d, i) {
/*d.x = 0; d.y = (i + 1) * 20; */return d.y;
}, 'class': 'param' });
gpdg.append('text').attr({ x: function x(d) {
return d.x + d.width / 2 + 5;
}, y: function y(d) {
return d.y;
}, 'class': 'label' }).text(function (d) {
return d.name;
});
gpd.exit().remove();
});
gd.select('text').attr({ x: 0, y: -10, 'class': 'label' }).text(function (d) {
return d.name;
});
gd.filter(function (d) {
return d.numberOfOutputs > 0;
}).each(function (d) {
var sel = d3.select(this).append('g').attr('class', 'output');
if (!d.cache.outs || d.cache.outs && d.cache.outs.length < d.numberOfOutputs) {
d.cache.outs = [];
for (var i = 0; i < d.numberOfOutputs; ++i) {
d.cache.outs.push(i);
}
}
var sel1 = sel.selectAll('g');
var seld = sel1.data(d.cache.outs);
seld.enter().append('g').append('rect').attr({ x: d.width - pointSize / 2, y: function y(d1) {
return d.outputStartY + d1 * 20 - pointSize / 2;
}, width: pointSize, height: pointSize, 'class': 'output' });
seld.exit().remove();
});
gd.filter(function (d) {
return d.numberOfInputs > 0;
}).each(function (d) {
var sel = d3.select(this).append('g').attr('class', 'input');
if (!d.cache.ins || d.cache.ins && d.cache.ins.length < d.numberOfInputs) {
d.cache.ins = [];
for (var i = 0; i < d.numberOfInputs; ++i) {
d.cache.ins.push(i);
}
}
var sel1 = sel.selectAll('g');
var seld = sel1.data(d.cache.ins);
seld.enter().append('g').append('rect').attr({ x: -pointSize / 2, y: function y(d1) {
return d.inputStartY + d1 * 20 - pointSize / 2;
}, width: pointSize, height: pointSize, 'class': 'input' });
seld.exit().remove();
});
// .append('rect')
// .attr({ x: -pointSize / 2, y: nodeHeight / 2 - pointSize / 2, width: pointSize, height: pointSize, 'class': 'input' });
gd.exit().remove();
// line 描画
var ld = lineGroup.selectAll('path').data(AudioNode_.audioConnections);
ld.enter().append('path');
ld.each(function (d) {
var path = d3.select(this);
var x1, y1, x2, y2;
// x1,y1
x1 = d.from.node.x + d.from.node.width / 2;
if (d.from.param) {
y1 = d.from.node.y - d.from.node.height / 2 + d.from.node.outputStartY + d.from.param * 20;
} else {
y1 = d.from.node.y - d.from.node.height / 2 + d.from.node.outputStartY;
}
x2 = d.to.node.x - d.to.node.width / 2;
y2 = d.to.node.y - d.to.node.height / 2;
if (d.to.param) {
if (d.to.param instanceof AudioParam_) {
x2 += d.to.param.x;
y2 += d.to.param.y;
} else {
y2 += d.to.node.inputStartY + d.to.param * 20;
}
} else {
y2 += d.to.node.inputStartY;
}
var line = d3.svg.line().x(function (d) {
return d.x;
}).y(function (d) {
return d.y;
}).interpolate('basis');
var pos = [{ x: x1, y: y1 }, { x: x1 + (x2 - x1) / 4, y: y1 }, { x: x1 + (x2 - x1) / 2, y: y1 + (y2 - y1) / 2 }, { x: x1 + (x2 - x1) * 3 / 4, y: y2 }, { x: x2, y: y2 }];
path.attr({ 'd': line(pos), 'class': 'line' });
//
// line.attr({
// x1: x1,
// y1: y1,
// x2: x2,
// y2: y2,
// 'class': 'line'
// });
});
ld.exit().remove();
}
"use strict";
const nodeHeight = 50;
const nodeWidth = 100;
const pointSize = 16;
var svg;
var counter = 0;
var audioNodes = [
];
var audioConnections = [
];
//aa
class NodeViewData {
constructor(x = 0, y = 0,width = nodeWidth,height = nodeHeight) {
this.x = x ;
this.y = y ;
this.width = width ;
this.height = height ;
}
}
class AudioParam_ extends NodeViewData {
constructor(audioNode_,name, param) {
super(0,0,pointSize,pointSize);
this.id = counter++;
this.name = name;
this.audioParam = param;
this.audioNode_ = audioNode_;
}
}
class AudioNode_ extends NodeViewData {
constructor(audioNode) {
super();
this.id = counter++;
this.audioNode = audioNode;
this.name = audioNode.constructor.toString().match(/function\s(.*)\(/)[1];
this.audioParams = [];
let cy = 1;
for (var i in audioNode) {
if (typeof audioNode[i] === 'function') {
this[i] = audioNode[i].bind(audioNode);
} else {
if (typeof audioNode[i] === 'object') {
if (audioNode[i] instanceof AudioParam) {
this[i] = new AudioParam_(this,i, audioNode[i]);
this.audioParams.push(this[i]);
this[i].y = (20 * cy++);
} else {
this[i] = audioNode[i];
}
} else {
Object.defineProperty(this, i, {
get: ((i) => this.audioNode[i]).bind(null, i),
set: ((i, v) => { this.audioNode[i] = v; }).bind(null, i),
enumerable: true,
configurable: false
});
}
}
}
this.inputStartY = cy * 20;
var inputHeight = (cy + this.numberOfInputs) * 20 ;
var outputHeight = this.numberOfOutputs * 20 + 20 ;
this.outputStartY = 20;
this.height = Math.max(this.height,inputHeight,outputHeight);
this.cache = {};
}
// 1つだけだとノードの削除で2つの場合はコネクションの削除
static remove(node) {
// ノードの削除
for (var i = 0; i < AudioNode_.audioNodes.length; ++i) {
if (AudioNode_.audioNodes[i] === node) {
AudioNode_.audioNodes.splice(i--, 1);
}
}
for (var i = 0; i < AudioNode_.audioConnections.length; ++i) {
let n = AudioNode_.audioConnections[i];
let disconnected = false;
if (n.from.node === node) {
if(n.to.param){
// toパラメータあり
if(n.to.param instanceof AudioParam_){
// AUdioParam
if(n.from.param){
// fromパラメータあり
n.from.node.disconnect(n.to.param.audioParam,n.from.param);
disconnected = true;
} else {
// fromパラメータなし
n.from.node.disconnect(n.to.param.audioParam);
disconnected = true;
}
} else {
// n.to.paramが数字
if(n.from.param){
// fromパラメータあり
n.from.node.disconnect(n.to.node.audioNode,n.from.param,n.to.param);
disconnected = true;
} else {
// fromパラメータなし
n.from.node.disconnect(n.to.node.audioNode,0,n.to.param);
disconnected = true;
}
}
} else {
// to パラメータなし
if(n.from.param){
// fromパラメータあり
n.from.node.disconnect(n.to.node.audioNode,n.from.param);
disconnected = true;
} else {
// fromパラメータなし
n.from.node.disconnect(n.to.node.audioNode);
disconnected = true;
}
}
}
if(n.to.node === node){
if(n.from.param){
// from パラメータあり
if(n.to.param){
// to パラメータあり
if(n.to.param instanceof AudioParam_){
// to パラメータがAudioParam_
n.from.node.disconnect(n.to.param.audioParam,n.from.param);
disconnected = true;
} else {
// to パラメータが数字
n.from.node.disconnect(n.to.node.audioNode,n.from.param,n.to.param);
disconnected = true;
}
} else {
// to パラメータなし
n.from.node.disconnect(n.to.node.audioNode,n.from.param);
disconnected = true;
}
} else {
// from パラメータなし
if(n.to.param){
// to パラメータあり
if(n.to.param instanceof AudioParam_){
// to パラメータがAudioParam_
n.from.node.disconnect(n.to.param.audioParam);
disconnected = true;
} else {
// to パラメータが数字
n.from.node.disconnect(n.to.node.audioNode,0,n.to.param);
disconnected = true;
}
} else {
// to パラメータなし
n.from.node.disconnect(n.to.node.audioNode);
disconnected = true;
}
}
}
if(disconnected){
AudioNode_.audioConnections.splice(i--,1);
}
}
}
static disconnect(from_,to_) {
if(from_ instanceof AudioNode_){
from_ = {node:from_};
}
if(to_ instanceof AudioNode_){
to_ = {node:to_};
}
if(to_ instanceof AudioParam_){
to_ = {node:to_.audioNode_,param:to_}
}
var con = {'from':from_,'to':to_};
// コネクションの削除
for (var i = 0; i < AudioNode_.audioConnections.length; ++i) {
let n = AudioNode_.audioConnections[i];
if(con === n){
AudioNode_.audioConnections.splice(i--,1);
if(con.from.param){
// fromパラメータあり
if(con.to.param){
// to パラメータあり
if(con.to.param instanceof AudioParam_){
// to AudioParam_
con.from.node.disconnect(con.to.param.audioParam,con.from.param);
} else {
// to 数字
con.from.node.disconnect(con.to.node.audioNode,con.from.param,con.to.param);
}
} else {
// to パラメータなし
con.from.node.disconnect(con.to.node.audioNode,con.from.param);
}
} else {
// fromパラメータなし
if(con.to.param){
// to パラメータあり
if(con.to.param instanceof AudioParam_){
// to AudioParam_
con.from.node.disconnect(con.to.param.audioParam);
} else {
// to 数字
con.from.node.disconnect(con.to.node.audioNode,0,con.to.param);
}
} else {
// to パラメータなし
con.from.node.disconnect(con.to.node.audioNode);
}
}
}
}
}
static create(audionode) {
var obj = new AudioNode_(audionode);
AudioNode_.audioNodes.push(obj);
return obj;
}
static connect(from_, to_) {
if(from_ instanceof AudioNode_ ){
from_ = {node:from_};
}
if(to_ instanceof AudioNode_){
to_ = {node:to_};
}
if(to_ instanceof AudioParam_){
to_ = {node:to_.audioNode_,param:to_};
}
// 存在チェック
for (var i = 0, l = AudioNode_.audioConnections.length; i < l; ++i) {
if (AudioNode_.audioConnections[i].from === from_
&& AudioNode_.audioConnections[i].to === to_) {
throw (new Error('接続が重複しています。'));
}
}
if (from_.param) {
// fromパラメータあり
if (to_.param) {
// toパラメータあり
if(to_.param instanceof AudioParam_){
// AudioParamの場合
from_.node.connect(to_.param.audioParam,from_.param);
} else{
// 数字の場合
from_.node.connect(to_.node.audioNode, from_.param,to_.param);
}
} else {
// toパラメータなし
from_.node.connect(to_.node.audioNode,from_.param);
}
} else {
// fromパラメータなし
if (to_.param) {
// toパラメータあり
if(to_.param instanceof AudioParam_){
// AudioParamの場合
from_.node.connect(to_.param.audioParam);
} else{
// 数字の場合
from_.node.connect(to_.node.audioNode,0,to_.param);
}
} else {
// toパラメータなし
from_.node.connect(to_.node.audioNode);
}
//throw new Error('Connection Error');
}
AudioNode_.audioConnections.push
({
'from': from_,
'to': to_
});
}
}
AudioNode_.audioNodes = [];
AudioNode_.audioConnections = [];
var nodeGroup, lineGroup;
var drag;
window.onload = () => {
var ctx = new AudioContext();
d3.select(window)
.on('resize',function(){
if(svg){
svg.attr({
width:window.innerWidth,
height:window.innerHeight
})
}
});
// for(var i in ctx){
// if(i.match(/create/)){
// try {
// var o = ctx[i]();
// for(var j in o){
// if(o[j] instanceof AudioParam){
// for(var k in o[j]){
// console.log(' ',k,o[j][k] instanceof AudioParam,o[j][k]);
// }
// }
// }
//
// } catch (e){
// console.log(e);
// }
//
// }
// }
//
var osc = AudioNode_.create(ctx.createOscillator());
osc.x = 100;
osc.y = 200;
var gain = AudioNode_.create(ctx.createGain());
gain.x = 400;
gain.y = 200;
var filter = AudioNode_.create(ctx.createBiquadFilter());
filter.x = 250;
filter.y = 330;
var out = AudioNode_.create(ctx.destination);
out.x = 750;
out.y = 300;
var osc2 = AudioNode_.create(ctx.createOscillator());
osc2.x = 100;
osc2.y = 600;
var splitter = AudioNode_.create(ctx.createChannelSplitter());
splitter.x = 250;
splitter.y = 600;
var merger = AudioNode_.create(ctx.createChannelMerger());
merger.x = 500;
merger.y = 600;
// for(var i in osc){
// console.log(i + ':' + osc[i]);
// for(var j in osc[i]){
// console.log(' ' + j + ':' + osc[i][j]);
// }
// }
AudioNode_.connect(osc, filter);
AudioNode_.connect(osc,gain.audioParams[0]);
AudioNode_.connect(filter,gain);
AudioNode_.connect(gain,out);
AudioNode_.connect(merger,out);
AudioNode_.connect({node:splitter,param:0},{node:merger,param:0});
AudioNode_.connect({node:splitter,param:1},{node:merger,param:1});
AudioNode_.connect({node:splitter,param:2},{node:merger,param:3});
AudioNode_.connect({node:splitter,param:3},{node:merger,param:2});
AudioNode_.connect({node:splitter,param:5},{node:merger,param:5});
AudioNode_.connect({node:splitter,param:4},{node:merger,param:4});
AudioNode_.connect(osc2,splitter);
console.log(AudioNode_.audioNodes.length);
console.log(AudioNode_.audioConnections.length);
console.log(AudioNode_.audioConnections[0]);
// osc.audioNode.type = 'sawtooth';
osc.type = 'sawtooth';
console.log(osc.type);
osc.frequency.value = 440;
// osc.start();
// osc.stop(ctx.currentTime + 0.1);
drag = d3.behavior.drag()
.origin(function (d) { return d; })
.on("drag", function (d) {
if (d3.event.sourceEvent.shiftKey) {
return;
}
d.x += d3.event.dx;
d.y += d3.event.dy;
d3.select(this).attr('transform', 'translate(' + (d.x - d.width / 2) + ',' + (d.y - d.height / 2) + ')');
draw();
});
svg = d3.select('body')
.append('svg')
.attr({ 'width': window.innerWidth, 'height': window.innerHeight });
nodeGroup = svg.append('g');
lineGroup = svg.append('g');
draw();
// window.setTimeout(() => {
// //AudioNode_.remove(osc);
// //AudioNode_.remove(out);
// console.log(AudioNode_.audioNodes.length);
// console.log(AudioNode_.audioConnections.length);
// draw();
// }, 2000);
//
// draw();
}
function draw() {
// nodeGroup.selectAll('g').remove();
var gd = nodeGroup.selectAll('g').
data(AudioNode_.audioNodes,function(d){return d.id;});
var g = gd.enter()
.append('g')
.attr('transform', function (d) { return 'translate(' + (d.x - d.width / 2) + ',' + (d.y - d.height / 2) + ')' })
.call(drag);
g.append('rect');
g.append('text');
gd.select('rect')
.attr({ 'width': (d)=> d.width, 'height': (d)=> d.height, 'class': 'audioNode' });
gd.each(function (d) {
var sel = d3.select(this);
var gp = sel.append('g');
var gpd = gp.selectAll('g')
.data(d.audioParams,function(d){return d.id;});
var gpdg = gpd.enter()
.append('g');
gpdg.append('circle')
.attr({ 'r': (d)=>d.width/2, cx: 0, cy: (d, i)=> { /*d.x = 0; d.y = (i + 1) * 20; */return d.y; }, 'class': 'param' });
gpdg.append('text')
.attr({x: (d)=> (d.x + d.width / 2 + 5),y:(d)=>d.y,'class':'label' })
.text((d)=>d.name);
gpd.exit().remove();
});
gd.select('text')
.attr({ x: 0, y: -10, 'class': 'label' })
.text(function (d) { return d.name; });
gd.filter(function (d) {
return d.numberOfOutputs > 0;
})
.each(function(d){
var sel = d3.select(this).append('g').attr('class','output');
if(!d.cache.outs || (d.cache.outs && (d.cache.outs.length < d.numberOfOutputs)))
{
d.cache.outs = [];
for(var i = 0;i < d.numberOfOutputs;++i){
d.cache.outs.push(i);
}
}
var sel1 = sel.selectAll('g');
var seld = sel1.data(d.cache.outs);
seld.enter()
.append('g')
.append('rect')
.attr({ x: d.width - pointSize / 2, y: (d1)=> (d.outputStartY + d1 * 20 - pointSize / 2), width: pointSize, height: pointSize, 'class': 'output' });
seld.exit().remove();
});
gd
.filter(function (d) { return d.numberOfInputs > 0; })
.each(function(d){
var sel = d3.select(this).append('g').attr('class','input');
if(!d.cache.ins || (d.cache.ins && (d.cache.ins.length < d.numberOfInputs)))
{
d.cache.ins = [];
for(var i = 0;i < d.numberOfInputs;++i){
d.cache.ins.push(i);
}
}
var sel1 = sel.selectAll('g');
var seld = sel1.data(d.cache.ins);
seld.enter()
.append('g')
.append('rect')
.attr({ x: - pointSize / 2, y: (d1)=> (d.inputStartY + d1 * 20 - pointSize / 2), width: pointSize, height: pointSize, 'class': 'input' });
seld.exit().remove();
});
// .append('rect')
// .attr({ x: -pointSize / 2, y: nodeHeight / 2 - pointSize / 2, width: pointSize, height: pointSize, 'class': 'input' });
gd.exit().remove();
// line 描画
var ld = lineGroup.selectAll('path')
.data(AudioNode_.audioConnections);
ld.enter()
.append('path');
ld.each(function (d) {
var path = d3.select(this);
var x1,y1,x2,y2;
// x1,y1
x1 = d.from.node.x + d.from.node.width / 2;
if(d.from.param){
y1 = d.from.node.y - d.from.node.height /2 + d.from.node.outputStartY + d.from.param * 20;
} else {
y1 = d.from.node.y - d.from.node.height /2 + d.from.node.outputStartY;
}
x2 = d.to.node.x - d.to.node.width / 2;
y2 = d.to.node.y - d.to.node.height / 2;
if(d.to.param){
if(d.to.param instanceof AudioParam_){
x2 += d.to.param.x;
y2 += d.to.param.y;
} else {
y2 += d.to.node.inputStartY + d.to.param * 20;
}
} else {
y2 += d.to.node.inputStartY;
}
var line = d3.svg.line()
.x(function(d){return d.x})
.y(function(d){return d.y})
.interpolate('basis');
var pos =
[
{x:x1,y:y1},
{x:x1 + (x2 - x1)/4,y:y1},
{x:x1 + (x2 - x1)/2,y:y1 + (y2 - y1)/2},
{x:x1 + (x2 - x1)*3/4,y:y2},
{x:x2, y:y2}
];
path.attr({'d':line(pos),'class':'line'});
//
// line.attr({
// x1: x1,
// y1: y1,
// x2: x2,
// y2: y2,
// 'class': 'line'
// });
});
ld.exit().remove();
}
*{
font-family: "Hiragino Kaku Gothic ProN","メイリオ", sans-serif;
}
body{
background:white;
color: black;
}
.audioNode {
fill:#BBB;
stroke-width:1px;
stroke:black;
color:black;
}
.param {
fill:red;
}
.label {
stroke:black;
font-size:8px;
}
.input {
fill:red;
}
.output{
fill:blue;
}
.line {
stroke: black;
stroke-width: 4px;
stroke-linecap: round;
fill:none;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.