将字节数组转换为双数组

问题描述:

我在Java中遇到了一些WAV文件的问题。将字节数组转换为双数组

WAV格式:PCM_SIGNED 44100.0赫兹,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算法并获取音频。

感谢

+0

听起来像一些这些数字实际上不能解释为'double' ... – 2013-03-20 20:22:52

+0

复制以http://计算器。com/questions/2905556/how-can-i-convert-a-byte-array-into-a-two-and-back? – maszter 2013-03-20 20:24:44

+0

@maszter:不是重复的,因为字节不代表双打。 – MvG 2013-03-20 22:33:49

你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是不常见的。

+0

谢谢,我会检查这个! – 2013-03-21 11:38:57

尝试:

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; 
} 

对于双它们通常在范围偏爱值[0,1]。所以你应该把每个元素除以2 -1。做像上面的MvG的答案,但有一些变化

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

但是双重真的是浪费空间和CPU。我建议将其转换为32位整数。有许多有效的方法来做到这一点SIMD本网站

+0

你的代码看起来有点混乱。你在循环内赋值为't',但不赋予'双精度'。所以最后你要得到的是最后一个采样的整数值,存储在't'中。你划分,但'double(0xFFFFFF)'不是有效的Java; '(双)0xFFFFFF'是。 – MvG 2013-09-25 08:45:17

+0

@MvG对不起,我的意思是在一个循环。编辑 – 2013-09-25 08:47:37