如何将16位PCM音频字节数组转换为双精度或浮点数组?

10

我正在尝试对一个.3gpp音频文件执行快速傅里叶变换。该文件包含来自手机麦克风的44100kHz下的5秒小录音。

我能找到的每个Java FFT算法只接受double[],float[]或Complex[]输入,这很明显,但是我正在以字节数组的形式读取音频文件,所以我有些困惑应该从哪里开始。我唯一能找到的是先前问题的答案:

Android audio FFT to retrieve specific frequency magnitude using audiorecord

但我不确定这是否是正确的过程。有任何了解的人吗?

2个回答

14

没有替代方法。你必须运行一个循环并分别将数组的每个元素转换类型。

对于我要进行浮点数fft的shorts,我也会执行相同的操作:

public static float[] floatMe(short[] pcms) {
    float[] floaters = new float[pcms.length];
    for (int i = 0; i < pcms.length; i++) {
        floaters[i] = pcms[i];
    }
    return floaters;
}

根据评论于2012年4月26日修订

如果您确实拥有16位PCM但是它被存储为byte[],那么您可以这样做:

public static short[] shortMe(byte[] bytes) {
    short[] out = new short[bytes.length / 2]; // will drop last byte if odd number
    ByteBuffer bb = ByteBuffer.wrap(bytes);
    for (int i = 0; i < out.length; i++) {
        out[i] = bb.getShort();
    }
    return out;
}

然后

float[] pcmAsFloats = floatMe(shortMe(bytes));

除非你正在使用一个奇怪和设计不良的类,它在第一次给你字节数组时已经将字节打包成了与Java将字节(每两个字节)转换为shorts的方式一致。


谢谢,我只是困惑于Java如何解释这些数据。也许我对它的存储方式有误解(请看我对Kyle答案的评论)。无论如何,如果我没有找到其他方法,任何代码都会受到赞赏。 - soren.qvist
感谢您的编辑mwengler。但是,pcmAsFloats不应该是short数组长度的一半吗?1个float = 4个字节,1个short = 2个字节? - soren.qvist
没关系,当然应该与短数组长度相同,因为它包含相同数量的样本。 - soren.qvist

-5
byte[] yourInitialData;
double[] yourOutputData = ByteBuffer.wrap(bytes).getDouble()

这看起来很优雅,但我猜困扰我的是这看起来太武断了(当涉及Java中的音频时,我还是新手)。如果16位PCM以这样的方式存储在字节中,表示模拟信号随时间变化的功率,那么转换过程不应该意识到这种结构吗?我的意思是,getDouble()怎么知道这是一个音频文件? - soren.qvist
浏览文档后,我发现getDouble()实际上返回的是double类型,而不是double[]。因此这样做行不通。 - soren.qvist
@Kyle 这个答案是错误的。 错误到它甚至无法编译。
  • getDouble() 返回类型为 double 而不是 double[]。这会导致编译失败。
  • getDouble() 一次获取4个字节,将它们拼接成32位并将整个东西解释为 double。由于字节数组未存储为 double,因此这将产生无意义的结果,并且只能从数组中提取与放入数组中的 short 数量的一半相同数量的 double
  • 请参见我上面的答案,以了解使用 ByteBuffer 将 byte[] 转换为有效的 short[] 的方法。
- mwengler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接