title: Generate .wav audio file with "ulaw" encoding in Android
body: <p>I'm trying to generate a wav file with ulaw encoding from raw pcm data.
  I have tried some of the solutions by finding in google and here, but I can't seem to make the audio nicely playable. Its noisy and sort of screenching sound. May be the audio is playing too fast, not sure.</p>
  
  <p>So, here is the code that I have tried so far. </p>
  
  <p>I have my AudioRecorder.java that has the recording and saving that data as raw pcm file, converting of raw pcm data to ulaw encoded data, creation of wav header and then combining both the streams of data and saving as one .wav file</p>
  
  <pre><code>public class AudioRecorder {
  private static final String TAG = "AudioRecorder";
  private int audioInput = MediaRecorder.AudioSource.MIC;
  private int audioSampleRate = 8000; //frequency which ranges from 8K Hz to 44.1K Hz
  private int audioChannel = AudioFormat.CHANNEL_IN_MONO;
  private int audioEncode = AudioFormat.ENCODING_PCM_16BIT;
  
  private int bufferSizeInBytes = 0;
  private AudioRecord audioRecord;
  private Status status = Status.STATUS_NO_READY;
  protected String pcmFileName;
  
  private int currentPosition = 0;
  private int lastVolumn = 0;
  private FileOutputStream fosPcm = null;
  
  public AudioRecorder() {
      pcmFileName = AudioFileUtils.getPcmFileAbsolutePath(RECORDED_FILE_NAME);
      status = Status.STATUS_READY;
  }
  
  public void setAudioInput(int audioInput) {
      this.audioInput = audioInput;
  }
  
  public void setAudioSampleRate(int audioSampleRate) {
      this.audioSampleRate = audioSampleRate;
  }
  
  public void setAudioChannel(int audioChannel) {
      this.audioChannel = audioChannel;
  }
  
  /**
   * This method is to start recording using AudioRecord, also has NoiseSuppressor and AutomaticGainControl enabled
   */
  public void startRecord() {
  
      bufferSizeInBytes = AudioRecord.getMinBufferSize(audioSampleRate,
              audioChannel, audioEncode);
      audioRecord = new AudioRecord(audioInput, audioSampleRate, audioChannel, audioEncode, bufferSizeInBytes);
      if (status == Status.STATUS_NO_READY) {
          throw new IllegalStateException("not init");
      }
      if (status == Status.STATUS_START) {
          throw new IllegalStateException("is recording ");
      }
      Log.d("AudioRecorder", "===startRecord===" + audioRecord.getState());
      audioRecord.startRecording();
  
      new Thread(new Runnable() {
          @Override
          public void run() {
              NoiseSuppressor noiseSuppressor = NoiseSuppressor.create(audioRecord.getAudioSessionId());
              if (noiseSuppressor != null) {
                  noiseSuppressor.setEnabled(true);
              }
  
              AutomaticGainControl automaticGainControl = AutomaticGainControl.create(audioRecord.getAudioSessionId());
              if (automaticGainControl != null) {
                  automaticGainControl.setEnabled(true);
              }
  
              recordToFile();
          }
      }).start();
  }
  
  public void stop() {
      if (status != Status.STATUS_START) {
          throw new IllegalStateException("not recording");
      } else {
          stopRecorder();
  //            convertPCmFile();
              makeDestFile();
              status = Status.STATUS_READY;
      }
  }
  
  private void convertPCmFile() {
      File file = new File(AudioFileUtils.getPcmFileAbsolutePath(RECORDED_GREETING_FILE_NAME)); // for ex. path= "/sdcard/samplesound.pcm" or "/sdcard/samplesound.wav"
      byte[] byteData = new byte[(int) file.length()];
      try {
          FileInputStream in = new FileInputStream(file);
          in.read(byteData);
          in.close();
  
      } catch (FileNotFoundException e) {
          e.printStackTrace();
      } catch (IOException e) {
          e.printStackTrace();
      }
  // Set and push to audio track..
          int intSize = android.media.AudioTrack.getMinBufferSize(audioSampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO,
                  AudioFormat.ENCODING_PCM_16BIT);
          AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, audioSampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO,
                  AudioFormat.ENCODING_PCM_16BIT, intSize, AudioTrack.MODE_STREAM);
          if (at != null) {
              at.play();
  // Write the byte array to the track
              at.write(byteData, 0, byteData.length);
              at.stop();
              at.release();
          } else
              Log.d("TCAudio", "audio track is not initialised ");
      }
  
  private void makeDestFile() {
      new Thread() {
          @Override
          public void run() {
              File file = new File(pcmFileName);
              try {
  
                  //Step 1: create input stream from generated pcm audio data
                  byte[] pcmData = new byte[(int) file.length()];
                  FileInputStream inputStream1 = new FileInputStream(file);
                  int readBytes = inputStream1.read(pcmData);
                  inputStream1.close();
  
                  //Step 2: calculate the size that has to be sent to constructor which is half the size of actual pcm audio data
                  int size = UlawEncoderInputStream.maxAbsPcm(pcmData, 0, pcmData.length / 2);
  
                  //Step 3: send the input stream as well as the size to the constructor
                  FileInputStream inputStream2 = new FileInputStream(file);
                  UlawEncoderInputStream ulawEncoderInputStream = new UlawEncoderInputStream(inputStream2, size);
  
                  //Step 4: create byte[] with size of half of bytes of pcm audio data
                  byte[] ulawData = new byte[pcmData.length / 2];
  
                  //Step 5: call read from UlawEncoderInputStream with above pcmData which is newly created
                  int nRead;
                  nRead = ulawEncoderInputStream.read(ulawData);
  
                  //Step 6: create wav header
                  byte[] wavHeader = wavFileHeader(ulawData.length, ulawData.length + 36, audioSampleRate, audioChannel, audioSampleRate);
  
                  //Step 7: combine wav header and encodedUlawBuffer in one byte[]
                  byte[] allByteArray = new byte[wavHeader.length + ulawData.length];
  
                  ByteBuffer buff = ByteBuffer.wrap(allByteArray);
                  buff.put(wavHeader);
                  buff.put(ulawData);
  
                  //Step 8 : writing the combined data into a new file
                  OutputStream outputStream = new FileOutputStream(new File(AudioFileUtils.getWavFileAbsolutePath(RECORDED_FILE_NAME)));
                  outputStream.write(allByteArray);
                  outputStream.close();
  
              } catch (FileNotFoundException e) {
                  e.printStackTrace();
              } catch (IOException e) {
                  e.printStackTrace();
              }
              releaseRecorder();
          }
      }.run();
  }
  
  private byte[] wavFileHeader(long totalAudioLen, long totalDataLen, long longSampleRate,
                               int channels, long byteRate) {
      byte[] header = new byte[44];
      header[0] = 'R'; // RIFF/WAVE header
      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'; // '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] = 7; // format = 7, for ulaw
      header[21] = 0;
      header[22] = (byte) (channels &amp; 0xff);
      header[23] = (byte) ((channels &gt;&gt; 8) &amp; 0xFF);
      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 * 8) / 8);//
      // block align
      header[33] = 0;
      header[34] = 8; // bits per sample
      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);
      return header;
  }
  
  public void release() {
      stopRecorder();
      releaseRecorder();
      status = Status.STATUS_READY;
  }
  
  private void releaseRecorder() {
      if (audioRecord != null) {
          audioRecord.release();
          audioRecord = null;
      }
  }
  
  private void stopRecorder() {
      if (audioRecord != null) {
          try {
              audioRecord.stop();
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
  }
  
  /**
   * linear PCM data recorded to file
   */
  private void recordToFile() {
      byte[] audiodata = new byte[bufferSizeInBytes];
  
      int readsize = 0;
      try {
          fosPcm = new FileOutputStream(pcmFileName, true);
      } catch (FileNotFoundException e) {
          Log.e("AudioRecorder", e.getMessage());
      }
      status = Status.STATUS_START;
      while (status == Status.STATUS_START &amp;&amp; audioRecord != null) {
          readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);
          if (AudioRecord.ERROR_INVALID_OPERATION != readsize &amp;&amp; fosPcm != null) {
              try {
  
                  //get the volumn  1--10
                  int sum = 0;
                  for (int i = 0; i &lt; readsize; i++) {
                      sum += Math.abs(audiodata[i]);
                  }
  
                  if (readsize &gt; 0) {
                      int raw = sum / readsize;
                      lastVolumn = raw &gt; 32 ? raw - 32 : 0;
                      Log.i(TAG, "writeDataTOFile: volumn -- " + raw + " / lastvolumn -- " + lastVolumn);
                  }
  
  
                  if (readsize &gt; 0 &amp;&amp; readsize &lt;= audiodata.length)
                      fosPcm.write(audiodata, 0, readsize);
              } catch (IOException e) {
                  Log.e("AudioRecorder", e.getMessage());
              }
          }
      }
      try {
          if (fosPcm != null) {
              fosPcm.close();
          }
      } catch (IOException e) {
          Log.e("AudioRecorder", e.getMessage());
      }
  }
  
  public Status getStatus() {
      return status;
  }
  
  public enum Status {
      STATUS_NO_READY,
      STATUS_READY,
      STATUS_START,
      STATUS_PAUSE,
      STATUS_STOP
  }
  
  }
  </code></pre>
  
  <p>I have AudioFileUtils.java that just provides the path and appending of the extension to save</p>
  
  <pre><code>public class AudioFileUtils {
  private static String rootPath = "audiorecord";
  private final static String AUDIO_PCM_BASEPATH = "/" + rootPath + "/pcm/";
  private final static String AUDIO_WAV_BASEPATH = "/" + rootPath + "/wav/";
  
  private static void setRootPath(String rootPath) {
      AudioFileUtils.rootPath = rootPath;
  }
  
  public static String getPcmFileAbsolutePath(String fileName) {
      if (TextUtils.isEmpty(fileName)) {
          throw new NullPointerException("fileName isEmpty");
      }
      String mAudioRawPath = "";
      if (!fileName.endsWith(".pcm")) {
          fileName = fileName + ".pcm";
      }
      String fileBasePath = SampleApp.getInstance().getApplicationContext().getExternalFilesDir(null) + AUDIO_PCM_BASEPATH;
      File file = new File(fileBasePath);
      if (!file.exists()) {
          file.mkdirs();
      }
      mAudioRawPath = fileBasePath + fileName;
  
      return mAudioRawPath;
  }
  
  
  public static String getWavFileAbsolutePath(String fileName) {
      if (fileName == null) {
          throw new NullPointerException("fileName can't be null");
      }
  
      String mAudioWavPath = "";
      if (!fileName.endsWith(".wav")) {
          fileName = fileName + ".wav";
      }
      String fileBasePath = SampleApp.getInstance().getApplicationContext().getExternalFilesDir(null) + AUDIO_WAV_BASEPATH;
      CoxApplication.getInstance().getAccountManager().setMessageFilePath(fileBasePath);
      File file = new File(fileBasePath);
      if (!file.exists()) {
          file.mkdirs();
      }
      mAudioWavPath = fileBasePath + fileName;
      return mAudioWavPath;
  }
  
  }
  </code></pre>
  
  <p>I am using UlawEncoderInputStream.java which I found <a href="https://alvinalexander.com/java/jwarehouse/android/core/java/android/speech/srec/UlawEncoderInputStream.java.shtml" rel="nofollow noreferrer">here</a> with some changes to it, can be found below</p>
  
  <pre><code>public class UlawEncoderInputStream extends InputStream {
  private final static String TAG = "UlawEncoderInputStream";
  
  private final static int MAX_ULAW = 8192;
  private final static int SCALE_BITS = 16;
  
  private InputStream mIn;
  
  private int mMax = 0;
  
  // this buffer needs to be LARGER than the largest possible file size for
  // a 30 second PCM file recorded at either 8000 Hz or 44.1 KHz.
  // If it is smaller, the file will be cut off.
  private final byte[] mBuf = new byte[1048576];
  private int mBufCount = 0; // should be 0 or 1
  
  private final byte[] mOneByte = new byte[1];
  
  
  public static void encode(byte[] pcmBuf, int pcmOffset,
                            byte[] ulawBuf, int ulawOffset, int length, int max) {
  
      // from  'ulaw' in wikipedia
      // +8191 to +8159                          0x80
      // +8158 to +4063 in 16 intervals of 256   0x80 + interval number
      // +4062 to +2015 in 16 intervals of 128   0x90 + interval number
      // +2014 to  +991 in 16 intervals of  64   0xA0 + interval number
      //  +990 to  +479 in 16 intervals of  32   0xB0 + interval number
      //  +478 to  +223 in 16 intervals of  16   0xC0 + interval number
      //  +222 to   +95 in 16 intervals of   8   0xD0 + interval number
      //   +94 to   +31 in 16 intervals of   4   0xE0 + interval number
      //   +30 to    +1 in 15 intervals of   2   0xF0 + interval number
      //     0                                   0xFF
  
      //    -1                                   0x7F
      //   -31 to    -2 in 15 intervals of   2   0x70 + interval number
      //   -95 to   -32 in 16 intervals of   4   0x60 + interval number
      //  -223 to   -96 in 16 intervals of   8   0x50 + interval number
      //  -479 to  -224 in 16 intervals of  16   0x40 + interval number
      //  -991 to  -480 in 16 intervals of  32   0x30 + interval number
      // -2015 to  -992 in 16 intervals of  64   0x20 + interval number
      // -4063 to -2016 in 16 intervals of 128   0x10 + interval number
      // -8159 to -4064 in 16 intervals of 256   0x00 + interval number
      // -8192 to -8160                          0x00
  
      // set scale factors
      if (max &lt;= 0) max = MAX_ULAW;
  
      int coef = MAX_ULAW * (1 &lt;&lt; SCALE_BITS) / max;
  
      for (int i = 0; i &lt; length; i++) {
          int pcm = (0xff &amp; pcmBuf[pcmOffset++]) + (pcmBuf[pcmOffset++] &lt;&lt; 8);
          pcm = (pcm * coef) &gt;&gt; SCALE_BITS;
  
          int ulaw;
          if (pcm &gt;= 0) {
              ulaw = pcm &lt;= 0 ? 0xff :
                      pcm &lt;=   30 ? 0xf0 + ((  30 - pcm) &gt;&gt; 1) :
                              pcm &lt;=   94 ? 0xe0 + ((  94 - pcm) &gt;&gt; 2) :
                                      pcm &lt;=  222 ? 0xd0 + (( 222 - pcm) &gt;&gt; 3) :
                                              pcm &lt;=  478 ? 0xc0 + (( 478 - pcm) &gt;&gt; 4) :
                                                      pcm &lt;=  990 ? 0xb0 + (( 990 - pcm) &gt;&gt; 5) :
                                                              pcm &lt;= 2014 ? 0xa0 + ((2014 - pcm) &gt;&gt; 6) :
                                                                      pcm &lt;= 4062 ? 0x90 + ((4062 - pcm) &gt;&gt; 7) :
                                                                              pcm &lt;= 8158 ? 0x80 + ((8158 - pcm) &gt;&gt; 8) :
                                                                                      0x80;
          } else {
              ulaw = -1 &lt;= pcm ? 0x7f :
                      -31 &lt;= pcm ? 0x70 + ((pcm -   -31) &gt;&gt; 1) :
                              -95 &lt;= pcm ? 0x60 + ((pcm -   -95) &gt;&gt; 2) :
                                      -223 &lt;= pcm ? 0x50 + ((pcm -  -223) &gt;&gt; 3) :
                                              -479 &lt;= pcm ? 0x40 + ((pcm -  -479) &gt;&gt; 4) :
                                                      -991 &lt;= pcm ? 0x30 + ((pcm -  -991) &gt;&gt; 5) :
                                                              -2015 &lt;= pcm ? 0x20 + ((pcm - -2015) &gt;&gt; 6) :
                                                                      -4063 &lt;= pcm ? 0x10 + ((pcm - -4063) &gt;&gt; 7) :
                                                                              -8159 &lt;= pcm ? 0x00 + ((pcm - -8159) &gt;&gt; 8) :
                                                                                      0x00;
          }
          ulawBuf[ulawOffset++] = (byte)ulaw;
      }
  }
  
  /**
   * Compute the maximum of the absolute value of the pcm samples.
   * The return value can be used to set ulaw encoder scaling.
   * @param pcmBuf array containing 16 bit pcm data.
   * @param offset offset of start of 16 bit pcm data.
   * @param length number of pcm samples (not number of input bytes)
   * @return maximum abs of pcm data values
   */
  public static int maxAbsPcm(byte[] pcmBuf, int offset, int length) {
      int max = 0;
      for (int i = 0; i &lt; length; i++) {
          int pcm = (0xff &amp; pcmBuf[offset++]) + (pcmBuf[offset++] &lt;&lt; 8);
          if (pcm &lt; 0) pcm = -pcm;
          if (pcm &gt; max) max = pcm;
      }
      return max;
  }
  
  /**
   * Create an InputStream which takes 16 bit pcm data and produces ulaw data.
   * @param in InputStream containing 16 bit pcm data.
   * @param max pcm value corresponding to maximum ulaw value.
   */
  public UlawEncoderInputStream(InputStream in, int max) {
      mIn = in;
      mMax = max;
  }
  
  @Override
  public int read(byte[] buf, int offset, int length) throws IOException {
      if (mIn == null) throw new IllegalStateException("not open");
  
      // return at least one byte, but try to fill 'length'
      while (mBufCount &lt; 2) {
          int n = mIn.read(mBuf, mBufCount, Math.min(length * 2, mBuf.length - mBufCount));
          if (n == -1) return -1;
          mBufCount += n;
      }
  
      // compand data
      int n = Math.min(mBufCount / 2, length);
      encode(mBuf, 0, buf, offset, n, mMax);
  
      // move data to bottom of mBuf
      mBufCount -= n * 2;
      for (int i = 0; i &lt; mBufCount; i++) mBuf[i] = mBuf[i + n * 2];
  
      return n;
  }
  
  /*public byte[] getUpdatedBuffer(){
      return mBuf;
  }*/
  
  @Override
  public int read(byte[] buf) throws IOException {
      return read(buf, 0, buf.length);
  }
  
  @Override
  public int read() throws IOException {
      int n = read(mOneByte, 0, 1);
      if (n == -1) return -1;
      return 0xff &amp; (int)mOneByte[0];
  }
  
  @Override
  public void close() throws IOException {
      if (mIn != null) {
          InputStream in = mIn;
          mIn = null;
          in.close();
      }
  }
  
  @Override
  public int available() throws IOException {
      return (mIn.available() + mBufCount) / 2;
  }
  }
  </code></pre>
  
  <p>After doing all of this, as I said the voice is too screeching and noisy. Not sure where am I wrong. </p>
  
  <p>Any help, is appreciated! Thank you in advance.</p>