Java 将pcm转换为.wav-噪声

Java 将pcm转换为.wav-噪声,java,android,audio,wav,Java,Android,Audio,Wav,我一直在尝试向我的应用程序添加一些代码,以将我录制的pcm文件转换为.wav文件。 我已经有了编写头文件的代码,但是由于某种原因,当我通过音频播放器播放转换后的.wav文件时,你所能听到的只是噪音 这是我的转换方法的代码: public void writeWavHeader(){ try { File pcmFile = new File(Environment.getExternalStorageDirectory() + File.separator + "Time

我一直在尝试向我的应用程序添加一些代码,以将我录制的pcm文件转换为.wav文件。 我已经有了编写头文件的代码,但是由于某种原因,当我通过音频播放器播放转换后的.wav文件时,你所能听到的只是噪音

这是我的转换方法的代码:

public void writeWavHeader(){
    try {
        File pcmFile = new File(Environment.getExternalStorageDirectory() + File.separator + "TimeShiftRecorder" + File.separator + "Recording.pcm");
        File wavFile = new File(Environment.getExternalStorageDirectory() + File.separator + "TimeShiftRecorder" + File.separator + "Recording_test.wav");
        OutputStream os = new FileOutputStream(wavFile);
        BufferedOutputStream bos = new BufferedOutputStream(os);
        DataOutputStream out = new DataOutputStream(bos);
        FileInputStream fis = new FileInputStream(pcmFile);
        InputStream bis = new BufferedInputStream(fis);
        DataInputStream dis = new DataInputStream(bis);

        short mBitsPerSample = 16;
        long audioLength = fis.getChannel().size();
        long byteRate = sampleRate * bitsPerSample/8 * channels;
        short format = 1;
        long totalDataLen = audioLength + 36;
        long longSampleRate = 44100;
        byte byteBitsPerSample = (byte) bitsPerSample; // = 16
        byte[] header = new byte[44];

        header[0] = 'R';  // RIFF/WAVE header
        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';  // 'fmt ' chunk
        header[13] = 'm';
        header[14] = 't';
        header[15] = ' ';
        header[16] = 16;  // 4 bytes: size of 'fmt ' chunk
        header[17] = 0;
        header[18] = 0;
        header[19] = 0;
        header[20] = 1;  // format = 1
        header[21] = 0;
        header[22] = (byte) channels;
        header[23] = 0;
        header[24] = (byte) (longSampleRate & 0xff);
        header[25] = (byte) ((longSampleRate >> 8) & 0xff);
        header[26] = (byte) ((longSampleRate >> 16) & 0xff);
        header[27] = (byte) ((longSampleRate >> 24) & 0xff);
        header[28] = (byte) (byteRate & 0xff);
        header[29] = (byte) ((byteRate >> 8) & 0xff);
        header[30] = (byte) ((byteRate >> 16) & 0xff);
        header[31] = (byte) ((byteRate >> 24) & 0xff);
        header[32] = (byte) (bitsPerSample/8*channels);  // block align
        header[33] = 0;
        header[34] = byteBitsPerSample;  // bits per sample
        header[35] = 0;
        header[36] = 'd';
        header[37] = 'a';
        header[38] = 't';
        header[39] = 'a';
        header[40] = (byte) (audioLength & 0xff);
        header[41] = (byte) ((audioLength >> 8) & 0xff);
        header[42] = (byte) ((audioLength >> 16) & 0xff);
        header[43] = (byte) ((audioLength >> 24) & 0xff);
        out.write(header, 0, 44);


        while (dis.available() > 0) {
            out.write(dis.read());
        }
        dis.close();
        out.close();
        //testHeader();

    }catch(IOException e){
        Log.v("recordService", "IOException");
    }

}
从我能在网上找到的所有例子来看,这个标题应该是正确的

如果有人能帮我解决我的问题,我将不胜感激

谢谢
Corey:)

通常,WAV格式使用16位信息(跨两个字节的内存)存储每个样本。确保您在阅读PCM和写入WAV时牢记这一点。一个字节每采样只能处理256个不同的值(通常为每秒44100次),这将导致8位格式的低保真度。取而代之的是,使用16位存储空间,您可以获得2^16个不同的值,这些值足以满足CD质量音频的要求。编写此类代码时,有两个程序非常方便:(1)将PCM转换为WAV,另一个(2)将WAV转换回PCM。这样,您可以使用(2)将已知良好的WAV作为源数据读入PCM,然后使用(1)输出WAV,然后比较两个WAV是否相等

还要确保您读/写二进制文件不是正常的文件模式


在您的情况下,由于WAV格式只是PCM数据,文件前面附加了一个标题,因此如果您将PCM作为二进制文件读取,请将标题输出到新的WAV文件,然后将内存缓冲区作为二进制写入WAV,您就可以了。

这是一个完整的工作示例

请参阅:了解更多信息

/**
 * overload using AudioFormat
 *
 * @param input  raw PCM data
 *               limit of file size for wave file: < 2^(2*4) - 36 bytes (~4GB)
 * @param output file to encode to in wav format
 * @param format corresponding audioformat for PCM data
 * @throws IOException in event of an error between input/output files
 * @see <a href="http://soundfile.sapp.org/doc/WaveFormat/">soundfile.sapp.org/doc/WaveFormat</a>
 */
static public void PCMToWAV(File input, File output, AudioFormat format) throws IOException {
    int bitsPerSample;
    switch (format.getEncoding()) {
        case AudioFormat.ENCODING_PCM_8BIT:
            bitsPerSample = 8;
            break;
        case AudioFormat.ENCODING_PCM_FLOAT:
            bitsPerSample = 32;
            break;
        case AudioFormat.ENCODING_PCM_16BIT:
        default:
            bitsPerSample = 16;

    }
    PCMToWAV(input, output, format.getChannelCount(), format.getSampleRate(), bitsPerSample);
}

/**
 * @param input         raw PCM data
 *                      limit of file size for wave file: < 2^(2*4) - 36 bytes (~4GB)
 * @param output        file to encode to in wav format
 * @param channelCount  number of channels: 1 for mono, 2 for stereo, etc.
 * @param sampleRate    sample rate of PCM audio
 * @param bitsPerSample bits per sample, i.e. 16 for PCM16
 * @throws IOException in event of an error between input/output files
 * @see <a href="http://soundfile.sapp.org/doc/WaveFormat/">soundfile.sapp.org/doc/WaveFormat</a>
 */
static public void PCMToWAV(File input, File output, int channelCount, int sampleRate, int bitsPerSample) throws IOException {
    final int inputSize = (int) input.length();

    try (OutputStream encoded = new FileOutputStream(output)) {
        // WAVE RIFF header
        writeToOutput(encoded, "RIFF"); // chunk id
        writeToOutput(encoded, 36 + inputSize); // chunk size
        writeToOutput(encoded, "WAVE"); // format

        // SUB CHUNK 1 (FORMAT)
        writeToOutput(encoded, "fmt "); // subchunk 1 id
        writeToOutput(encoded, 16); // subchunk 1 size
        writeToOutput(encoded, (short) 1); // audio format (1 = PCM)
        writeToOutput(encoded, (short) channelCount); // number of channelCount
        writeToOutput(encoded, sampleRate); // sample rate
        writeToOutput(encoded, sampleRate * channelCount * bitsPerSample / 8); // byte rate
        writeToOutput(encoded, (short) (channelCount * bitsPerSample / 8)); // block align
        writeToOutput(encoded, (short) bitsPerSample); // bits per sample

        // SUB CHUNK 2 (AUDIO DATA)
        writeToOutput(encoded, "data"); // subchunk 2 id
        writeToOutput(encoded, inputSize); // subchunk 2 size
        copy(new FileInputStream(input), encoded);
    }
}


/**
 * Size of buffer used for transfer, by default
 */
private static final int TRANSFER_BUFFER_SIZE = 10 * 1024;

/**
 * Writes string in big endian form to an output stream
 *
 * @param output stream
 * @param data   string
 * @throws IOException
 */
public static void writeToOutput(OutputStream output, String data) throws IOException {
    for (int i = 0; i < data.length(); i++)
        output.write(data.charAt(i));
}

public static void writeToOutput(OutputStream output, int data) throws IOException {
    output.write(data >> 0);
    output.write(data >> 8);
    output.write(data >> 16);
    output.write(data >> 24);
}

public static void writeToOutput(OutputStream output, short data) throws IOException {
    output.write(data >> 0);
    output.write(data >> 8);
}

public static long copy(InputStream source, OutputStream output)
        throws IOException {
    return copy(source, output, TRANSFER_BUFFER_SIZE);
}

public static long copy(InputStream source, OutputStream output, int bufferSize) throws IOException {
    long read = 0L;
    byte[] buffer = new byte[bufferSize];
    for (int n; (n = source.read(buffer)) != -1; read += n) {
        output.write(buffer, 0, n);
    }
    return read;
}
/**
*使用音频格式重载
*
*@param输入原始PCM数据
*wave文件的文件大小限制:<2^(2*4)-36字节(~4GB)
*@param要以wav格式编码的输出文件
*@param格式对应PCM数据的audioformat
*@在输入/输出文件之间发生错误时引发IOException
*@见
*/
静态公共void PCMToWAV(文件输入、文件输出、音频格式)引发IOException{
int位样本;
开关(format.getEncoding()){
case AudioFormat.ENCODING_PCM8bit:
bitsPerSample=8;
打破
case AudioFormat.ENCODING_PCM_FLOAT:
bitsPerSample=32;
打破
case AudioFormat.ENCODING_PCM_16位:
违约:
bitsPerSample=16;
}
PCMToWAV(输入、输出、format.getChannelCount()、format.getSampleRate()、bitsPerSample);
}
/**
*@param输入原始PCM数据
*wave文件的文件大小限制:<2^(2*4)-36字节(~4GB)
*@param要以wav格式编码的输出文件
*@param channelCount通道数:单声道1个,立体声2个,等等。
*@param-sampleRate PCM音频的采样率
*@param bitsPerSample每个样本的位,即PCM16为16位
*@在输入/输出文件之间发生错误时引发IOException
*@见
*/
静态公共void PCMToWAV(文件输入、文件输出、int channelCount、int sampleRate、int bitsPerSample)引发IOException{
final int inputSize=(int)input.length();
try(OutputStream encoded=新文件OutputStream(输出)){
//波裂头
writeToOutput(编码的,“RIFF”);//块id
writeToOutput(编码,36+inputSize);//块大小
writeToOutput(编码的“WAVE”);//格式
//子块1(格式)
writeToOutput(编码的,“fmt”);//子块1 id
writeToOutput(编码,16);//子块1大小
writeToOutput(编码,(短)1);//音频格式(1=PCM)
writeToOutput(编码,(短)channelCount);//channelCount的数量
writeToOutput(编码,sampleRate);//采样率
writeToOutput(编码,sampleRate*channelCount*bitsPerSample/8);//字节率
writeToOutput(编码,(短)(channelCount*bitsPerSample/8));//块对齐
writeToOutput(编码的,(短)位采样);//每个采样位
//子块2(音频数据)
writeToOutput(编码的,“数据”);//子块2 id
writeToOutput(编码,inputSize);//子块2大小
复制(新文件输入流(输入),编码);
}
}
/**
*默认情况下,用于传输的缓冲区大小
*/
私有静态最终整数传输缓冲区大小=10*1024;
/**
*将大端形式的字符串写入输出流
*
*@param输出流
*@param数据字符串
*@抛出异常
*/
公共静态void writeToOutput(OutputStream输出,字符串数据)引发IOException{
对于(int i=0;i>0);
输出。写入(数据>>8);
输出.写入(数据>>16);
输出。写入(数据>>24);
}
公共静态void writeToOutput(OutputStream输出,短数据)引发IOException{
输出.写入(数据>>0);
输出。写入(数据>>8);
}
公共静态长拷贝(InputStream源、OutputStream输出)
抛出IOException{
返回副本(源、输出、传输缓冲区大小);
}
公共静态长拷贝(InputStream源、OutputStream输出、int bufferSize)引发IOException{
长读数=0升;
字节[]缓冲区=新字节[bufferSize];
for(int n;(n=源.读(缓冲区))!=-1;读+=n){
输出写入(缓冲区,0,n);
}
返回读取;
}

尝试使用可以解释原始PCM数据源格式的东西读取.wav文件-应该发生的是,您会收到一个简短的噪音,因为它将标题错误地解释为音频,然后正确播放以下音频。若并没有,那个么您对数据、如何复制数据、或者以某种方式使数据从奇数偏移开始,而不是从偶数偏移开始,都有问题。但是如果以原始音频的形式播放它确实有效,那么您可能会遇到标题问题。您确定PCM文件是little endian吗?