Skip to content

Instantly share code, notes, and snippets.

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

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

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

WebAudioのモジュラー接続デモを作っています。 とりあえずノードの挿入・接続ができ、OsciilatorNodeについてはCtrl+Clickで再生することができます。 その他の音を発するノードは今のところ使用できません。

Chromeで動作を確認しています。エディターズ・ドラフトの機能を使っているため、おそらく他のブラウザでは動作しません。

操作方法

  • 画面の何もないところで右クリック ... AudioNodeの挿入
  • AudioNodeをShift+左クリック ... 削除
  • 出力端子をドラッグ ... 端子接続
  • コネクションをShift+左クリック ... 接続解除
  • AudioNode上で右クリック ... パラメータ設定

実装するのに使用しているもの

  • d3.js
  • VS.Code
  • jasmine
'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 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 = [];
'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 counter = 0;
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 STATUS_PLAY_NOT_PLAYED = 0;
var STATUS_PLAY_PLAYING = 1;
var STATUS_PLAY_PLAYED = 2;
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 = [];
this.params = [];
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.params.push((function (p) {
return {
name: i,
'get': function get() {
return p.audioParam.value;
},
'set': function set(v) {
p.audioParam.value = v;
},
param: p,
node: _this
};
})(this[i]));
this[i].y = 20 * cy++;
} else {
this[i] = audioNode[i];
}
} else {
var desc = Object.getOwnPropertyDescriptor(AudioNode.prototype, i);
if (!desc) {
desc = Object.getOwnPropertyDescriptor(this.audioNode.__proto__, i);
}
var props = {};
if (desc.get) {
props.get = (function (i) {
return _this.audioNode[i];
}).bind(null, i);
}
if (desc.set) {
props.set = (function (i, v) {
_this.audioNode[i] = v;
}).bind(null, i);
}
props.enumerable = desc.enumerable;
props.configurable = desc.configurable;
Object.defineProperty(this, i, props);
props.name = i;
props.node = this;
this.params.push(props);
}
}
}
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.temp = {};
this.statusPlay = STATUS_PLAY_NOT_PLAYED; // not played.
}
// 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.from === n.from && con.to === n.to) {
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_, param: 0 };
}
if (to_ instanceof AudioNode_) {
to_ = { node: to_, param: 0 };
}
if (to_ instanceof AudioParam_) {
to_ = { node: to_.audioNode_, param: to_ };
}
// 存在チェック
for (var i = 0, l = AudioNode_.audioConnections.length; i < l; ++i) {
var c = AudioNode_.audioConnections[i];
if (c.from.node === from_.node && c.from.param === from_.param && c.to.node === to_.node && c.to.param === to_.param) {
return;
// 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 counter = 0;
class NodeViewData {
constructor(x = 0, y = 0,width = nodeWidth,height = nodeHeight) {
this.x = x ;
this.y = y ;
this.width = width ;
this.height = height ;
}
}
const STATUS_PLAY_NOT_PLAYED = 0;
const STATUS_PLAY_PLAYING = 1;
const STATUS_PLAY_PLAYED = 2;
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 = [];
this.params = [];
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.params.push(((p)=>{
return {
name:i,
'get':() => p.audioParam.value,
'set':(v) =>{p.audioParam.value = v;},
param:p,
node:this
}
})(this[i]));
this[i].y = (20 * cy++);
} else {
this[i] = audioNode[i];
}
} else {
var desc = Object.getOwnPropertyDescriptor(AudioNode.prototype, i);
if(!desc){
desc = Object.getOwnPropertyDescriptor(this.audioNode.__proto__, i);
}
var props = {};
if(desc.get){
props.get = ((i) => this.audioNode[i]).bind(null, i);
}
if(desc.set){
props.set = ((i, v) => { this.audioNode[i] = v; }).bind(null, i);
}
props.enumerable = desc.enumerable;
props.configurable = desc.configurable;
Object.defineProperty(this, i,props);
props.name = i;
props.node = this;
this.params.push(props);
}
}
}
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.temp = {};
this.statusPlay = STATUS_PLAY_NOT_PLAYED;// not played.
}
// 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.from === n.from && con.to === n.to){
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_,param:0};
}
if(to_ instanceof AudioNode_){
to_ = {node:to_,param:0};
}
if(to_ instanceof AudioParam_){
to_ = {node:to_.audioNode_,param:to_};
}
// 存在チェック
for (var i = 0, l = AudioNode_.audioConnections.length; i < l; ++i) {
var c = AudioNode_.audioConnections[i];
if (c.from.node === from_.node
&& c.from.param === from_.param
&& c.to.node === to_.node
&& c.to.param === to_.param
)
{
return;
// 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 = [];
"use strict";
describe('AudioNodeTest', function () {
var ctx = new AudioContext();
var osc, gain, filter, out, osc2, splitter, merger;
beforeEach(function () {});
it("AudioNode_追加", function () {
osc = AudioNode_.create(ctx.createOscillator());
osc.x = 100;
osc.y = 200;
gain = AudioNode_.create(ctx.createGain());
gain.x = 400;
gain.y = 200;
filter = AudioNode_.create(ctx.createBiquadFilter());
filter.x = 250;
filter.y = 330;
out = AudioNode_.create(ctx.destination);
out.x = 750;
out.y = 300;
osc2 = AudioNode_.create(ctx.createOscillator());
osc2.x = 100;
osc2.y = 600;
splitter = AudioNode_.create(ctx.createChannelSplitter());
splitter.x = 250;
splitter.y = 600;
merger = AudioNode_.create(ctx.createChannelMerger());
merger.x = 500;
merger.y = 600;
expect(AudioNode_.audioNodes.length).toEqual(7);
});
it('コネクション追加後チェック', function () {
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);
expect(AudioNode_.audioConnections.length).toEqual(12);
});
it('コネクション削除', function () {
AudioNode_.remove(osc);
expect(AudioNode_.audioNodes.length).toEqual(6);
expect(AudioNode_.audioConnections.length).toEqual(10);
});
it('フィルター削除後チェック', function () {
AudioNode_.remove(filter);
expect(AudioNode_.audioNodes.length).toEqual(5);
expect(AudioNode_.audioConnections.length).toEqual(9);
expect((function () {
var ret = 0;
AudioNode_.audioConnections.forEach(function (d) {
if (d.from.node === filter || d.to.node === filter) {
++ret;
}
});
return ret;
})()).toEqual(0);
});
it('描画してみる', function () {
// osc.audioNode.type = 'sawtooth';
osc.type = 'sawtooth';
console.log(osc.type);
osc.frequency.value = 440;
// osc.start();
// osc.stop(ctx.currentTime + 0.1);
initDraw();
draw();
expect(true).toBe(true);
});
});
"use strict";
describe('AudioNodeTest', () => {
var ctx = new AudioContext();
var osc, gain, filter, out, osc2, splitter, merger;
beforeEach(() => {
});
it("AudioNode_追加", () => {
osc = AudioNode_.create(ctx.createOscillator());
osc.x = 100;
osc.y = 200;
gain = AudioNode_.create(ctx.createGain());
gain.x = 400;
gain.y = 200;
filter = AudioNode_.create(ctx.createBiquadFilter());
filter.x = 250;
filter.y = 330;
out = AudioNode_.create(ctx.destination);
out.x = 750;
out.y = 300;
osc2 = AudioNode_.create(ctx.createOscillator());
osc2.x = 100;
osc2.y = 600;
splitter = AudioNode_.create(ctx.createChannelSplitter());
splitter.x = 250;
splitter.y = 600;
merger = AudioNode_.create(ctx.createChannelMerger());
merger.x = 500;
merger.y = 600;
expect(AudioNode_.audioNodes.length).toEqual(7);
});
it('コネクション追加後チェック', () => {
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);
expect(AudioNode_.audioConnections.length).toEqual(12);
});
it('コネクション削除', () => {
AudioNode_.remove(osc);
expect(AudioNode_.audioNodes.length).toEqual(6);
expect(AudioNode_.audioConnections.length).toEqual(10);
});
it('フィルター削除後チェック', () => {
AudioNode_.remove(filter);
expect(AudioNode_.audioNodes.length).toEqual(5);
expect(AudioNode_.audioConnections.length).toEqual(9);
expect((() => {
var ret = 0;
AudioNode_.audioConnections.forEach((d) => {
if (d.from.node === filter || d.to.node === filter) {
++ret;
}
});
return ret;
})()).toEqual(0);
});
it('描画してみる',()=>{
// osc.audioNode.type = 'sawtooth';
osc.type = 'sawtooth';
console.log(osc.type);
osc.frequency.value = 440;
// osc.start();
// osc.stop(ctx.currentTime + 0.1);
initDraw();
draw();
expect(true).toBe(true);
});
});
'use strict';
var svg;
//aa
var nodeGroup, lineGroup;
var drag;
var dragOut;
var dragPanel;
var mouseClickNode;
var mouseOverNode;
var line;
var audioNodeCreators = [];
// Drawの初期化
function initUI() {
// AudioNodeドラッグ用
drag = d3.behavior.drag().origin(function (d) {
return d;
}).on('dragstart', function (d) {
if (d3.event.sourceEvent.ctrlKey || d3.event.sourceEvent.shiftKey) {
this.dispatchEvent(new Event('mouseup'));
return false;
}
d.temp.x = d.x;
d.temp.y = d.y;
d3.select(this.parentNode).append('rect').attr({ id: 'drag', width: d.width, height: d.height, x: 0, y: 0, 'class': 'audioNodeDrag' });
}).on("drag", function (d) {
if (d3.event.sourceEvent.ctrlKey || d3.event.sourceEvent.shiftKey) {
return;
}
d.temp.x += d3.event.dx;
d.temp.y += d3.event.dy;
//d3.select(this).attr('transform', 'translate(' + (d.x - d.width / 2) + ',' + (d.y - d.height / 2) + ')');
//draw();
var dragCursol = d3.select(this.parentNode).select('rect#drag');
var x = parseFloat(dragCursol.attr('x')) + d3.event.dx;
var y = parseFloat(dragCursol.attr('y')) + d3.event.dy;
dragCursol.attr({ x: x, y: y });
}).on('dragend', function (d) {
if (d3.event.sourceEvent.ctrlKey || d3.event.sourceEvent.shiftKey) {
return;
}
var dragCursol = d3.select(this.parentNode).select('rect#drag');
var x = parseFloat(dragCursol.attr('x'));
var y = parseFloat(dragCursol.attr('y'));
d.x = d.temp.x;
d.y = d.temp.y;
dragCursol.remove();
draw();
});
// ノード間接続用 drag
dragOut = d3.behavior.drag().origin(function (d) {
return d;
}).on('dragstart', function (d) {
var x1 = d.node.x + d.node.width / 2,
y1;
if (d.index) {
y1 = d.node.y - d.node.height / 2 + d.node.outputStartY + d.index * 20;
} else {
y1 = d.node.y - d.node.height / 2 + d.node.outputStartY;
}
d.x1 = x1, d.y1 = y1;
d.x2 = x1, d.y2 = y1;
var pos = makePos(x1, y1, d.x2, d.y2);
d.line = svg.append('g').datum(d).append('path').attr({ 'd': line(pos), 'class': 'line-drag' });
}).on("drag", function (d) {
d.x2 += d3.event.dx;
d.y2 += d3.event.dy;
d.line.attr('d', line(makePos(d.x1, d.y1, d.x2, d.y2)));
}).on("dragend", function (d) {
var targetX = d.x2;
var targetY = d.y2;
// inputもしくはparamに到達しているか
// input
var connected = false;
var inputs = d3.selectAll('.input')[0];
for (var i = 0, l = inputs.length; i < l; ++i) {
var elm = inputs[i];
var bbox = elm.getBBox();
var node = elm.__data__.node;
var left = node.x - node.width / 2 + bbox.x,
_top = node.y - node.height / 2 + bbox.y,
right = node.x - node.width / 2 + bbox.x + bbox.width,
bottom = node.y - node.height / 2 + bbox.y + bbox.height;
if (targetX >= left && targetX <= right && targetY >= _top && targetY <= bottom) {
console.log('hit', node.name, d);
var from_ = { node: d.node, param: d.index };
var to_ = { node: node, param: elm.__data__.index };
AudioNode_.connect(from_, to_);
//AudioNode_.connect();
draw();
connected = true;
break;
}
}
if (!connected) {
// AudioParam
var params = d3.selectAll('.param')[0];
for (var i = 0, l = params.length; i < l; ++i) {
var elm = params[i];
var bbox = elm.getBBox();
var param = elm.__data__;
var node = param.audioNode_;
var left = node.x - node.width / 2 + bbox.x,
_top2 = node.y - node.height / 2 + bbox.y,
right = node.x - node.width / 2 + bbox.x + bbox.width,
bottom = node.y - node.height / 2 + bbox.y + bbox.height;
if (targetX >= left && targetX <= right && targetY >= _top2 && targetY <= bottom) {
AudioNode_.connect({ node: d.node, param: d.index }, { node: node, param: param });
draw();
break;
}
}
}
// lineプレビューの削除
d.line.remove();
delete d.line;
});
// パネル用Dragその他
dragPanel = d3.behavior.drag()
// .origin(function(d){ return d;})
.on('dragstart', function (d) {
var sel = d3.select(this.parentNode);
d3.select('#content').insert('div').attr({ id: 'prop-dummy',
'class': 'prop-panel prop-dummy' }).style({
left: sel.style('left'),
top: sel.style('top')
});
}).on("drag", function (d) {
var dummy = d3.select('#prop-dummy');
var x = parseFloat(dummy.style('left')) + d3.event.dx;
var y = parseFloat(dummy.style('top')) + d3.event.dy;
dummy.style({ 'left': x + 'px', 'top': y + 'px' });
}).on('dragend', function (d) {
var sel = d3.select(this.parentNode);
var dummy = d3.select('#prop-dummy');
sel.style({ 'left': dummy.style('left'), 'top': dummy.style('top') });
dummy.remove();
});
d3.select('#panel-header').call(dragPanel);
d3.select('#panel-close').on('click', function () {
d3.select('#prop-panel').style('visibility', 'hidden');d3.event.returnValue = false;d3.event.preventDefault();
});
// node間接続line描画関数
line = d3.svg.line().x(function (d) {
return d.x;
}).y(function (d) {
return d.y;
}).interpolate('basis');
// DOMにsvgエレメントを挿入
svg = d3.select('#content').append('svg').attr({ 'width': window.innerWidth, 'height': window.innerHeight });
// ノードが入るグループ
nodeGroup = svg.append('g');
// ラインが入るグループ
lineGroup = svg.append('g');
// body属性に挿入
audioNodeCreators = [{ name: 'Gain', create: ctx.createGain.bind(ctx) }, { name: 'Delay', create: ctx.createDelay.bind(ctx) }, { name: 'AudioBufferSource', create: ctx.createBufferSource.bind(ctx) }, { name: 'MediaElementAudioSource', create: ctx.createMediaElementSource.bind(ctx) }, { name: 'Panner', create: ctx.createPanner.bind(ctx) }, { name: 'Convolver', create: ctx.createConvolver.bind(ctx) }, { name: 'Analyser', create: ctx.createAnalyser.bind(ctx) }, { name: 'ChannelSplitter', create: ctx.createChannelSplitter.bind(ctx) }, { name: 'ChannelMerger', create: ctx.createChannelMerger.bind(ctx) }, { name: 'DynamicsCompressor', create: ctx.createDynamicsCompressor.bind(ctx) }, { name: 'BiquadFilter', create: ctx.createBiquadFilter.bind(ctx) }, { name: 'Oscillator', create: ctx.createOscillator.bind(ctx) }, { name: 'MediaStreamAudioSource', create: ctx.createMediaStreamSource.bind(ctx) }, { name: 'WaveShaper', create: ctx.createWaveShaper.bind(ctx) }];
if (ctx.createMediaStreamDestination) {
audioNodeCreators.push({ name: 'MediaStreamAudioDestination',
create: ctx.createMediaStreamDestination.bind(ctx)
});
}
d3.select('#content').on('contextmenu', function () {
showAudioNodePanel(this);
});
}
// 描画
function draw() {
// AudioNodeの描画
var gd = nodeGroup.selectAll('g').data(AudioNode_.audioNodes, function (d) {
return d.id;
});
// 矩形の更新
gd.select('rect').attr({ 'width': function width(d) {
return d.width;
}, 'height': function height(d) {
return d.height;
} });
// AudioNode矩形グループ
var g = gd.enter().append('g');
// AudioNode矩形グループの座標位置セット
gd.attr('transform', function (d) {
return 'translate(' + (d.x - d.width / 2) + ',' + (d.y - d.height / 2) + ')';
});
// AudioNode矩形
g.append('rect').call(drag).attr({ 'width': function width(d) {
return d.width;
}, 'height': function height(d) {
return d.height;
}, 'class': 'audioNode' }).classed('play', function (d) {
return d.statusPlay === STATUS_PLAY_PLAYING;
}).on('contextmenu', function (d) {
showPanel.bind(this, d)();
d3.event.preventDefault();
d3.event.returnValue = false;
}).on('click.remove', function (d) {
if (d3.event.shiftKey) {
AudioNode_.remove(d);
draw();
}
d3.event.returnValue = false;
d3.event.preventDefault();
}).filter(function (d) {
return d.audioNode instanceof OscillatorNode || d.audioNode instanceof AudioBufferSourceNode || d.audioNode instanceof MediaElementAudioSourceNode;
}).on('click', function (d) {
console.log(d3.event);
d3.event.returnValue = false;
d3.event.preventDefault();
if (!d3.event.ctrlKey) {
return;
}
var sel = d3.select(this);
if (d.statusPlay === STATUS_PLAY_PLAYING) {
d.statusPlay = STATUS_PLAY_PLAYED;
sel.classed('play', false);
d.stop(0);
} else if (d.statusPlay !== STATUS_PLAY_PLAYED) {
d.start(0);
d.statusPlay = STATUS_PLAY_PLAYING;
sel.classed('play', true);
} else {
alert('一度停止すると再生できません。');
}
}).call(tooltip('Ctrl + Click で再生・停止'));
;
// AudioNodeのラベル
g.append('text').attr({ x: 0, y: -10, 'class': 'label' }).text(function (d) {
return d.name;
});
// AudioParamの表示
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.filter(function (d) {
return d.numberOfOutputs > 0;
}).each(function (d) {
var sel = d3.select(this).append('g');
if (!d.temp.outs || d.temp.outs && d.temp.outs.length < d.numberOfOutputs) {
d.temp.outs = [];
for (var i = 0; i < d.numberOfOutputs; ++i) {
d.temp.outs.push({ node: d, index: i });
}
}
var sel1 = sel.selectAll('g');
var seld = sel1.data(d.temp.outs);
seld.enter().append('g').append('rect').attr({ x: d.width - pointSize / 2, y: function y(d1) {
return d.outputStartY + d1.index * 20 - pointSize / 2;
}, width: pointSize, height: pointSize, 'class': 'output' }).call(dragOut);
seld.exit().remove();
});
// 入力表示
gd.filter(function (d) {
return d.numberOfInputs > 0;
}).each(function (d) {
var sel = d3.select(this).append('g');
if (!d.temp.ins || d.temp.ins && d.temp.ins.length < d.numberOfInputs) {
d.temp.ins = [];
for (var i = 0; i < d.numberOfInputs; ++i) {
d.temp.ins.push({ node: d, index: i });
}
}
var sel1 = sel.selectAll('g');
var seld = sel1.data(d.temp.ins);
seld.enter().append('g').append('rect').attr({ x: -pointSize / 2, y: function y(d1) {
return d.inputStartY + d1.index * 20 - pointSize / 2;
}, width: pointSize, height: pointSize, 'class': 'input' }).on('mouseenter', function (d) {
mouseOverNode = { node: d.audioNode_, param: d };
}).on('mouseleave', function (d) {
if (mouseOverNode.node) {
if (mouseOverNode.node === d.audioNode_ && mouseOverNode.param === d) {
mouseOverNode = null;
}
}
});
seld.exit().remove();
});
// 不要なノードの削除
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 pos = makePos(x1, y1, x2, y2);
path.attr({ 'd': line(pos), 'class': 'line' });
path.on('click', function (d) {
if (d3.event.shiftKey) {
AudioNode_.disconnect(d.from, d.to);
d3.event.returnValue = false;
draw();
}
}).call(tooltip('Shift + clickで削除'));
});
ld.exit().remove();
}
// 簡易tooltip表示
function tooltip(mes) {
return function (d) {
this.on('mouseenter', function () {
svg.append('text').attr({ 'class': 'tip', x: d3.event.x + 20, y: d3.event.y - 20 }).text(mes);
}).on('mouseleave', function () {
svg.selectAll('.tip').remove();
});
};
}
// 接続線の座標生成
function makePos(x1, y1, x2, y2) {
return [{ 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 }];
}
// プロパティパネルの表示
function showPanel(d) {
d3.select('#panel-header').text(d.name);
var panel = d3.select('#prop-panel').select('#panel-content');
panel.select('table').remove();
var table = panel.append('table');
// var th = table.append('thead')
// .append('tr');
// th.append('th').text('パラメータ');
// th.append('th').text('値');
var tbody = table.append('tbody').selectAll('tr').data(d.params);
var tr = tbody.enter().append('tr');
tr.append('td').text(function (d) {
return d.name;
});
tr.append('td').append('input').attr({ type: "text", value: function value(d) {
return d.get();
}, readonly: function readonly(d) {
return d.set ? null : 'readonly';
} }).on('change', function (d) {
var sel = d3.select(this);
var value = this.value;
console.log(value);
var vn = parseFloat(value);
if (isNaN(vn)) {
d.set(value);
} else {
d.set(vn);
}
});
d3.select('#prop-panel').style('visibility', 'visible');
d3.event.returnValue = false;
d3.event.preventDefault();
d3.event.cancelBubble = true;
}
// ノード挿入パネルの表示
function showAudioNodePanel(d) {
d3.select('#panel-header').text('AudioNodeの挿入');
var panel = d3.select('#prop-panel').select('#panel-content');
panel.select('table').remove();
var table = panel.append('table');
var tbody = table.append('tbody').selectAll('tr').data(audioNodeCreators);
var tr = tbody.enter().append('tr').append('td').text(function (d) {
return d.name;
}).on('click', function (dt) {
console.log(d3.event);
var node = AudioNode_.create(dt.create());
node.x = d3.event.clientX;
node.y = d3.event.clientY;
draw();
d3.select('#prop-panel').style('visibility', 'hidden');
});
d3.select('#prop-panel').style({
'visibility': 'visible',
'left': d3.event.clientX + 'px',
'top': d3.event.clientY + 'px'
});
d3.event.returnValue = false;
d3.event.preventDefault();
}
var svg;
//aa
var nodeGroup, lineGroup;
var drag;
var dragOut;
var dragPanel;
var mouseClickNode;
var mouseOverNode;
var line;
var audioNodeCreators = [];
// Drawの初期化
function initUI(){
// AudioNodeドラッグ用
drag = d3.behavior.drag()
.origin(function (d) { return d; })
.on('dragstart',function(d){
if(d3.event.sourceEvent.ctrlKey || d3.event.sourceEvent.shiftKey){
this.dispatchEvent(new Event('mouseup'));
return false;
}
d.temp.x = d.x;
d.temp.y = d.y;
d3.select(this.parentNode)
.append('rect')
.attr({id:'drag',width:d.width,height:d.height,x:0,y:0,'class':'audioNodeDrag'} );
})
.on("drag", function (d) {
if(d3.event.sourceEvent.ctrlKey || d3.event.sourceEvent.shiftKey){
return;
}
d.temp.x += d3.event.dx;
d.temp.y += d3.event.dy;
//d3.select(this).attr('transform', 'translate(' + (d.x - d.width / 2) + ',' + (d.y - d.height / 2) + ')');
//draw();
var dragCursol = d3.select(this.parentNode)
.select('rect#drag');
var x = parseFloat(dragCursol.attr('x')) + d3.event.dx;
var y = parseFloat(dragCursol.attr('y')) + d3.event.dy;
dragCursol.attr({x:x,y:y});
})
.on('dragend',function(d){
if(d3.event.sourceEvent.ctrlKey || d3.event.sourceEvent.shiftKey){
return;
}
var dragCursol = d3.select(this.parentNode)
.select('rect#drag');
var x = parseFloat(dragCursol.attr('x'));
var y = parseFloat(dragCursol.attr('y'));
d.x = d.temp.x;
d.y = d.temp.y;
dragCursol.remove();
draw();
});
// ノード間接続用 drag
dragOut = d3.behavior.drag()
.origin(function (d) { return d; })
.on('dragstart',function(d){
var x1 = d.node.x + d.node.width / 2,y1;
if(d.index){
y1 = d.node.y - d.node.height /2 + d.node.outputStartY + d.index * 20;
} else {
y1 = d.node.y - d.node.height /2 + d.node.outputStartY;
}
d.x1 = x1,d.y1 = y1;
d.x2 = x1,d.y2 = y1;
var pos = makePos(x1,y1,d.x2,d.y2);
d.line = svg.append('g')
.datum(d)
.append('path')
.attr({'d':line(pos),'class':'line-drag'});
})
.on("drag", function (d) {
d.x2 += d3.event.dx;
d.y2 += d3.event.dy;
d.line.attr('d',line(makePos(d.x1,d.y1,d.x2,d.y2)));
})
.on("dragend", function (d) {
let targetX = d.x2;
let targetY = d.y2;
// inputもしくはparamに到達しているか
// input
let connected = false;
let inputs = d3.selectAll('.input')[0];
for(var i = 0,l = inputs.length;i < l;++i){
let elm = inputs[i];
let bbox = elm.getBBox();
let node = elm.__data__.node;
let left = node.x - node.width / 2 + bbox.x,
top = node.y - node.height / 2 + bbox.y,
right = node.x - node.width / 2 + bbox.x + bbox.width,
bottom = node.y - node.height / 2 + bbox.y + bbox.height;
if(targetX >= left && targetX <= right && targetY >= top && targetY <= bottom)
{
console.log('hit',node.name,d);
let from_ = {node:d.node,param:d.index};
let to_ = {node:node,param:elm.__data__.index};
AudioNode_.connect(from_,to_);
//AudioNode_.connect();
draw();
connected = true;
break;
}
}
if(!connected){
// AudioParam
var params = d3.selectAll('.param')[0];
for(var i = 0,l = params.length;i < l;++i)
{
let elm = params[i];
let bbox = elm.getBBox();
let param = elm.__data__;
let node = param.audioNode_;
let left = node.x - node.width / 2 + bbox.x,
top = node.y - node.height / 2 + bbox.y,
right = node.x - node.width / 2 + bbox.x + bbox.width,
bottom = node.y - node.height / 2 + bbox.y + bbox.height;
if(targetX >= left && targetX <= right && targetY >= top && targetY <= bottom)
{
AudioNode_.connect({node:d.node,param:d.index},{node:node,param:param});
draw();
break;
}
}
}
// lineプレビューの削除
d.line.remove();
delete d.line;
});
// パネル用Dragその他
dragPanel = d3.behavior.drag()
// .origin(function(d){ return d;})
.on('dragstart',function(d){
var sel = d3.select(this.parentNode);
d3.select('#content')
.insert('div')
.attr({id:'prop-dummy',
'class':'prop-panel prop-dummy'})
.style({
left:sel.style('left'),
top:sel.style('top')
});
})
.on("drag", function (d) {
var dummy = d3.select('#prop-dummy');
var x = parseFloat(dummy.style('left')) + d3.event.dx;
var y = parseFloat(dummy.style('top')) + d3.event.dy;
dummy.style({'left':x + 'px','top':y + 'px'});
})
.on('dragend',function(d){
var sel = d3.select(this.parentNode);
var dummy = d3.select('#prop-dummy');
sel.style(
{'left':dummy.style('left'),'top':dummy.style('top')}
);
dummy.remove();
});
d3.select('#panel-header').call(dragPanel);
d3.select('#panel-close')
.on('click',function(){d3.select('#prop-panel').style('visibility','hidden');d3.event.returnValue = false;d3.event.preventDefault();});
// node間接続line描画関数
line = d3.svg.line()
.x(function(d){return d.x})
.y(function(d){return d.y})
.interpolate('basis');
// DOMにsvgエレメントを挿入
svg = d3.select('#content')
.append('svg')
.attr({ 'width': window.innerWidth, 'height': window.innerHeight });
// ノードが入るグループ
nodeGroup = svg.append('g');
// ラインが入るグループ
lineGroup = svg.append('g');
// body属性に挿入
audioNodeCreators =
[
{name:'Gain',create:ctx.createGain.bind(ctx)},
{name:'Delay',create:ctx.createDelay.bind(ctx)},
{name:'AudioBufferSource',create:ctx.createBufferSource.bind(ctx)},
{name:'MediaElementAudioSource',create:ctx.createMediaElementSource.bind(ctx)},
{name:'Panner',create:ctx.createPanner.bind(ctx)},
{name:'Convolver',create:ctx.createConvolver.bind(ctx)},
{name:'Analyser',create:ctx.createAnalyser.bind(ctx)},
{name:'ChannelSplitter',create:ctx.createChannelSplitter.bind(ctx)},
{name:'ChannelMerger',create:ctx.createChannelMerger.bind(ctx)},
{name:'DynamicsCompressor',create:ctx.createDynamicsCompressor.bind(ctx)},
{name:'BiquadFilter',create:ctx.createBiquadFilter.bind(ctx)},
{name:'Oscillator',create:ctx.createOscillator.bind(ctx)},
{name:'MediaStreamAudioSource',create:ctx.createMediaStreamSource.bind(ctx)},
{name:'WaveShaper',create:ctx.createWaveShaper.bind(ctx)}
];
if(ctx.createMediaStreamDestination){
audioNodeCreators.push({name:'MediaStreamAudioDestination',
create:ctx.createMediaStreamDestination.bind(ctx)
});
}
d3.select('#content')
.on('contextmenu',function(){
showAudioNodePanel(this);
});
}
// 描画
function draw() {
// AudioNodeの描画
var gd = nodeGroup.selectAll('g').
data(AudioNode_.audioNodes,function(d){return d.id;});
// 矩形の更新
gd.select('rect')
.attr({ 'width': (d)=> d.width, 'height': (d)=> d.height });
// AudioNode矩形グループ
var g = gd.enter()
.append('g');
// AudioNode矩形グループの座標位置セット
gd.attr('transform', function (d) { return 'translate(' + (d.x - d.width / 2) + ',' + (d.y - d.height / 2) + ')' });
// AudioNode矩形
g.append('rect')
.call(drag)
.attr({ 'width': (d)=> d.width, 'height': (d)=> d.height, 'class': 'audioNode' })
.classed('play',function(d){
return d.statusPlay === STATUS_PLAY_PLAYING;
})
.on('contextmenu',function(d){
showPanel.bind(this,d)();
d3.event.preventDefault();
d3.event.returnValue = false;
})
.on('click.remove',function(d){
if(d3.event.shiftKey){
AudioNode_.remove(d);
draw();
}
d3.event.returnValue = false;
d3.event.preventDefault();
})
.filter(function(d){
return d.audioNode instanceof OscillatorNode || d.audioNode instanceof AudioBufferSourceNode || d.audioNode instanceof MediaElementAudioSourceNode;
})
.on('click',function(d){
console.log(d3.event);
d3.event.returnValue = false;
d3.event.preventDefault();
if(!d3.event.ctrlKey){
return;
}
let sel = d3.select(this);
if(d.statusPlay === STATUS_PLAY_PLAYING){
d.statusPlay = STATUS_PLAY_PLAYED;
sel.classed('play',false);
d.stop(0);
} else if(d.statusPlay !== STATUS_PLAY_PLAYED){
d.start(0);
d.statusPlay = STATUS_PLAY_PLAYING;
sel.classed('play',true);
} else {
alert('一度停止すると再生できません。');
}
})
.call(tooltip('Ctrl + Click で再生・停止'));
;
// AudioNodeのラベル
g.append('text')
.attr({ x: 0, y: -10, 'class': 'label' })
.text(function (d) { return d.name; });
// AudioParamの表示
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.filter(function (d) {
return d.numberOfOutputs > 0;
})
.each(function(d){
var sel = d3.select(this).append('g');
if(!d.temp.outs || (d.temp.outs && (d.temp.outs.length < d.numberOfOutputs)))
{
d.temp.outs = [];
for(var i = 0;i < d.numberOfOutputs;++i){
d.temp.outs.push({node:d,index:i});
}
}
var sel1 = sel.selectAll('g');
var seld = sel1.data(d.temp.outs);
seld.enter()
.append('g')
.append('rect')
.attr({ x: d.width - pointSize / 2, y: (d1)=> (d.outputStartY + d1.index * 20 - pointSize / 2), width: pointSize, height: pointSize, 'class': 'output' })
.call(dragOut);
seld.exit().remove();
});
// 入力表示
gd
.filter(function (d) { return d.numberOfInputs > 0; })
.each(function(d){
var sel = d3.select(this).append('g');
if(!d.temp.ins || (d.temp.ins && (d.temp.ins.length < d.numberOfInputs)))
{
d.temp.ins = [];
for(var i = 0;i < d.numberOfInputs;++i){
d.temp.ins.push({node:d,index:i});
}
}
var sel1 = sel.selectAll('g');
var seld = sel1.data(d.temp.ins);
seld.enter()
.append('g')
.append('rect')
.attr({ x: - pointSize / 2, y: (d1)=> (d.inputStartY + d1.index * 20 - pointSize / 2), width: pointSize, height: pointSize, 'class': 'input' })
.on('mouseenter',function(d){
mouseOverNode = {node:d.audioNode_,param:d};
})
.on('mouseleave',function(d){
if(mouseOverNode.node){
if(mouseOverNode.node === d.audioNode_ && mouseOverNode.param === d){
mouseOverNode = null;
}
}
});
seld.exit().remove();
});
// 不要なノードの削除
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 pos = makePos(x1,y1,x2,y2);
path.attr({'d':line(pos),'class':'line'});
path.on('click',function(d){
if(d3.event.shiftKey){
AudioNode_.disconnect(d.from,d.to);
d3.event.returnValue = false;
draw();
}
}).call(tooltip('Shift + clickで削除'));
});
ld.exit().remove();
}
// 簡易tooltip表示
function tooltip(mes)
{
return function(d){
this
.on('mouseenter',function(){
svg.append('text')
.attr({'class':'tip',x:d3.event.x + 20 ,y:d3.event.y - 20})
.text(mes);
})
.on('mouseleave',function(){
svg.selectAll('.tip').remove();
})
};
}
// 接続線の座標生成
function makePos(x1,y1,x2,y2){
return [
{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}
];
}
// プロパティパネルの表示
function showPanel(d){
d3.select('#panel-header').text(d.name);
var panel = d3.select('#prop-panel').select('#panel-content');
panel.select('table').remove();
var table = panel.append('table');
// var th = table.append('thead')
// .append('tr');
// th.append('th').text('パラメータ');
// th.append('th').text('値');
var tbody = table.append('tbody').selectAll('tr').data(d.params);
var tr = tbody.enter()
.append('tr');
tr.append('td')
.text((d)=>d.name);
tr.append('td')
.append('input')
.attr({type:"text",value:(d)=>d.get(),readonly:(d)=>d.set?null:'readonly'})
.on('change',function(d){
let sel = d3.select(this);
let value = this.value;
console.log(value);
let vn = parseFloat(value);
if(isNaN(vn)){
d.set(value);
} else {
d.set(vn);
}
});
d3.select('#prop-panel').style('visibility','visible');
d3.event.returnValue = false;
d3.event.preventDefault();
d3.event.cancelBubble = true;
}
// ノード挿入パネルの表示
function showAudioNodePanel(d){
d3.select('#panel-header').text('AudioNodeの挿入');
var panel = d3.select('#prop-panel').select('#panel-content');
panel.select('table').remove();
var table = panel.append('table');
var tbody = table.append('tbody').selectAll('tr').data(audioNodeCreators);
var tr = tbody.enter()
.append('tr')
.append('td')
.text((d)=>d.name)
.on('click',function(dt){
console.log(d3.event);
var node = AudioNode_.create(dt.create());
node.x = d3.event.clientX;
node.y = d3.event.clientY;
draw();
d3.select('#prop-panel').style('visibility','hidden');
});
d3.select('#prop-panel').style(
{
'visibility':'visible',
'left': d3.event.clientX + 'px',
'top':d3.event.clientY + 'px'
});
d3.event.returnValue = false;
d3.event.preventDefault();
}
"use strict";
var nodeHeight = 50;
var nodeWidth = 100;
var pointSize = 16;
"use strict";
const nodeHeight = 50;
const nodeWidth = 100;
const pointSize = 16;
<!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="./global.js"></script>
<script type="text/javascript" src="./script.js"></script>
<script src="./draw.js"></script>
<script type="text/javascript" src="./audioNode_.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>
<div id="content">
<div id="prop-panel" class="prop-panel">
<div class="panel-close" id="panel-close">x</div>
<header id="panel-header"></header>
<article id="panel-content"></article>
</div>
</div>
</body>
</html>
{
"compilerOptions": {
"target": "ES6"
}
}
{
"name": "webaudiomoduler",
"version": "1.0.0",
"description": "Web Audio APIのテストです。",
"main": "index.html",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+ssh://git@gist.github.com/3a854cb6ab7951c7de5b.git"
},
"author": "S.F.",
"license": "MIT",
"bugs": {
"url": "https://gist.github.com/3a854cb6ab7951c7de5b"
},
"homepage": "https://gist.github.com/3a854cb6ab7951c7de5b"
}
"use strict";
var ctx;
window.onload = function () {
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;
//
// var osc3 = AudioNode_.create(ctx.createOscillator());
// osc3.x = 100;
// osc3.y = 700;
// 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]);
initUI();
draw();
// window.setTimeout(() => {
// //AudioNode_.remove(osc);
// //AudioNode_.remove(out);
// console.log(AudioNode_.audioNodes.length);
// console.log(AudioNode_.audioConnections.length);
// draw();
// }, 2000);
//
// draw();
};
"use strict";
var ctx;
window.onload = () => {
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;
//
// var osc3 = AudioNode_.create(ctx.createOscillator());
// osc3.x = 100;
// osc3.y = 700;
// 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]);
initUI();
draw();
// window.setTimeout(() => {
// //AudioNode_.remove(osc);
// //AudioNode_.remove(out);
// console.log(AudioNode_.audioNodes.length);
// console.log(AudioNode_.audioConnections.length);
// draw();
// }, 2000);
//
// draw();
};
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Jasmine Spec Runner v2.3.4</title>
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.3.4/jasmine.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.3.4/jasmine.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.3.4/jasmine-html.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.3.4/boot.js"></script>
<!-- include source files here... -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.js" ></script>
<script src="./global.js"></script>
<script src="./audioNode_.js"></script>
<script src="./draw.js"></script>
<!-- include spec files here... -->
<script src="audioNodeTest.js"></script>
</head>
<body>
</body>
</html>
*{
font-family: "Hiragino Kaku Gothic ProN","メイリオ", sans-serif;
}
body{
background:white;
color: black;
}
.audioNode {
fill:#BBB;
stroke-width:1px;
stroke:black;
color:black;
}
.audioNode:hover {
stroke:red;
}
.play{
fill:#f99;
}
.audioNodeDrag {
cursor: move;
opacity:0.25;
fill:#BBB;
stroke-width:1px;
stroke:black;
color:black;
}
.param {
fill:red;
}
.param:hover{
fill:orange
}
.label {
stroke:black;
font-size:8px;
user-select: none;
-moz-user-select: none; /* Firefox */
-webkit-user-select: none; /* Safari、Chromeなど */
-ms-user-select: none; /* IE10かららしい */
}
.input {
fill:red;
}
.input:hover{
fill:orange
}
.output{
fill:blue;
}
.output:hover{
fill:lightblue
}
.line {
stroke: black;
stroke-width: 4px;
stroke-linecap: round;
fill:none;
}
.line:hover{
stroke:red;
}
.line-drag {
opacity: 0.25;
stroke: black;
stroke-width: 4px;
stroke-linecap: round;
fill:none;
cursor:crosshair;
}
.tip {
opacity:0.5;
font-size:10pt;
stroke:red;
}
#content {
margin:0;
padding: 0;
position: relative;
z-index: 0;
}
.prop-panel{
opacity: 0.85;
position: absolute;
visibility: hidden;
display:block;
background:#eee;
color:black;
left:100px;
top:100px;
width:300px;
min-height:300px;
overflow-y:none;
overflow-x:none;
box-shadow: 8px 8px 8px rgba(0,0,0,0.4);
border-radius:4px 4px 4px 4px;
}
.prop-dummy{
visibility: visible;
opacity: 0.5;
z-index: 0;
}
#panel-header {
text-align:center;
background:gray;
}
#panel-content table {
width:95%;
margin:auto;
overflow-y:auto;
overflow-x:none;
border-collapse: collapse;
}
#panel-content table td,#panel-content table th {
border-bottom:1px solid black;
font-size: 10px;
}
#panel-content table td:hover {
background: lightcoral;
cursor: pointer;
}
#panel-content table td input[type="text"]{
border:none;
background: #333;
color:white;
width:90%;
height:85%;
padding:2px;
}
#panel-content table td input[type="text"][readonly="readonly"]{
background:none;
color:black;
}
#panel-content table th {
background:darkgray;
}
.panel-close {
display:block;
position:absolute;
top:0px;
padding:0px;
margin:0px;
right:5px;
width:15px;
height:20px;
text-align: center;
}
.panel-close:hover{
color:red;
cursor:default;
}
svg {
z-index: 10;
}
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.