是否可以使用Web Audio API实现自定义AudioNode?
我想构建一个包含其他几个节点的节点(ChannelSplitters和AnalyserNodes)。理想情况下,我将能够像任何其他AudioNode一样连接到此自定义节点。例如,
var customNode = new CustomNode();
mediaStreamSource = context.createMediaStreamSource(userMedia);
// This will not work, as I need to know what to implement in CustomNode
mediaStreamSource.connect(customNode);
customNode.connect(context.destination);
根据MDN documentation,AudioNode实现EventTarget interface。是否所有这些都用于改变音频?如果是这样,如何以处理音频的方式实现此接口?
答案 0 :(得分:5)
This article似乎有一种方法可以满足您的需求。
基本前提:
function MyCustomNode(){
this.input = audioContext.createGainNode();
var output = audioContext.createGainNode();
this.connect = function(target){
output.connect(target);
};
}
示例:
function AudioBus(){
this.input = audioContext.createGainNode();
var output = audioContext.createGainNode();
var custom = new MyCustomNode();
this.input.connect(custom);
custom.connect(output);
this.connect = function(target){
output.connect(target);
};
}
//create some native oscillators and our custom audio bus
var bus = new AudioBus(),
instrument1 = audioContext.createOscillator(),
instrument2 = audioContext.createOscillator(),
instrument3 = audioContext.createOscillator();
//connect our instruments to the same bus
instrument1.connect(bus.input);
instrument2.connect(bus.input);
instrument3.connect(bus.input);
bus.connect(audioContext.destination);
修改:问题可能是Creating a custom echo node with web-audio的可能重复,但我相信您要找的答案是the one from @MattDiamond。它不是一个漂亮的解决方案,但它似乎完成了工作:
function FeedbackDelayNode(context, delay, feedback){
this.delayTime.value = delay;
this.gainNode = context.createGainNode();
this.gainNode.gain.value = feedback;
this.connect(this.gainNode);
this.gainNode.connect(this);
}
function FeedbackDelayFactory(context, delayTime, feedback){
var delay = context.createDelayNode(delayTime + 1);
FeedbackDelayNode.call(delay, context, delayTime, feedback);
return delay;
}
AudioContext.prototype.createFeedbackDelay = function(delay, feedback){
return FeedbackDelayFactory(this, delay, feedback);
};
答案 1 :(得分:4)
AudioNode类文件
"use strict";
var AudioNode = global.AudioNode;
var AudioNode$connect;
var AudioNode$disconnect;
function connect() {
var args = [].slice.call(arguments);
if (args.length && typeof args[0].__connectFrom === "function") {
args[0].__connectFrom.apply(args[0], [ this ].concat(args.slice(1)));
} else {
AudioNode$connect.apply(this, args);
}
}
function disconnect() {
var args = [].slice.call(arguments);
if (args.length && typeof args[0].__disconnectFrom === "function") {
args[0].__disconnectFrom.apply(args[0], [ this ].concat(args.slice(1)));
} else {
AudioNode$disconnect.apply(this, args);
}
}
function use() {
if (typeof AudioNode !== "undefined" && AudioNode.prototype.connect !== connect) {
AudioNode$connect = AudioNode.prototype.connect;
AudioNode$disconnect = AudioNode.prototype.disconnect;
AudioNode.prototype.connect = connect;
AudioNode.prototype.disconnect = disconnect;
}
}
function unuse() {
if (typeof AudioNode !== "undefined" && AudioNode.prototype.connect === connect) {
AudioNode.prototype.connect = AudioNode$connect;
AudioNode.prototype.disconnect = AudioNode$disconnect;
}
}
module.exports = {
use: use,
unuse: unuse,
};
AudioNode测试文件
"use strict";
var assert = require("power-assert");
var PowerAudioNode = require("../");
function CustomAudioNode(audioContext) {
this.audioContext = audioContext;
this.gain1 = audioContext.createGain();
this.gain2 = audioContext.createGain();
this.inlet = this.gain1;
this.outlet = this.gain2;
}
CustomAudioNode.prototype.connect = function() {
this.gain1.connect(this.gain2);
this.gain2.connect.apply(this.gain2, arguments);
};
CustomAudioNode.prototype.disconnect = function() {
this.gain1.disconnect();
this.gain2.disconnect.apply(this.gain2, arguments);
};
CustomAudioNode.prototype.__connectFrom = function(source) {
source.connect(this.gain1);
};
CustomAudioNode.prototype.__disconnectFrom = function(source) {
source.disconnect();
};
describe("PowerAudioNode", function() {
describe("use(): void", function() {
before(PowerAudioNode.use);
before(PowerAudioNode.use);
it("works", function() {
var audioContext = new global.AudioContext();
var oscillator = audioContext.createOscillator();
var customAudioNode = new CustomAudioNode(audioContext);
var compressor = audioContext.createDynamicsCompressor();
oscillator.connect(customAudioNode);
customAudioNode.connect(compressor);
compressor.connect(audioContext.destination);
assert(audioContext.destination.$isConnectedFrom(compressor));
assert(compressor.$isConnectedFrom(customAudioNode.outlet));
assert(customAudioNode.inlet.$isConnectedFrom(oscillator));
oscillator.disconnect(customAudioNode);
customAudioNode.disconnect();
compressor.disconnect();
assert(!audioContext.destination.$isConnectedFrom(compressor));
assert(!compressor.$isConnectedFrom(customAudioNode.outlet));
assert(!customAudioNode.inlet.$isConnectedFrom(oscillator));
});
});
describe("unuse(): void", function() {
before(PowerAudioNode.unuse);
it("works", function() {
var audioContext = new global.AudioContext();
var oscillator = audioContext.createOscillator();
var customAudioNode = new CustomAudioNode(audioContext);
var compressor = audioContext.createDynamicsCompressor();
assert.throws(function() {
oscillator.connect(customAudioNode);
});
customAudioNode.connect(compressor);
compressor.connect(audioContext.destination);
assert(audioContext.destination.$isConnectedFrom(compressor));
assert(compressor.$isConnectedFrom(customAudioNode.outlet));
assert(!customAudioNode.inlet.$isConnectedFrom(oscillator));
oscillator.disconnect();
customAudioNode.disconnect();
compressor.disconnect();
assert(!audioContext.destination.$isConnectedFrom(compressor));
assert(!compressor.$isConnectedFrom(customAudioNode.outlet));
assert(!customAudioNode.inlet.$isConnectedFrom(oscillator));
});
});
});