我正在尝试使音乐播放器具有实时改变输出设备(耳机或扬声器)的能力。我有工作功能,以改变目标设备与setSinkId。我也有工作Biquad滤波器(低通,高通…)和音频处理器,以产生增益水平条(下图)。滤波器滑块和增益条
部分代码(很多)。
设置输出设备:
if (audiooutput_ch1active == 0) {
if (typeof audiooutput_headphonesid !== "undefined") {
audiooutput_ch1active = 1;
await audio_ch1.setSinkId(audiooutput_headphonesid);
return;
}
} else if (audiooutput_ch1active == 1) {
if (typeof audiooutput_speakersid !== "undefined") {
audiooutput_ch1active = 0;
await audio_ch1.setSinkId(audiooutput_speakersid);
return;
}
}
定义过滤器:
var filter_ch1_lowpass = audioCtx_ch1.createBiquadFilter();
filter_ch1_lowpass.type = "lowpass";
filter_ch1_lowpass.frequency.value = 12000;
var filter_ch1_highpass = audioCtx_ch1.createBiquadFilter();
filter_ch1_highpass.type = "highpass";
filter_ch1_highpass.frequency.value = 0;
var filter_ch1_lowshelf = audioCtx_ch1.createBiquadFilter();
filter_ch1_lowshelf.type = "lowshelf";
filter_ch1_lowshelf.frequency.value = 100;
filter_ch1_lowshelf.gain.value = 0;
连接过滤器和处理器:
audio_ch1.src = path;
source_ch1 = audioCtx_ch1.createMediaElementSource(audio_ch1);
source_ch1.connect(filter_ch1_lowpass);
filter_ch1_lowpass.connect(filter_ch1_highpass);
filter_ch1_highpass.connect(filter_ch1_lowshelf);
filter_ch1_lowshelf.connect(processor_ch1);
filter_ch1_lowshelf.connect(audioCtx_ch1.destination);
processor_ch1.connect(filter_ch1_lowshelf);
当我将过滤器连接到我的音频上下文时,我不能使用setSinkId - Error: Uncaught (in promise) DOMException:该操作无法执行并被中止
当我跳过连接过滤器的代码时,setSinkId可以正常工作。
setSinkId不支持音频上下文过滤器吗?
我是JavaScript音频新手。
在您将<audio>
的输出路由到音频上下文之后,您正在设置<audio>
元素的接收器。<audio>
已经失去了对其输出的控制,现在是音频上下文控制了它,因此你不能再设置<audio>
元素的接收器了。
你可以在不使用过滤器的时候调用这个方法,这是Web Audio API中一个很大的Chrome bug的一部分,我相信他们正在积极地工作:基本上,他们只在音频图形连接到目的地时才真正创建连接。
你可以通过从你的<audio>
创建一个MediaStream
来避免这个错误,通过调用它的captureStream()
方法,然后从MediaStream
对象连接一个MediaStreamAudioSourceNode
到你的音频图形。然而,你仍然只设置输入<audio>
的接收器,而不是你在音频上下文中修改的接收器。
相反,你可能想要的是直接设置音频上下文的接收器。
有一个积极的建议,在AudioContext
接口上添加一个setSinkId()
方法,最新的Chrome Canary确实暴露了它。然而,它仍然是一个建议,AFAICT只有金丝雀揭露它。所以在不久的将来你只需要输入
audioCtx_ch1.setSinkId(audiooutput_speakersid).catch(handlePossibleError);
但是现在,你需要做一些体操,首先创建一个MediaStreamAudioDestinationNode
,你将连接你的音频图形,将它的MediaStream
设置为另一个<audio>
元素的srcObject
,你将在其上称为setSinkId()
。
udio_ch1.src = path;
// might be better to use createMediaStreamSource(audio_ch1.captureStream()) anyway here
source_ch1 = audioCtx_ch1.createMediaElementSource(audio_ch1);
source_ch1.connect(filter_ch1_lowpass);
filter_ch1_lowpass.connect(filter_ch1_highpass);
filter_ch1_highpass.connect(filter_ch1_lowshelf);
filter_ch1_lowshelf.connect(processor_ch1);
processor_ch1.connect(filter_ch1_lowshelf);
const outputNode = audioCtx_ch1.createMediaStreamDestination();
filter_ch1_lowshelf.connect(outputNode);
const outputElem = new Audio();
outputElem.srcObject = outputNode.stream;
outputElem.setSinkId(audiooutput_speakersid);
outputElem.play();