Wednesday, September 11, 2013

Merging two WAVE files on Android (concatenate)

Merging two WAVE files on Android (concatenate)

I have been trying to merge two WAVE files on Android for quite some time
now but really can't seem to get it working properly.
Everything looks fine, the files are read, and written to the output file
which is also readable at a later stage and has the file size I would
expect to see.
The problems occur right after the app has finished merging. This message
will show in the log: Error occured in updateListener, recording is
aborted which is a message from extAudioRecorder and appears when the
OnRecordPositionUpdateListener reaches the catch clause (the Exception has
the following detailMessage: write failed: EBADF (Bad file number)). This
does not seem to break anything so I'm not too worried about this.
The real problem arises when I try to create a MediaPlayer and call the
setDataSource(String path) on the MediaPlayer instance. Whenever I do this
with a merged file the following error message will show in the log:
Unable to to create media player (the IOException that is thrown contains
the following detailMessage: setDataSourceFD failed.: status=0x80000000).
Note that the first time the file will play perfectly fine (this first
file was not made by the combineWaveFiles() method). That Error message
appears to indicate that the audio file's format is incorrect and/or can't
be read by the MediaPlayer.
My question is if anyone sees any real problems with the code below (I
know it's sub-optimal in many ways but I prefer to get it to work first
then worry about performance).
public static String MergeRecordings(String cumulativeFile, String
recordFile, int sampleRate, int bpp, int bufferSize, int channels) {
if (cumulativeFile == null) {
return recordFile;
} else if (recordFile == null) {
return cumulativeFile;
}
String outputFile = FileUtils.getFilePath(null,
MDSettings.shared().getMainActivity());
FileUtils.combineWaveFiles(cumulativeFile, recordFile, outputFile,
sampleRate, bpp, bufferSize, channels);
//FileUtils.removeFile(cumulativeFile);
//FileUtils.removeFile(recordFile);
return outputFile;
}
//creates a new file containing file1 + file2 stuck together as such.
private static void combineWaveFiles(String file1, String file2, String
outputFile, int sampleRate, int bpp, int bufferSize, int channels) {
FileInputStream in1 = null, in2 = null;
FileOutputStream out = null;
long longSampleRate = sampleRate;
long byteRate = sampleRate * channels * bpp / 8;
byte[] data;
try {
try {
in1 = new FileInputStream(file1);
} catch (Exception e) { }
try {
in2 = new FileInputStream(file2);
} catch (FileNotFoundException e) { }
out = new FileOutputStream(outputFile);
long file1Size = 0;
long file2Size = 0;
if (in1 != null) { file1Size = in1.getChannel().size() - 44; }
if (in2 != null) { file2Size = in2.getChannel().size() - 44; }
long totalAudioLen = file1Size + file2Size;
long totalDataLen = totalAudioLen + 36;
FileUtils.writeWaveFileHeader(out, totalAudioLen, totalDataLen,
longSampleRate, channels, byteRate, bpp);
if (in1 != null) {
in1.skip(44);
data = new byte[bufferSize];
if (file1Size < bufferSize) {
data = new byte[(int)file1Size];
}
while (in1.read(data) != -1) {
out.write(data);
file1Size -= bufferSize;
if (file1Size <= 0) {
break;
} else if (file1Size < bufferSize) {
data = new byte[(int)file1Size];
}
}
}
if (in2 != null) {
in2.skip(44);
data = new byte[bufferSize];
if (file2Size < bufferSize) {
data = new byte[(int)file2Size];
}
while (in2.read(data) != -1) {
out.write(data);
file2Size -= bufferSize;
if (file2Size <= 0) {
break;
} else if (file2Size < bufferSize) {
data = new byte[(int)file2Size];
}
}
}
out.close();
if (in1 != null) { in1.close(); }
if (in2 != null) { in2.close(); }
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void writeWaveFileHeader(FileOutputStream out, long
totalAudioLen, long totalDataLen, long longSampleRate, int channels, long
byteRate, int bpp)
throws IOException {
byte[] header = new byte[44];
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] = 16;
header[17] = 0;
header[18] = 0;
header[19] = 0;
header[20] = 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 * bpp); //(2 * 16 / 8);
header[33] = 0;
header[34] = (byte)bpp;
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);
}
Large parts of this code are taken from this answer.
If you need any more information I'd be happy to provide this to you.

No comments:

Post a Comment