Fusionar dos archivos WAVE en Android (concatenar)

He estado tratando de fusionar dos archivos WAVE en Android durante bastante tiempo, pero realmente no puedo conseguirlo funcionar correctamente.

Todo se ve bien, los archivos se leen y se escriben en el archivo de salida que también es legible en una etapa posterior y tiene el tamaño de archivo que esperaría ver.

Los problemas se producen justo después de que la aplicación haya finalizado la fusión. Este mensaje se mostrará en el registro: Se produjo un error en updateListener, se anula la grabación que es un mensaje de extAudioRecorder y aparece cuando el OnRecordPositionUpdateListener alcanza la cláusula catch (la excepción tiene el siguiente detailMessage: write failed: EBADF (Bad file number) ). Esto no parece romper nada así que no estoy demasiado preocupado por esto.

El verdadero problema surge cuando intento crear un MediaPlayer y llamar a la setDataSource(String path) en la instancia de MediaPlayer. Cada vez que hago esto con un archivo combinado el siguiente mensaje de error se mostrará en el registro: No se puede crear reproductor de medios (la IOException que se arroja contiene el siguiente detailMessage: setDataSourceFD failed .: status = 0x80000000 ). Tenga en cuenta que la primera vez que el archivo se reproducirá perfectamente bien (este primer archivo no fue hecho por el método de combineWaveFiles() ). Ese mensaje de error aparece para indicar que el formato del archivo de audio es incorrecto y / o no puede ser leído por MediaPlayer.

Mi pregunta es si alguien ve algún problema real con el código de abajo (sé que es sub-óptima de muchas maneras, pero prefiero conseguir que funcione primero y luego preocuparse por el rendimiento).

 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); } 

Las partes grandes de este código se toman de esta respuesta .

    2 Solutions collect form web for “Fusionar dos archivos WAVE en Android (concatenar)”

    Hago esto exactamente en mi androide app. En lugar de dos, me fusiono varios archivos basados ​​en la selección de usuarios. Utilizo AsyncTask para combinar las muestras en el fondo. Echa un vistazo aquí. Solo filtra la sección que necesitas. Si tienes curiosidad acerca de mi aplicación, se llama Sound Recorder + Pro , junto a la combinación i do mix, añade echo y amplifica 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; } 

    Pruebe con este código para concatenar los archivos wav:

     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(); } } }); } } 

    Nota: no se olvide de dar este permiso WRITE_EXTERNAL_STORAGE

    Otros trucos: Estos son los métodos utilizados para mezclar 3 sonidos juntos:

     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(); } } 

    Una vez que tenemos un sonido mixto PCM, puede ser transformado en un archivo .wav para que cada jugador pueda leerlo.

      /** * 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 } } 

    Créditos: nalitzis

    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.