如何在android中将8位和16位pcm文件合并到单独的音频通道中
我知道有很多类似的问题,我读了很多,但我没能做到。正如我在标题中所说,我有一个8位和一个16位pcm单声道文件,我想将它们合并到一个立体声波形文件中,但在不同的声道中,一个在左声道,一个在右声道 到目前为止,我已经做了以下工作,但无论我做什么,都会出现两个问题: 1-我无法在输出文件的单独通道中写入它们。 2-其中一个文件会像噪音一样写入(无论“RECORDER_BPP”是8还是16) 我的代码:如何在android中将8位和16位pcm文件合并到单独的音频通道中,android,audio,merge,wav,pcm,Android,Audio,Merge,Wav,Pcm,我知道有很多类似的问题,我读了很多,但我没能做到。正如我在标题中所说,我有一个8位和一个16位pcm单声道文件,我想将它们合并到一个立体声波形文件中,但在不同的声道中,一个在左声道,一个在右声道 到目前为止,我已经做了以下工作,但无论我做什么,都会出现两个问题: 1-我无法在输出文件的单独通道中写入它们。 2-其中一个文件会像噪音一样写入(无论“RECORDER_BPP”是8还是16) 我的代码: new Thread(new Runnable() { @Override
new Thread(new Runnable() {
@Override
public void run() {
int channels = 2;
RECORDER_BPP = 16;
int RECORDER_SAMPLERATE = 44100;
int minBufferSize = AudioTrack.getMinBufferSize(maxCaptureRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
byte[] data = new byte[minBufferSize];
long longSampleRate = RECORDER_SAMPLERATE;
long byteRate = RECORDER_BPP * RECORDER_SAMPLERATE * channels/8;
try {
FileInputStream in1 = new FileInputStream(rawAudio1);
FileInputStream in2 = new FileInputStream(rawAudio2);
FileOutputStream out = new FileOutputStream(wavAudio);
long totalAudioLen = in1.getChannel().size();
long totalDataLen = totalAudioLen + 36;
WriteWaveFileHeader(out, totalAudioLen, totalDataLen,
longSampleRate, channels, byteRate);
while(in1.read(data) != -1){
out.write(data);
//2nd channel ?! // unsuccessful
in2.read(data);
out.write(data);
}
in1.close();
in2.close();
out.close();
} catch (Exception e) {
Log.d("EEE-2ch", e.getMessage());
}
}
}).start();
下面是标题部分:
private void WriteWaveFileHeader(
FileOutputStream out, long totalAudioLen,
long totalDataLen, long longSampleRate, int channels,
long byteRate) throws IOException {
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) (channels * RECORDER_BPP / 8); // block align
header[33] = 0;
header[34] = (byte) RECORDER_BPP; // bits per sample
header[35] = 0;
header[36] = 'd';
header[37] = 'a';
header[38] = 't';
header[39] = 'a';
header[40] = (byte) (totalAudioLen & 0xff);
header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
out.write(header, 0, 44);
}
感谢您的帮助,输出数据需要交错,这意味着您需要从in1中读取单个样本,将其写入输出,然后从in2中读取单个样本并将其写入输出。因为in2是8位音频,所以需要读取8位样本,然后写出16位
byte[] data = new byte[2];
while(in1.read(data) != -1) { // read 2 bytes
out.write(data); // write 2 bytes (L)
in2.read(data, 0, 1); // read 1 byte
data[1] = 0; // set the other byte to zero
// if this is the wrong byte order then do this
// data[1] = data[0];
// data[0] = 0;
out.write(data); // write 2 bytes (R)
}
此代码假定in1是具有16位音频的文件。如果这是错误的,那么你需要重新做一点 输出数据需要交错,这意味着您需要从in1中读取单个样本,将其写入输出,然后从in2中读取单个样本并将其写入输出。因为in2是8位音频,所以需要读取8位样本,然后写出16位
byte[] data = new byte[2];
while(in1.read(data) != -1) { // read 2 bytes
out.write(data); // write 2 bytes (L)
in2.read(data, 0, 1); // read 1 byte
data[1] = 0; // set the other byte to zero
// if this is the wrong byte order then do this
// data[1] = data[0];
// data[0] = 0;
out.write(data); // write 2 bytes (R)
}
此代码假定in1是具有16位音频的文件。如果这是错误的,那么你需要重新做一点 你的循环很奇怪。一次读/写的字节太多。您也无法检查从其中一个输入文件读取是否失败 此外,8位pcm样本通常是无符号的,但pcm通常是有符号的。必须转换8位值,以便符号匹配。我已经在代码示例中包含了这两种情况。选择与您的输入样本匹配的一个
byte[] data = new byte[minBufferSize]; // this is way too big for interlacing sample by sample.
while(in1.read(data) != -1){
out.write(data);
in2.read(data); // <-- This could fail.
out.write(data);
}
解决方案2,放大8位值
byte[] stereo_pair = new byte[4]; // a 16bit pcm stereo pair has only 4 bytes.
while (in1.read(stereo_pair, 0, 2)) // read two bytes from 16 bits pcm file.
{
if (!in2.read(stereo_pair, 2, 1)) // read one byte from 8 bit file.
{
if (/*is unsigned 8 bit value*/) // make it zero if error.
stereo_pair[2] = 128;
else
stereo_pair[2] = 0;
}
// we have to do some processing on that 8bit value.
// If it's unsigned, we have to make it signed, then sign extend it.
if (/*is unsigned 8 bit value*/)
{
stereo_pair[2] += 128; // this will offset the byte to make it signed
}
// we now have to sign extend the value
if (stereo_pair[2] >= 127)
stereo_pair[3] = 0xFF;
else
stereo_pair[3] = 0;
// our stereo sample is ready to write.
out.write(stereo_pair);
}
byte[] stereo_pair = new byte[4]; // a stereo pair has only 4 bytes.
while (in1.read(stereo_pair, 0, 2)) // read two bytes from 16 bits pcm file.
{
// the only differences are that we read the byte at a different offset
// in our stereo buffer, effectively multiplying the value by 256.
// we also do not need to sign-extend the velue.
if (!in2.read(stereo_pair, 3, 1)) // read one byte from 8 bit file.
{
if (/*is unsigned 8 bit value*/) // make it zero if error.
stereo_pair[3] = 128;
else
stereo_pair[3] = 0;
}
// we have to do some processing on that 8bit value.
// If it's unsigned, we have to make it signed
if (/*is unsigned 8 bit value*/)
{
stereo_pair[3] += 128; // this will offset the byte to make it signed
}
stereo_pair[2] = 0; // I put this here for clarity, but it can be moved out of the loop.
// our stereo sample is ready to write.
out.write(stereo_pair);
}
注意,当文件in1比文件in2短时,这不会处理这种情况…您的循环是奇数的。一次读/写的字节太多。您也无法检查从其中一个输入文件读取是否失败 此外,8位pcm样本通常是无符号的,但pcm通常是有符号的。必须转换8位值,以便符号匹配。我已经在代码示例中包含了这两种情况。选择与您的输入样本匹配的一个
byte[] data = new byte[minBufferSize]; // this is way too big for interlacing sample by sample.
while(in1.read(data) != -1){
out.write(data);
in2.read(data); // <-- This could fail.
out.write(data);
}
解决方案2,放大8位值
byte[] stereo_pair = new byte[4]; // a 16bit pcm stereo pair has only 4 bytes.
while (in1.read(stereo_pair, 0, 2)) // read two bytes from 16 bits pcm file.
{
if (!in2.read(stereo_pair, 2, 1)) // read one byte from 8 bit file.
{
if (/*is unsigned 8 bit value*/) // make it zero if error.
stereo_pair[2] = 128;
else
stereo_pair[2] = 0;
}
// we have to do some processing on that 8bit value.
// If it's unsigned, we have to make it signed, then sign extend it.
if (/*is unsigned 8 bit value*/)
{
stereo_pair[2] += 128; // this will offset the byte to make it signed
}
// we now have to sign extend the value
if (stereo_pair[2] >= 127)
stereo_pair[3] = 0xFF;
else
stereo_pair[3] = 0;
// our stereo sample is ready to write.
out.write(stereo_pair);
}
byte[] stereo_pair = new byte[4]; // a stereo pair has only 4 bytes.
while (in1.read(stereo_pair, 0, 2)) // read two bytes from 16 bits pcm file.
{
// the only differences are that we read the byte at a different offset
// in our stereo buffer, effectively multiplying the value by 256.
// we also do not need to sign-extend the velue.
if (!in2.read(stereo_pair, 3, 1)) // read one byte from 8 bit file.
{
if (/*is unsigned 8 bit value*/) // make it zero if error.
stereo_pair[3] = 128;
else
stereo_pair[3] = 0;
}
// we have to do some processing on that 8bit value.
// If it's unsigned, we have to make it signed
if (/*is unsigned 8 bit value*/)
{
stereo_pair[3] += 128; // this will offset the byte to make it signed
}
stereo_pair[2] = 0; // I put this here for clarity, but it can be moved out of the loop.
// our stereo sample is ready to write.
out.write(stereo_pair);
}
请注意,这不处理文件in1比文件in2短的情况…立体声PCM数据是交错的(lR等),因此您需要一次读取/写入一个样本。对于8位的样本,如果你想保持它的响度,你需要应用一些缩放。@Michael谢谢,但是你能给我一些如何做的样本代码吗?老实说,我没有处理音频文件的经验,也不太明白你说的话。ThanksTereo PCM数据是交错的(L R等),因此您需要一次读取/写入一个样本。对于8位的样本,如果你想保持它的响度,你需要应用一些缩放。@Michael谢谢,但是你能给我一些如何做的样本代码吗?老实说,我没有处理音频文件的经验,也不太明白你说的话。thanksIt实际上将左声道和右声道分开,但写入8位文件的声道完全是杂音。这似乎是因为我在写入wav文件时设置了RECORDER_BPP=16。我像你说的那样改变了字节顺序,但另一个字节是空的,什么也不写。我检查了pcm文件,没问题。知道怎么解决吗?我不太明白你说的另一个字节是空的是什么意思。对于第二个通道,您需要做的是读取一个字节,然后通过左移位将其写入两个字节。因此,如果文件中的示例值是0x43,那么您正在写入0x4300-瞧,8位值变成了16位值。您说“如果这是错误的字节顺序,那么请执行此操作…”。这实际上写了一些与第一次尝试数据[1]=0相反的东西,但这只是噪音。但它确实使尺寸增大了两倍。如果我使用记录器将其转换为wave_BPP=8,则该文件将被正常写入,但另一个文件(16位)将被写入所有噪声。有点不对劲,但我不知道是什么。它实际上把左声道和右声道分开了,但8位文件写入的声道完全是噪音。这似乎是因为我在写入wav文件时设置了RECORDER_BPP=16。我像你说的那样改变了字节顺序,但另一个字节是空的,什么也不写。我检查了pcm文件,没问题。知道怎么解决吗?我不太明白你说的另一个字节是空的是什么意思。对于第二个通道,您需要做的是读取一个字节,然后通过左移位将其写入两个字节。因此,如果文件中的示例值是0x43,那么您正在写入0x4300-瞧,8位值变成了16位值。您说“如果这是错误的字节顺序,那么请执行此操作…”。这实际上写了一些与第一次尝试数据[1]=0相反的东西,但这只是噪音。但它确实使尺寸增大了两倍。如果我使用记录器将其转换为wave_BPP=8,则该文件将被正常写入,但另一个文件(16位)将被写入所有噪声。有点不对劲,但我不知道是什么。