将音频缓冲区从 44100 重新采样到 16000

Posted

技术标签:

【中文标题】将音频缓冲区从 44100 重新采样到 16000【英文标题】:resample audio buffer from 44100 to 16000 【发布时间】:2014-12-22 07:24:53 【问题描述】:

我有 data-uri 格式的音频数据,然后我将此 data-uri 转换为缓冲区,现在我需要此缓冲区数据以新的采样率,当前音频数据为 44.1khz,我需要 16khz 的数据,如果我使用 RecordRTC API 录制了音频,如果我以低采样率录制音频,那么我的音频声音就会失真,所以我不知道如何重新采样我的音频缓冲区,

如果你们中的任何人对此有任何想法,请帮助我。

提前致谢:)

【问题讨论】:

【参考方案1】:

您可以使用 OfflineAudioContext 进行重采样,但您需要先将 data-uri 转换为 ArrayBuffer。此解决方案适用于浏览器,而不是服务器,因为在网络上发送质量较低的音频(较低的采样率)比在服务器上发送大量数据和重新采样要好。

// `source` is an AudioBuffer instance of the source audio
// at the original sample rate.

var TARGET_SAMPLE_RATE = 16000;

var offlineCtx = new OfflineAudioContext(source.numberOfChannels,
                                         source.duration * TARGET_SAMPLE_RATE,
                                         TARGET_SAMPLE_RATE);

// Play it from the beginning.
var offlineSource = offlineCtx.createBufferSource();
offlineSource.buffer = source;
offlineSource.connect(offlineCtx.destination);
offlineSource.start();
offlineCtx.startRendering().then((resampled) => 
  // `resampled` contains an AudioBuffer resampled at 16000Hz.
  // use resampled.getChannelData(x) to get an Float32Array for channel x.
);

【讨论】:

快速相关的qn。 FF内部使用的重采样算法是什么? 我们使用来自 speex 编解码器的重采样器:dxr.mozilla.org/mozilla-central/source/media/libspeex_resampler/…,它针对速度和良好的感知质量进行了优化。 一个小问题,但很重要:请注意,当 floats[i] = 1 时,floats[i] * Math.pow(2, 16) / 2 会产生错误的值。建议使用:ints[i] = floats[i] < 0 ? floats[i] * 32768 : floats[i] * 32767;(或者当然,缓存 floats[i] 可能会有所帮助避免双重数组查找的性能明智)。 这段代码对我不起作用 - 我最终得到一个空的音频文件。 此示例代码存在错误,这使得音频 API 的新手难以理解。 "o.startRendering()" // 什么是 o? "offlineCtx.createBuffer(... buffer.length...)" // 什么是缓冲区?那个变量不存在。这似乎很明显,但我们有多个缓冲区保存相同的东西,只是略有不同,所以让变量正确有助于理解。【参考方案2】:

没有答案是正确的。这是完美的代码。

// `sourceAudioBuffer` is an AudioBuffer instance of the source audio
// at the original sample rate.
const DESIRED_SAMPLE_RATE = 16000;
const offlineCtx = new OfflineAudioContext(sourceAudioBuffer.numberOfChannels, sourceAudioBuffer.duration * DESIRED_SAMPLE_RATE, DESIRED_SAMPLE_RATE);
const cloneBuffer = offlineCtx.createBuffer(sourceAudioBuffer.numberOfChannels, sourceAudioBuffer.length, sourceAudioBuffer.sampleRate);
// Copy the source data into the offline AudioBuffer
for (let channel = 0; channel < sourceAudioBuffer.numberOfChannels; channel++) 
    cloneBuffer.copyToChannel(sourceAudioBuffer.getChannelData(channel), channel);

// Play it from the beginning.
const source = offlineCtx.createBufferSource();
source.buffer = cloneBuffer;
source.connect(offlineCtx.destination);
offlineCtx.oncomplete = function(e) 
  // `resampledAudioBuffer` contains an AudioBuffer resampled at 16000Hz.
  // use resampled.getChannelData(x) to get an Float32Array for channel x.
  const resampledAudioBuffer = e.renderedBuffer;

offlineCtx.startRendering();
source.start(0);

【讨论】:

【参考方案3】:

如果您使用的是 chrome 浏览器,您可以直接在 AudioContext 中指定采样率。

1.您可以直接通过麦克风录制声音。

var context = new AudioContext(
    sampleRate: 16000,
);

2.如果您已经有一个文件或 ArrayBuffer 。那么您可以使用相同的音频上下文重新采样它

    const fileReader = new FileReader();
    fileReader.readAsArrayBuffer(target.files[0]);
    
    fileReader.onload =  (e) => 
        //e.target.result is an ArrayBuffer
        context.decodeAudioData(e.target.result, async function(buffer) 
        console.log(buffer)
    )
        
    

【讨论】:

工作就像一个魅力!你拯救了我的一天。我正在尝试所有其他复杂的方法。【参考方案4】:

这只是 padenot 答案的副本,我对其进行了更新,以避免其他可能发现这篇文章并且遇到缺少变量定义或如何获得最终重新采样的 float32array 问题的人感到困惑。这在 Firefox 量子 64.0 中适用于我:

  var sourceAudioBuffer = e.inputBuffer;  // directly received by the audioprocess event from the microphone in the browser

  var TARGET_SAMPLE_RATE = 8000;
  var offlineCtx = new OfflineAudioContext(sourceAudioBuffer.numberOfChannels, sourceAudioBuffer.duration * sourceAudioBuffer.numberOfChannels * TARGET_SAMPLE_RATE, TARGET_SAMPLE_RATE);
  var buffer = offlineCtx.createBuffer(sourceAudioBuffer.numberOfChannels, sourceAudioBuffer.length, sourceAudioBuffer.sampleRate);
  // Copy the source data into the offline AudioBuffer
  for (var channel = 0; channel < sourceAudioBuffer.numberOfChannels; channel++) 
      buffer.copyToChannel(sourceAudioBuffer.getChannelData(channel), channel);
  
  // Play it from the beginning.
  var source = offlineCtx.createBufferSource();
  source.buffer = sourceAudioBuffer;
  source.connect(offlineCtx.destination);
  source.start(0);
  offlineCtx.oncomplete = function(e) 
    // `resampled` contains an AudioBuffer resampled at 16000Hz.
    // use resampled.getChannelData(x) to get an Float32Array for channel x.
    var resampled = e.renderedBuffer;
    var leftFloat32Array = resampled.getChannelData(0);
    // use this float32array to send the samples to the server or whatever
  
  offlineCtx.startRendering();

在我的例子中,原始重采样 8000 pcm 数据通过这样的 udp 广播传输到 ffmpeg

ffmpeg -fflags nobuffer -analyzeduration 1M -f f32le -ar 8000 -ac 1 -i udp://127.0.0.1:12000 -ar 44100 -ac 2 -f alsa hw:0

所以 websocket 服务器只接收 base64 编码的 pcm 数据,解码 base64 字符串并通过 udp 进行广播。结果在扬声器上由 ffmpeg 播放。

【讨论】:

【参考方案5】:

一种更简单的方法是单独调用 resample,它只需要一个输入音频缓冲区、一个输入采样率、一个输出采样率,然后返回输出缓冲区。我发现这个链接可以做到这一点: audio resampling

这很好用(在音频范围内引入的噪音不多..)。感谢作者。

【讨论】:

链接库使用线性插值,没有任何带宽限制,因此会引入混叠伪影,听起来可能很糟糕。

以上是关于将音频缓冲区从 44100 重新采样到 16000的主要内容,如果未能解决你的问题,请参考以下文章

下采样 wav 音频文件

libfdk_aac音频采样率和编码字节数注意

从 44100 到 96000 Hz 的音频上采样和线性插值

如何在 AVCaptureSession 上设置音频采样率?

AudioQueue 录制音频样本

将 1ch 16000Hz PCM 转换为 2ch 44100Hz PCM