2010-12-14 71 views

回答

0

這個資源,WAVE PCM soundfile format,幫我PCM數據解析到WAVE。我已經建立了一個基於它的庫,它對我來說工作得很好。

+0

你能鏈接你的庫實現嗎? – 2016-02-14 04:06:05

+0

@LeifGruenwoldt這是一個5歲的答案,我很抱歉,但我沒有它。 – 2016-02-14 18:23:42

0

我知道一個叫做「OperateWav」,我在第一次實習期間在我的第一個項目中開發了一個轉換器(linux c/C++)。我不確定這個存在本身並且它是否支持java.Actually wav文件是簡單地增加對PCM原始數據wav格式的頭......

6

這是我的代碼

/** 
* Write PCM data as WAV file 
* @param os Stream to save file to 
* @param pcmdata 8 bit PCMData 
* @param srate Sample rate - 8000, 16000, etc. 
* @param channel Number of channels - Mono = 1, Stereo = 2, etc.. 
* @param format Number of bits per sample (16 here) 
* @throws IOException 
*/ 
public void PCMtoFile(OutputStream os, short[] pcmdata, int srate, int channel, int format) throws IOException { 
    byte[] header = new byte[44]; 
    byte[] data = get16BitPcm(pcmdata); 

    long totalDataLen = data.length + 36; 
    long bitrate = srate * channel * format; 

    header[0] = 'R'; 
    header[1] = 'I'; 
    header[2] = 'F'; 
    header[3] = 'F'; 
    header[4] = (byte) (totalDataLen & 0xff); 
    header[5] = (byte) ((totalDataLen >> 8) & 0xff); 
    header[6] = (byte) ((totalDataLen >> 16) & 0xff); 
    header[7] = (byte) ((totalDataLen >> 24) & 0xff); 
    header[8] = 'W'; 
    header[9] = 'A'; 
    header[10] = 'V'; 
    header[11] = 'E'; 
    header[12] = 'f'; 
    header[13] = 'm'; 
    header[14] = 't'; 
    header[15] = ' '; 
    header[16] = (byte) format; 
    header[17] = 0; 
    header[18] = 0; 
    header[19] = 0; 
    header[20] = 1; 
    header[21] = 0; 
    header[22] = (byte) channel; 
    header[23] = 0; 
    header[24] = (byte) (srate & 0xff); 
    header[25] = (byte) ((srate >> 8) & 0xff); 
    header[26] = (byte) ((srate >> 16) & 0xff); 
    header[27] = (byte) ((srate >> 24) & 0xff); 
    header[28] = (byte) ((bitrate/8) & 0xff); 
    header[29] = (byte) (((bitrate/8) >> 8) & 0xff); 
    header[30] = (byte) (((bitrate/8) >> 16) & 0xff); 
    header[31] = (byte) (((bitrate/8) >> 24) & 0xff); 
    header[32] = (byte) ((channel * format)/8); 
    header[33] = 0; 
    header[34] = 16; 
    header[35] = 0; 
    header[36] = 'd'; 
    header[37] = 'a'; 
    header[38] = 't'; 
    header[39] = 'a'; 
    header[40] = (byte) (data.length & 0xff); 
    header[41] = (byte) ((data.length >> 8) & 0xff); 
    header[42] = (byte) ((data.length >> 16) & 0xff); 
    header[43] = (byte) ((data.length >> 24) & 0xff); 

    os.write(header, 0, 44); 
    os.write(data); 
    os.close(); 
} 

編輯:2016年1月11日

public byte[] get16BitPcm(short[] data) { 
    byte[] resultData = new byte[2 * data.length]; 
    int iter = 0; 
    for (double sample : data) { 
     short maxSample = (short)((sample * Short.MAX_VALUE)); 
     resultData[iter++] = (byte)(maxSample & 0x00ff); 
     resultData[iter++] = (byte)((maxSample & 0xff00) >>> 8); 
    } 
    return resultData; 
} 
+0

'get16BitPcm(short [])'是否只是創建一個字節[]'它的參數大小的兩倍?如果是這樣,什麼endianness?如果不是,它是做什麼的? – Scruffy 2016-01-10 09:02:01

+0

@ Scruffy是的,抱歉缺少方法,只是更新了我的答案。 – devflow 2016-01-11 01:04:38

+0

根據http://soundfile.sapp.org/doc/WaveFormat,無論每個採樣的位數是多少,報頭中的字節16應該始終爲「16」。 – lreeder 2017-01-30 15:31:22

0

這應該很簡單,因爲WAV =元數據+ PCM(按此順序)。這應該工作:

private void rawToWave(final File rawFile, final File waveFile) throws IOException { 

byte[] rawData = new byte[(int) rawFile.length()]; 
DataInputStream input = null; 
try { 
    input = new DataInputStream(new FileInputStream(rawFile)); 
    input.read(rawData); 
} finally { 
    if (input != null) { 
     input.close(); 
    } 
} 

DataOutputStream output = null; 
try { 
    output = new DataOutputStream(new FileOutputStream(waveFile)); 
    // WAVE header 
    // see http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ 
    writeString(output, "RIFF"); // chunk id 
    writeInt(output, 36 + rawData.length); // chunk size 
    writeString(output, "WAVE"); // format 
    writeString(output, "fmt "); // subchunk 1 id 
    writeInt(output, 16); // subchunk 1 size 
    writeShort(output, (short) 1); // audio format (1 = PCM) 
    writeShort(output, (short) 1); // number of channels 
    writeInt(output, 44100); // sample rate 
    writeInt(output, RECORDER_SAMPLERATE * 2); // byte rate 
    writeShort(output, (short) 2); // block align 
    writeShort(output, (short) 16); // bits per sample 
    writeString(output, "data"); // subchunk 2 id 
    writeInt(output, rawData.length); // subchunk 2 size 
    // Audio data (conversion big endian -> little endian) 
    short[] shorts = new short[rawData.length/2]; 
    ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts); 
    ByteBuffer bytes = ByteBuffer.allocate(shorts.length * 2); 
    for (short s : shorts) { 
     bytes.putShort(s); 
    } 

    output.write(fullyReadFileToBytes(rawFile)); 
} finally { 
    if (output != null) { 
     output.close(); 
    } 
} 
} 
byte[] fullyReadFileToBytes(File f) throws IOException { 
int size = (int) f.length(); 
byte bytes[] = new byte[size]; 
byte tmpBuff[] = new byte[size]; 
FileInputStream fis= new FileInputStream(f); 
try { 

    int read = fis.read(bytes, 0, size); 
    if (read < size) { 
     int remain = size - read; 
     while (remain > 0) { 
      read = fis.read(tmpBuff, 0, remain); 
      System.arraycopy(tmpBuff, 0, bytes, size - remain, read); 
      remain -= read; 
     } 
    } 
} catch (IOException e){ 
    throw e; 
} finally { 
    fis.close(); 
} 

return bytes; 
} 
private void writeInt(final DataOutputStream output, final int value) throws IOException { 
output.write(value >> 0); 
output.write(value >> 8); 
output.write(value >> 16); 
output.write(value >> 24); 
} 

private void writeShort(final DataOutputStream output, final short value) throws IOException { 
output.write(value >> 0); 
output.write(value >> 8); 
} 

private void writeString(final DataOutputStream output, final String value) throws IOException { 
for (int i = 0; i < value.length(); i++) { 
    output.write(value.charAt(i)); 
    } 
} 

如何使用

它的使用非常簡單。只是這樣稱呼它:

File f1 = new File("/sdcard/44100Sampling-16bit-mono-mic.pcm"); // The location of your PCM file 
File f2 = new File("/sdcard/44100Sampling-16bit-mono-mic.wav"); // The location where you want your WAV file 
try { 
rawToWave(f1, f2); 
} catch (IOException e) { 
e.printStackTrace(); 
} 

如何這一切工作

正如你所看到的,WAV頭爲WAV和PCM格式的文​​件之間的唯一區別。假設您正在錄製16位PCM MONO音頻(根據您的代碼,您是)。 rawToWave函數只是將標題整齊地添加到WAV文件中,這樣音樂播放器就能知道打開文件時會發生什麼,然後在文件頭之後,它就會從最後一位開始寫入PCM數據。

酷提示

如果你想你的聲音的音高移動,或進行語音轉換應用中,所有你要做的就是增加/在你的代碼減少的writeInt(output, 44100); // sample rate值。減少它會告訴玩家以不同的速率播放它,從而改變輸出音調。只是一個額外的'高知識'的事情。 :)