【问题标题】:Scheduling sounds in web audio api PCM在网络音频 api PCM 中安排声音
【发布时间】:2013-06-13 20:09:13
【问题描述】:

您好,我在客户端缓冲了一组 PCM 数据 (Float32) (所有这些音频阵列都是同一首歌的一部分) 所以它是一种 PCM 数据的客户端缓冲。 从服务器下载 N 个数组后,我开始播放这些缓冲区并调用下面的函数来播放示例数组。

var AudioStart =0;

function playPcmChunk(data){

    var source = audioContext.createBufferSource();

    var audio=new Float32Array(data);

    var audioBuffer = audioContext.createBuffer(1, audio.length , 44100);

    audioBuffer.getChannelData(0).set(audio);

    source.buffer = audioBuffer;

    source.start(AudioStart);

    AudioStart += audioBuffer.duration;
}

在每个样本之间我都会获得点击。

现在,为了确保我查看了服务器端的数据,它在 Audacity 中运行良好且流畅。

然后为了调试,我逐字打印出这些值,并在客户端和服务器端进行比较,看看是否存在传输问题,它们是否相同。

那么为什么我会在缓冲的样本数组之间听到点击声音。

我对确切时间的计算不正确吗?这是一个已知的网络音频 api 问题吗?

我认为这是尽可能精确的。我是否需要应用过滤器来消除点击。该过滤器的名称是什么?即使有我认为那将是黑客攻击。

欢迎提出意见

编辑:

这是我读取 websocket 并将 30 个(这完全是一个随机数)这些样本存储在一个数组中的地方。 在存储了 30 个之后,我开始循环每个样本并调用 playPcmChunk 并丢弃其余的音频。(这是用于测试)

每个都是 44100 32bitFloat,单声道。

wsAudio.onmessage = function (messageEvent) {
        if (messageEvent.data instanceof Blob) {


            var fileReader = new FileReader();
            fileReader.onload = function (e) {
                if (currentCount == -1) {
                    return;
                }
                if (currentCount < 30) {
                    audioChunks[currentCount] = e.target.result;
                    currentCount++;
                } else {

                    for (var j = 0; j < 30; j++) {

                        playPcmChunk(audioChunks[j]);
                    }
                    currentCount = -1;
                }



            };
            fileReader.readAsArrayBuffer(messageEvent.data);



        }
    }

【问题讨论】:

  • 需要查看调用 playPcmChunk 的代码。

标签: javascript scheduling web-audio-api pcm


【解决方案1】:

我正在做类似的事情并遇到了同样的问题。解决方案是使用 audioContext.createJavaScriptNode()。

不要在从 websocket 接收到块时播放它,你应该将它存储在某个地方并在请求时填充输出缓冲区。

==

var audioNode = audioContext.createJavaScriptNode(4096, 1, 1);

audioNode.onaudioprocess = function(event) {
                           if (decodedAudioBuffer.length >= bufferSize) {
                                  var decoded = decodedAudioBuffer.splice(0, bufferSize);
                                  var samples = new Float32Array(bufferSize);
                                  for (var i=0; i<decoded.length; i++) {
                                         samples[i] = decoded[i];
                                  }

                                  samples = resampler.resample(samples);
                                  var output = event.outputBuffer.getChannelData(0);
                                    for (var i = 0; i < output.length; i++) {
                                      output[i] = samples[i];
                                    }

                           }
};

audioNode.connect(audioContext.destination);

【讨论】:

  • 是的,我用它来重新采样我的音频样本,因为它们的采样率与 audioContext.sampleRate 不同。
  • @AnthonyHo,我想以类似的方式重新采样(麦克风输入从 44100 降至 16000 或 8000 ......)但我在这里有点困惑。 event.outputBuffer.length 不是必然不匹配吗?我们正在更改音频上下文中的样本数量。它是如何工作的?
【解决方案2】:

认为问题在于您基于audioContext.currentTime 加上总缓冲区持续时间进行调度。但问题是currentTime 是一个移动的目标。您需要一个一致的锚来确定您的所有时间安排。

您可能想要做的是,在您第一次开始玩时,说var AudioStart = audioContext.currentTime,然后按照您现在的方式继续添加它。然后将您的播放安排为source.start(AudioStart);

【讨论】:

  • 我也试过了 source.noteOn(AudioStart); AudioStart += audioBuffer.duration;当我得到第一个块时,我只运行它 AudioStart = audioContext.currentTime;但仍然没有喜悦,我也尝试了这个以确保它是一个调度。我将所有数据下载到单个数组中,而不是块中。并且玩了那个。这在没有任何咔哒声的情况下有效。因此,当无法安排日程时,它会很顺利。
  • 我也发现了这个。 jsbin.com/ffdead-audio-wav-slice/233 在计划播放现有缓冲区的同时创建一个新缓冲区似乎很容易导致播放出现故障(尤其是对于较大的缓冲区)。此实验在当前播放的缓冲区中间开始缓冲,看起来更稳定。
  • 这给我带来了各种各样的错误,但如果有人有兴趣,这里有一个我拼凑的快速克隆,效果更好:jsbin.com/eledul/1
  • 是的,我在之前的评论中发布了该链接,并实际联系了大卫,该网站的作者大卫实际上为谷歌工作。所以我希望我能在某个时候得到回复。
猜你喜欢
  • 2023-03-13
  • 1970-01-01
  • 2014-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-22
  • 1970-01-01
相关资源
最近更新 更多