2013-03-20 44 views
3

我在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

回答

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

+0

謝謝,我會檢查這個! – 2013-03-21 11:38:57

7

嘗試:

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

對於雙它們通常在範圍偏愛值[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