在 Web Audio API 中使用 ChannelSplitter 和 MergeSplitter 节点

Posted

技术标签:

【中文标题】在 Web Audio API 中使用 ChannelSplitter 和 MergeSplitter 节点【英文标题】:Using ChannelSplitter and MergeSplitter nodes in Web Audio API 【发布时间】:2013-12-17 20:46:55 【问题描述】:

我正在尝试使用 ChannelSplitter 节点将音频信号发送到 ChannelMerger 节点和目的地,然后尝试使用 ChannelMerger 节点合并两个不同的音频信号(一个来自拆分源,一个来自使用 getUserMedia 的麦克风)到使用 Recorder.js 的录音机中。

我不断收到以下错误:“未捕获的语法错误:指定了无效或非法的字符串。”

错误在以下代码行:

audiosource.splitter.connect(merger);

其中audioSource是ThreeAudio.js库中的ThreeAudio.Source的一个实例,splitter是我通过修改原型自己实例化的通道分流器,merge是我的合并节点。前面的代码是:

merger = context.createChannelMerger(2);
userInput.connect(merger);

其中 userInput 是来自用户麦克风的流。那一个连接而不会引发错误。声音从 audioSource 传到目的地(我能听到),所以分离器似乎不一定是错误的 - 我似乎无法连接它。

有人有什么见解吗?

【问题讨论】:

如果你有完整的代码示例会容易很多。我怀疑还有什么问题。 不幸的是,我认为在一个问题上发布的代码太多了!我也有这种感觉,我现在正在编写一个不使用 ThreeAudio 库的版本,所以我可以确定我正在以正确的方式连接所有内容。如果您看到了一个使用拆分器和合并器的正确示例的链接,那也会很有帮助。 【参考方案1】:

我很难理解ChannelSplitterNodeChannelMergerNode API。最后我找到了缺失的部分,connect()方法的第二个和第三个可选参数——输入和输出通道。

connect(destinationNode: AudioNode, output?: number, input?: number): AudioNode;

当使用带有 Splitter 或 Merger 节点的 connect() 方法时,请分隔输入/输出通道。这就是您拆分和合并到音频数据的方式。

您可以在此示例中看到我如何加载音频数据、将其拆分为 2 个通道以及控制左/右输出。注意connect() 方法的第二个和第三个参数:

const audioUrl = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/858/outfoxing.mp3";
const audioElement = new Audio(audioUrl);
    audioElement.crossOrigin = "anonymous"; // cross-origin - if file is stored on remote server

    const audioContext = new AudioContext();

    const audioSource = audioContext.createMediaElementSource(audioElement);

    const volumeNodeL = new GainNode(audioContext);
    const volumeNodeR = new GainNode(audioContext);

    volumeNodeL.gain.value = 2;
    volumeNodeR.gain.value = 2;

    const channelsCount = 2; // or read from: 'audioSource.channelCount'

    const splitterNode = new ChannelSplitterNode(audioContext,  numberOfOutputs: channelsCount );
    const mergerNode = new ChannelMergerNode(audioContext,  numberOfInputs: channelsCount );

    audioSource.connect(splitterNode);

    splitterNode.connect(volumeNodeL, 0); // connect OUTPUT channel 0
    splitterNode.connect(volumeNodeR, 1); // connect OUTPUT channel 1

    volumeNodeL.connect(mergerNode, 0, 0); // connect INPUT channel 0
    volumeNodeR.connect(mergerNode, 0, 1); // connect INPUT channel 1

    mergerNode.connect(audioContext.destination);

    let isPlaying;
    function playPause() 
      // check if context is in suspended state (autoplay policy)
      if (audioContext.state === 'suspended') 
        audioContext.resume();
      

      isPlaying = !isPlaying;
      if (isPlaying) 
        audioElement.play();
       else 
        audioElement.pause();
      
    

    function setBalance(val) 
      volumeNodeL.gain.value = 1 - val;
      volumeNodeR.gain.value = 1 + val;
    
  <h3>Try using headphones</h3>

  <button onclick="playPause()">play/pause</button>
  <br><br>
  <button onclick="setBalance(-1)">Left</button>
  <button onclick="setBalance(0)">Center</button>
  <button onclick="setBalance(+1)">Right</button>

P.S:音轨不是真正的立体声轨道,而是同一单声道播放的左右副本。你可以试试这个例子,用真正的立体声播放来获得真正的平衡效果。

【讨论】:

如果你有一个单声道你把它改成 splitterNode.connect(volumeNodeR, 0);? @Curtis,对于单声道,audioSource.channelCount 的值将是 1。但是为什么要控制单声道的平衡呢?如果你想改变左右扬声器输出可以使用StereoPannerNode,看这个例子:codepen.io/Rumyra/pen/qyMzqN 感谢您的提示。我不能使用 StereoPannerNode,因为 Safari 不支持它。不过很酷的例子。【参考方案2】:

这里有一些有效的分离器/合并器代码会产生乒乓延迟 - 也就是说,它会在立体声信号的 L 和 R 通道上设置单独的延迟,并跨越反馈。这是来自我在 webaudiodemos.appspot.com 上的输入效果演示(github 上的代码)。

var merger = context.createChannelMerger(2);
var leftDelay = context.createDelayNode();
var rightDelay = context.createDelayNode();
var leftFeedback = audioContext.createGainNode();
var rightFeedback = audioContext.createGainNode();
var splitter = context.createChannelSplitter(2);

// Split the stereo signal.
splitter.connect( leftDelay, 0 );

// If the signal is dual copies of a mono signal, we don't want the right channel - 
// it will just sound like a mono delay.  If it was a real stereo signal, we do want
// it to just mirror the channels.
if (isTrueStereo)
    splitter.connect( rightDelay, 1 );

leftDelay.delayTime.value = delayTime;
rightDelay.delayTime.value = delayTime;

leftFeedback.gain.value = feedback;
rightFeedback.gain.value = feedback;

// Connect the routing - left bounces to right, right bounces to left.
leftDelay.connect(leftFeedback);
leftFeedback.connect(rightDelay);

rightDelay.connect(rightFeedback);
rightFeedback.connect(leftDelay);

// Re-merge the two delay channels into stereo L/R
leftFeedback.connect(merger, 0, 0);
rightFeedback.connect(merger, 0, 1);

// Now connect your input to "splitter", and connect "merger" to your output destination.

【讨论】:

非常感谢!这真的很有帮助。

以上是关于在 Web Audio API 中使用 ChannelSplitter 和 MergeSplitter 节点的主要内容,如果未能解决你的问题,请参考以下文章

可以使用Web Audio API和createMediaElementSource分析来自Icecast的流式音频吗?

如何在 Web Audio API 中更改音频缓冲源的时间?

使用 Web Audio API 或其他任何东西在前端修剪静音

使用 Web Audio API 进行离线/非实时渲染

如何在没有 Web Audio API 的情况下直接从 ArrayBuffer 获取通道数据?

在 Web Audio API 中导出音频强度