2010-12-14 70 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值。减少它会告诉玩家以不同的速率播放它,从而改变输出音调。只是一个额外的'高知识'的事情。 :)