title: Merging two WAVE files on Android (concatenate)
body: <p>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.</p>
  
  <p>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.</p>
  
  <p>The problems occur right after the app has finished merging. This message will show in the log: <strong>Error occured in updateListener, recording is aborted</strong> which is a message from <a href="http://i-liger.com/sites/default/files/ExtAudioRecorder.java" rel="nofollow noreferrer"><code>extAudioRecorder</code></a> and appears when the <code>OnRecordPositionUpdateListener</code> reaches the catch clause (the Exception has the following detailMessage: <strong>write failed: EBADF (Bad file number)</strong>). This does not seem to break anything so I'm not too worried about this.</p>
  
  <p>The real problem arises when I try to create a <a href="http://developer.android.com/reference/android/media/MediaPlayer.html" rel="nofollow noreferrer"><code>MediaPlayer</code></a> and call the <a href="http://developer.android.com/reference/android/media/MediaPlayer.html#setDataSource%28java.lang.String%29" rel="nofollow noreferrer"><code>setDataSource(String path)</code></a> on the MediaPlayer instance. Whenever I do this with a merged file the following error message will show in the log: <strong>Unable to to create media player</strong> (the <code>IOException</code> that is thrown contains the following detailMessage: <strong>setDataSourceFD failed.: status=0x80000000</strong>). Note that the first time the file will play perfectly fine (this first file was not made by the <code>combineWaveFiles()</code> method). That Error message appears to indicate that the audio file's format is incorrect and/or can't be read by the MediaPlayer.</p>
  
  <p>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).</p>
  
  <pre><code>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 &lt; bufferSize) {
                  data = new byte[(int)file1Size];
              }
  
              while (in1.read(data) != -1) {
                  out.write(data);
  
                  file1Size -= bufferSize;
  
                  if (file1Size &lt;= 0) {
                      break;
                  } else if (file1Size &lt; bufferSize) {
                      data = new byte[(int)file1Size];
                  }
              }
          }
  
          if (in2 != null) {
              in2.skip(44);
              data = new byte[bufferSize];
  
              if (file2Size &lt; bufferSize) {
                  data = new byte[(int)file2Size];
              }
  
              while (in2.read(data) != -1) {
                  out.write(data);
  
                  file2Size -= bufferSize;
  
                  if (file2Size &lt;= 0) {
                      break;
                  } else if (file2Size &lt; 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 &amp; 0xff);
      header[5] = (byte)((totalDataLen &gt;&gt; 8) &amp; 0xff);
      header[6] = (byte)((totalDataLen &gt;&gt; 16) &amp; 0xff);
      header[7] = (byte)((totalDataLen &gt;&gt; 24) &amp; 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 &amp; 0xff);
      header[25] = (byte)((longSampleRate &gt;&gt; 8) &amp; 0xff);
      header[26] = (byte)((longSampleRate &gt;&gt; 16) &amp; 0xff);
      header[27] = (byte)((longSampleRate &gt;&gt; 24) &amp; 0xff);
      header[28] = (byte)(byteRate &amp; 0xff);
      header[29] = (byte)((byteRate &gt;&gt; 8) &amp; 0xff);
      header[30] = (byte)((byteRate &gt;&gt; 16) &amp; 0xff);
      header[31] = (byte)((byteRate &gt;&gt; 24) &amp; 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 &amp; 0xff);
      header[41] = (byte)((totalAudioLen &gt;&gt; 8) &amp; 0xff);
      header[42] = (byte)((totalAudioLen &gt;&gt; 16) &amp; 0xff);
      header[43] = (byte)((totalAudioLen &gt;&gt; 24) &amp; 0xff);
  
      out.write(header, 0, 44);
  }
  </code></pre>
  
  <p>Large parts of this code are taken from <a href="https://stackoverflow.com/a/11452687/703595">this answer</a>.</p>