NodeJS:将立体声PCM波流捕获到单声道AudioBuffer中

问题描述 投票:0回答:1

我正在使用node-microphone(这只是录制的javascript接口)从nodejs录制音频,并想使用AudioBuffer(这是Web Audio的nodejs实现)将流块存储在web-audio-api中API)。

我的音频源有两个通道,而我的AudioBuffer只有一个(故意)。

这是我的工作配置,用于通过USB声卡记录音频(我使用的是在Raspbian buster上运行的Raspberry pi 3):

arecord -D hw:1,0 -c 2 -f S16_LE -r 44100

使用输出路径运行此命令,并使用aplay播放生成的wav文件,效果很好。因此,节点麦克风能够使用这些参数记录音频,最后我得到一个nodejs可读流流波数据。

但是

我正在努力完成从流块(Buffer实例)到AudioBuffer的桥接。更确切地说;我不确定传入数据的格式,不确定目标格式,也不确定如何转换:

流块是Buffer,因此它们也是Uint8Array。关于我的配置,我想它们是16位带符号整数的二进制表示形式(小端,我不知道这是什么意思。)>

AudioBuffer拥有多个缓冲区(每个通道一个,所以在我的情况下只有一个),我可以通过调用Float32Array作为AudioBuffer.prototype.getChannelData()来访问。 MDN也说:

该缓冲区包含以下格式的数据:标称范围在-1和+1之间的非交织IEEE754 32位线性PCM,即32位浮点缓冲区,每个样本在-1.0和1.0之间。

关键是要知道我必须从传入的Buffer中提取什么,以及如何转换它,使其适合Float32Array目标(并保持有效的波形数据),并且知道音频源是立体声且AudioBuffer不是。

到目前为止,我最好的竞争者是Buffer.prototype.readFloatLE()方法,其名称看起来可以解决我的问题,但这并不成功(只是噪音)。

我的第一个尝试(在进行研究之前)只是天真地将缓冲区数据复制到Float32Array,并交织索引以处理立体声/单声道转换。显然,它主要产生噪音,但我可以听到一些我录制的声音(难以置信但确实存在),所以我想我应该提一下。

这是我天真的尝试的简化版本(我知道这并不意味着可以很好地发挥作用,我只是将其包括在我的问题中作为讨论的基础):

import { AudioBuffer } from 'web-audio-api'
import Microphone from 'node-microphone'

const rate = 44100
const channels = 2 // Number of source channels

const microphone = new Microphone({ // These parameters result to the arecord command above
  channels,
  rate,
  device: 'hw:1,0',
  bitwidth: 16,
  endian: 'little',
  encoding: 'signed-integer'
})

const audioBuffer = new AudioBuffer(
  1, // 1 channel
  30 * rate, // 30 seconds buffer
  rate
})

const chunks = []
const data = audioBuffer.getChannelData(0) // This is the Float32Array
const stream = microphone.startRecording()

setTimeout(() => microphone.stopRecording(), 5000) // Recording for 5 seconds

stream.on('data', chunk => chunks.push(chunk))

stream.on('close', () => {
  chunks.reduce((offset, chunk) => {
    for (var index = 0; index < chunk.length; index += channels) {
      let value = 0

      for (var channel = 0; channel < channels; channel++) {
        value += chunk[index + channel]
      }

      data[(offset + index) / channels] = value / channels // Average value from the two channels
    }

    return offset + chunk.length // Since data comes as chunks, this offsets AudioBuffer's index
  }, 0)
})

如果您能提供帮助,我将非常感激:)

[我正在使用节点麦克风(这只是用于录制的javascript接口)录制来自nodejs的音频,并希望使用网络音频API(这是一个nodejs ...)将流块存储在AudioBuffer中。

因此,输入立体声信号是16位带符号整数,左右声道交错,这意味着单个立体声采样的相应缓冲区(8位无符号整数)具有这种格式:

[LEFT ] 8 bits (LSB)
[LEFT ] 8 bits (MSB)
[RIGHT] 8 bits (LSB)
[RIGHT] 8 bits (MSB)

由于记录配置为little endian

格式,因此,首先是最低有效字节(LSB),然后是最高有效字节(MSB)。

[AudioBuffer单通道缓冲区,由Float32Array表示,期望值介于-11之间(每个样本一个值)。

因此要将值从输入Buffer映射到目标Float32Array,我必须使用Buffer.prototype.readInt16LE(offset)方法将字节offset参数每个样本增加4(2个左字节+ 2个右字节= 4个字节) ),然后将输入值从[-32768;+32768]范围(16位有符号整数范围)插入到[-1;+1]范围:

import { AudioBuffer } from 'web-audio-api'
import Microphone from 'node-microphone'

const rate = 44100
const channels = 2 // 2 input channels

const microphone = new Microphone({
  channels,
  rate,
  device: 'hw:1,0',
  bitwidth: 16,
  endian: 'little',
  encoding: 'signed-integer'
})

const audioBuffer = new AudioBuffer(
  1, // 1 channel
  30 * rate, // 30 seconds buffer
  rate
})

const chunks = []
const data = audioBuffer.getChannelData(0)
const stream = microphone.startRecording()

setTimeout(() => microphone.stopRecording(), 5000) // Recording for 5 seconds

stream.on('data', chunk => chunks.push(chunk))

stream.on('close', () => {
  chunks.reduce((offset, chunk) => {
    for (var index = 0; index < chunk.length; index += channels + 2) {
      let value = 0

      for (var channel = 0; channel < channels; channel++) {
        // Iterates through input channels and adds the values
        // of all the channel so we can compute the
        // average value later to reduce them into a mono signal

        // Multiplies the channel index by 2 because
        // there are 2 bytes per channel sample

        value += chunk.readInt16LE(index + channel * 2)
      }

      // Interpolates index according to the number of input channels
      // (also divides it by 2 because there are 2 bytes per channel sample)
      // and computes average value as well as the interpolation
      // from range [-32768;+32768] to range [-1;+1]
      data[(offset + index) / channels / 2] = value / channels / 32768
    }

    return offset + chunk.length
  }, 0)
})
javascript node.js audio web-audio-api alsa
1个回答
0
投票

因此,输入立体声信号是16位带符号整数,左右声道交错,这意味着单个立体声采样的相应缓冲区(8位无符号整数)具有这种格式:

© www.soinside.com 2019 - 2024. All rights reserved.