Java 将pcm转换为.wav-噪声
我一直在尝试向我的应用程序添加一些代码,以将我录制的pcm文件转换为.wav文件。 我已经有了编写头文件的代码,但是由于某种原因,当我通过音频播放器播放转换后的.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
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吗?