将字节数组转换为双数组

时间:2013-03-20 20:19:02

标签: java arrays fft wav

我在Java中遇到WAV文件的一些问题。

WAV格式:PCM_SIGNED 44100.0 Hz,24位,立体声,6字节/帧,小端。

  • 我将WAV数据提取到一个没有问题的字节数组。
  • 我正在尝试将字节数组转换为双数组,但有些双精度数字带有“NaN”值。

代码:

ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
double[] doubles = new double[byteArray.length / 8];
for (int i = 0; i < doubles.length; i++) {
    doubles[i] = byteBuffer.getDouble(i * 8);
}

16/24/32位,单声道/立体声的事实让我感到困惑。

我打算将double []传递给FFT算法并获得音频频率。

由于

3 个答案:

答案 0 :(得分:8)

试试这个:

public static byte[] toByteArray(double[] doubleArray){
    int times = Double.SIZE / Byte.SIZE;
    byte[] bytes = new byte[doubleArray.length * times];
    for(int i=0;i<doubleArray.length;i++){
        ByteBuffer.wrap(bytes, i*times, times).putDouble(doubleArray[i]);
    }
    return bytes;
}

public static double[] toDoubleArray(byte[] byteArray){
    int times = Double.SIZE / Byte.SIZE;
    double[] doubles = new double[byteArray.length / times];
    for(int i=0;i<doubles.length;i++){
        doubles[i] = ByteBuffer.wrap(byteArray, i*times, times).getDouble();
    }
    return doubles;
}

public static byte[] toByteArray(int[] intArray){
    int times = Integer.SIZE / Byte.SIZE;
    byte[] bytes = new byte[intArray.length * times];
    for(int i=0;i<intArray.length;i++){
        ByteBuffer.wrap(bytes, i*times, times).putInt(intArray[i]);
    }
    return bytes;
}

public static int[] toIntArray(byte[] byteArray){
    int times = Integer.SIZE / Byte.SIZE;
    int[] ints = new int[byteArray.length / times];
    for(int i=0;i<ints.length;i++){
        ints[i] = ByteBuffer.wrap(byteArray, i*times, times).getInt();
    }
    return ints;
}

答案 1 :(得分:4)

您的WAV格式为24位,但双倍使用64位。因此,存储在wav中的数量不能是双倍的。每帧和通道有一个24位有符号整数,相当于这6个字节。

你可以这样做:

private static double readDouble(ByteBuffer buf) {
  int v = (byteBuffer.get() & 0xff);
  v |= (byteBuffer.get() & 0xff) << 8;
  v |= byteBuffer.get() << 16;
  return (double)v;
}

您将为左声道调用该方法一次,为右侧调用一次。不确定正确的顺序,但我想先离开。字节从最不重要的一个读到最重要的一个,如little-endian所示。较低的两个字节用0xff屏蔽,以便将它们视为无符号。最重要的字节被视为有符号,因为它将包含带符号的24位整数的符号。

如果您对阵列进行操作,则可以在没有ByteBuffer的情况下进行操作,例如像这样:

double[] doubles = new double[byteArray.length / 3];
for (int i = 0, j = 0; i != doubles.length; ++i, j += 3) {
  doubles[i] = (double)( (byteArray[j  ] & 0xff) | 
                        ((byteArray[j+1] & 0xff) <<  8) |
                        ( byteArray[j+2]         << 16));
}

您将获得交错的两个通道的样本,因此您可能希望之后将它们分开。

如果您有单声道,则不会有两个通道交错但只有一次。对于16位,您可以使用byteBuffer.getShort(),对于32位,您可以使用byteBuffer.getInt()。但是24位并不常用于计算,因此ByteBuffer没有这方法。如果您有未签名的样本,则必须屏蔽所有符号,并抵消结果,但我认为无符号WAV相当罕见。

答案 2 :(得分:0)

对于double,他们通常更喜欢[0,1]范围内的值。所以你应该将每个元素除以2 24 -1。喜欢上面的MvG的答案,但有一些变化

int t =  (byteArray[j  ] & 0xff) | 
        ((byteArray[j+1] & 0xff) <<  8) |
         (byteArray[j+2]         << 16);
return t/double(0xFFFFFF);

但是双重真的是浪费空间和CPU。我建议将其转换为32位int。 SIMD在这个网站上有很多有效的方法可以做到这一点