在过去的两天里,我一直在尝试在 Android 上操作 16 位 PCM 数据,但收效甚微。我目前正在使用 WAV recorder捕捉音频。在使用 randomAccessWriter
写入缓冲区之前的 onPeriodicNotification(AudioRecord recorder)
方法中,我将缓冲区发送到自定义类,以操作样本,并将样本保存回缓冲区。我的自定义类中的方法如下:
由于缓冲区是一个字节数组,我首先将它们转换成短裤,现在一个短裤代表一帧(只有一个 channel )。我将实现 FFT 算法,一旦我克服了这个障碍,它需要输入是一个 float 组——所以我将每个短整数转换成一个 float 。现在,将数据写入 WAV 文件的 randomAccessWriter
接受一个字节数组,并期望每个帧为 2 个字节。因此,我将每个 float 转换回 short 并使用 ByteBuffer 重建一个字节数组,然后将其返回。当我运行我的记录器应用程序时,通过上面的代码发送缓冲区,一切都很好。
我尝试使用一个简单的语音调制算法来测试录音是否被修改,算法放在TODO注释的地方:
现在,如果我在我的 iPhone 上使用上述代码,音频样本将被转换,尽管数据本身是 32 位 float 。但是,在 Android 上,当我重新运行录音机应用程序并插入上述代码时,所产生的只是白噪声。在我可以使用上述代码成功修改样本之前,我无法继续我的 FFT 算法。
为什么会这样?如果了解该主题的人可以阐明该主题,我将不胜感激。
已解决 - 作者:Bjorn Roche
根本原因:记录是在 Little Endian 中提供数据,而 Java shorts 在 Big Endian 中;当使用两种不同形式应用函数时,会产生白噪声。下面的代码展示了如何接收 Little Endian 字节数组,转换为 Big Endian float 组并返回 Little Endian 字节数组。虽然 float 你可以做任何你想做的事,但我现在将使用我的 FFT 算法:
public byte[] manipulateSamples(byte[] data,
int samplingRate,
int numFrames,
short numChannels) {
// Convert byte[] to short[] (16 bit) to float[] (32 bit) (End result: Big Endian)
ShortBuffer sbuf = ByteBuffer.wrap(data).asShortBuffer();
short[] audioShorts = new short[sbuf.capacity()];
sbuf.get(audioShorts);
float[] audioFloats = new float[audioShorts.length];
for (int i = 0; i < audioShorts.length; i++) {
audioFloats[i] = ((float)Short.reverseBytes(audioShorts[i])/0x8000);
}
// Do your tasks here.
// Convert float[] to short[] to byte[] (End result: Little Endian)
audioShorts = new short[audioFloats.length];
for (int i = 0; i < audioFloats.length; i++) {
audioShorts[i] = Short.reverseBytes((short) ((audioFloats[i])*0x8000));
}
byte byteArray[] = new byte[audioShorts.length * 2];
ByteBuffer buffer = ByteBuffer.wrap(byteArray);
sbuf = buffer.asShortBuffer();
sbuf.put(audioShorts);
data = buffer.array();
return data;
}
最佳答案
你的问题是 java 中的 shorts 是 bigendian,但如果你从 WAV 文件中获取数据,则数据是 little endian。
关于java - 带符号的 16 位 PCM 转换不工作。为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18675801/