Merge two WAVE files on Android (concatenate)

I’ve been trying to merge two WAVE files on Android for quite some time now, but can't seem to get it working fine.

Everything looks great, files are read and written to the output file, which is also read at a later stage and has the file size that I expect to see.

Problems occur immediately after the merge of the application is completed. This message will be displayed in the log: An error occurred in updateListener, the recording is interrupted , which is an extAudioRecorder message extAudioRecorder and appears when OnRecordPositionUpdateListener reaches the catch clause (exception has the following detailMessage: write failed: EBADF (bad file number) ). It doesn't seem to break anything, so I'm not too worried about it.

The real problem occurs when I try to create a MediaPlayer and call setDataSource(String path) on the MediaPlayer instance. Whenever I do this with a merged file, the following error message will be displayed in the log: Unable to create a media player (pressing IOException contains the following detailMessage: setDataSourceFD failed: status = 0x80000000 ). Please note that the first time the file will play perfectly (this first file was not created by the combineWaveFiles() method). This error message appears indicating that the audio file format is incorrect and / or MediaPlayer cannot be read.

My question is: if anyone sees any real problems with the code below (I know that it is not optimal in many ways, but I prefer to work first and 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); } 

Most of this code is taken from this answer .

+4
source share
2 answers

I do it exactly in my android app. Instead of two, I merge multiple files based on user selection. I use AsyncTask to combine patterns in the background. Take a look at this here. Just filter the required section. If you are interested in learning about my application, its called Sound Recorder + Pro.In addition to merging, I really mix, add echo and amplify the samples:

  @Override protected Void doInBackground(Void... params) { isProcessingOn=true; try { DataOutputStream amplifyOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(Environment.getExternalStorageDirectory() + "/Soundrecpluspro/" + year +"-"+ month +"-"+ date +"-"+ hour+"-" + min +"-"+ sec+"ME.wav"))); DataInputStream[] mergeFilesStream = new DataInputStream[selection.size()]; long[] sizes=new long[selection.size()]; for(int i=0; i<selection.size(); i++) { File file = new File(Environment.getExternalStorageDirectory() + "/Soundrecpluspro/" +selection.get(i)); sizes[i] = (file.length()-44)/2; } for(int i =0; i<selection.size(); i++) { mergeFilesStream[i] =new DataInputStream(new BufferedInputStream(new FileInputStream(Environment.getExternalStorageDirectory() + "/Soundrecpluspro/" +selection.get(i)))); if(i == selection.size()-1) { mergeFilesStream[i].skip(24); byte[] sampleRt = new byte[4]; mergeFilesStream[i].read(sampleRt); ByteBuffer bbInt = ByteBuffer.wrap(sampleRt).order(ByteOrder.LITTLE_ENDIAN); RECORDER_SAMPLERATE = bbInt.getInt(); mergeFilesStream[i].skip(16); } else { mergeFilesStream[i].skip(44); } } for(int b=0; b<selection.size(); b++) { for(int i=0; i<(int)sizes[b]; i++) { byte[] dataBytes = new byte[2]; try { dataBytes[0] = mergeFilesStream[b].readByte(); dataBytes[1] = mergeFilesStream[b].readByte(); } catch (EOFException e) { amplifyOutputStream.close(); } short dataInShort = ByteBuffer.wrap(dataBytes).order(ByteOrder.LITTLE_ENDIAN).getShort(); float dataInFloat= (float) dataInShort/37268.0f; short outputSample = (short)(dataInFloat * 37268.0f); byte[] dataFin = new byte[2]; dataFin[0] = (byte) (outputSample & 0xff); dataFin[1] = (byte)((outputSample >> 8) & 0xff); amplifyOutputStream.write(dataFin, 0 , 2); } } amplifyOutputStream.close(); for(int i=0; i<selection.size(); i++) { mergeFilesStream[i].close(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } long size =0; try { FileInputStream fileSize = new FileInputStream(Environment.getExternalStorageDirectory() + "/Soundrecpluspro/"+year +"-"+ month +"-"+ date +"-"+ hour+"-" + min +"-"+ sec+"ME.wav"); size = fileSize.getChannel().size(); fileSize.close(); } catch (FileNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } final int RECORDER_BPP = 16; long datasize=size+36; long byteRate = (RECORDER_BPP * RECORDER_SAMPLERATE)/8; long longSampleRate = RECORDER_SAMPLERATE; byte[] header = new byte[44]; header[0] = 'R'; // RIFF/WAVE header header[1] = 'I'; header[2] = 'F'; header[3] = 'F'; header[4] = (byte) (datasize & 0xff); header[5] = (byte) ((datasize >> 8) & 0xff); header[6] = (byte) ((datasize >> 16) & 0xff); header[7] = (byte) ((datasize >> 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) 1; 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) ((RECORDER_BPP) / 8); // block align header[33] = 0; header[34] = RECORDER_BPP; // bits per sample header[35] = 0; header[36] = 'd'; header[37] = 'a'; header[38] = 't'; header[39] = 'a'; header[40] = (byte) (size & 0xff); header[41] = (byte) ((size >> 8) & 0xff); header[42] = (byte) ((size >> 16) & 0xff); header[43] = (byte) ((size >> 24) & 0xff); // out.write(header, 0, 44); try { RandomAccessFile rFile = new RandomAccessFile(Environment.getExternalStorageDirectory() + "/Soundrecpluspro/" +year +"-"+ month +"-"+ date +"-"+ hour+"-" + min +"-"+ sec+ "ME.wav", "rw"); rFile.seek(0); rFile.write(header); rFile.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } 
+5
source

Try this code to concatenate wav files:

 public class ConcateSongActivity extends Activity { Button mbutt; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mbutt = (Button)findViewById(R.id.button_clickme); mbutt.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { try { FileInputStream fis1 = new FileInputStream("/sdcard/MJdangerous.wav"); FileInputStream fis2 = new FileInputStream("/sdcard/MJBad.wav"); SequenceInputStream sis = new SequenceInputStream(fis1,fis2); FileOutputStream fos = new FileOutputStream(new File("/sdcard/MJdangerousMJBad.wav")); int temp; try { while ((temp = sis.read())!= -1){ fos.write(temp); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); } } 

Note. Remember to give this permission WRITE_EXTERNAL_STORAGE

other tricks: These are the methods used to mix three sounds:

 private void mixFiles(){ try { InputStream is1 = getResources().openRawResource(R.raw.test1); List<Short> music1 = createMusicArray(is1); InputStream is2 = getResources().openRawResource(R.raw.test2); List<Short> music2 = createMusicArray(is2); completeStreams(music1,music2); short[] music1Array = buildShortArray(music1); short[] music2Array = buildShortArray(music2); short[] output = new short[music1Array.length]; for(int i=0; i < output.length; i++){ float samplef1 = music1Array[i] / 32768.0f; float samplef2 = music2Array[i] / 32768.0f; float mixed = samplef1 + samplef2; // reduce the volume a bit: mixed *= 0.8; // hard clipping if (mixed > 1.0f) mixed = 1.0f; if (mixed < -1.0f) mixed = -1.0f; short outputSample = (short)(mixed * 32768.0f); output[i] = outputSample; } saveToFile(output); } catch (NotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } 

Once we have mixed PCM sound, it can be converted to a WAV file so that every player can read it.

  /** * Dealing with big endian streams * @param byte0 * @param byte1 * @return a shrt with the two bytes swapped */ private static short swapBytes(byte byte0, byte byte1){ return (short)((byte1 & 0xff) << 8 | (byte0 & 0xff)); } /** * From file to byte[] array * @param sample * @param swap should swap bytes? * @return * @throws IOException */ public static byte[] sampleToByteArray(File sample, boolean swap) throws IOException{ ByteArrayOutputStream baos = new ByteArrayOutputStream(); BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sample)); int BUFFERSIZE = 4096; byte[] buffer = new byte[BUFFERSIZE]; while(bis.read(buffer) != - 1){ baos.write(buffer); } byte[] outputByteArray = baos.toByteArray(); bis.close(); baos.close(); if(swap){ for(int i=0; i < outputByteArray.length - 1; i=i+2){ byte byte0 = outputByteArray[i]; outputByteArray[i] = outputByteArray[i+1]; outputByteArray[i+1] = byte0; } } return outputByteArray; } /** * Read a file and returns its contents as array of shorts * @param sample the sample file * @param swap true if we should swap the bytes of short (reading a little-endian file), false otherwise (reading a big-endian file) * @return * @throws IOException */ public static short[] sampleToShortArray(File sample, boolean swap) throws IOException{ short[] outputArray = new short[(int)sample.length()/2]; byte[] outputByteArray = sampleToByteArray(sample,false); for(int i=0, j=0; i < outputByteArray.length; i+= 2, j++){ if(swap){ outputArray[j] = swapBytes(outputByteArray[i], outputByteArray[i + 1]); } else{ outputArray[j] = swapBytes(outputByteArray[i + 1], outputByteArray[i]); } } return outputArray; } public void completeStreams(List<Short> mListShort_1, List<Short> mListShort_2) { //TODO: check length int size_a = mListShort_1.size(); int size_b = mListShort_2.size(); if (size_a > size_b){ // adding series of '0' for (int i = size_b+1; i <= size_a; i++) { mListShort_2.set(i, (short) 0); } } else if (size_a < size_b) { for (int i = size_a+1; i <= size_b; i++) { mListShort_1.set(i, (short) 0); } } else { //do nothing } } 

Credits: nalitzis

+2
source

Source: https://habr.com/ru/post/1501732/


All Articles