我有一个以data-uri格式存储的音频数据,现在我已将该数据转换为缓冲区,现在我需要将该缓冲区数据转换成新的采样率,目前音频数据采用44.1kHz,我需要16kHz的数据。如果我使用RecordRTC API录制音频,并且以低采样率录制音频,则会出现扭曲的音频声音,因此我不知道如何重新取样我的音频缓冲区。
如果您有任何关于此问题的想法,请帮助我解决。
提前致谢 :)
我有一个以data-uri格式存储的音频数据,现在我已将该数据转换为缓冲区,现在我需要将该缓冲区数据转换成新的采样率,目前音频数据采用44.1kHz,我需要16kHz的数据。如果我使用RecordRTC API录制音频,并且以低采样率录制音频,则会出现扭曲的音频声音,因此我不知道如何重新取样我的音频缓冲区。
如果您有任何关于此问题的想法,请帮助我解决。
提前致谢 :)
你可以使用 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.
});
没有一个答案是正确的。这里是完美的代码。
// `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);
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)
})
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();
在我的情况下,原始的重新采样后的8000PCM数据通过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
floats[i] * Math.pow(2, 16) / 2
会产生错误的值。建议使用:ints[i] = floats[i] < 0 ? floats[i] * 32768 : floats[i] * 32767;
(当然,缓存floats[i]可能有助于性能,以避免双数组查找)。 - user1693593