Android Open Source - audio-analyzer-for-android Wav Writer






From Project

Back to project page audio-analyzer-for-android.

License

The source code is released under:

Apache License

If you think the Android project audio-analyzer-for-android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.google.corp.productivity.specialprojects.android.samples.fft;
//from ww w  .jav  a 2 s.c  o  m
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import android.annotation.SuppressLint;
import android.os.Environment;
import android.os.StatFs;
import android.util.Log;

public class WavWriter {
  static final String TAG = "WavWriter";
  private File outPath;
  private OutputStream out;
  private byte[] header = new byte[44];
  final String relativeDir = "/Recorder";

  private int channels = 1;
  private byte RECORDER_BPP = 16;  // bits per sample
  private int byteRate;            // Average bytes per second
  private int totalDataLen  = 0;   // (file size) - 8
  private int totalAudioLen = 0;   // bytes of audio raw data
  private int framesWrited = 0;

  public WavWriter(int sampleRate) {
    byteRate = sampleRate*RECORDER_BPP/8*channels;
    
    header[0] = 'R';  // RIFF/WAVE header
    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';  // '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, PCM/uncompressed
    header[21] = 0;
    header[22] = (byte) channels;
    header[23] = 0;
    header[24] = (byte) (sampleRate & 0xff);
    header[25] = (byte) ((sampleRate >> 8) & 0xff);
    header[26] = (byte) ((sampleRate >> 16) & 0xff);
    header[27] = (byte) ((sampleRate >> 24) & 0xff);
    header[28] = (byte) (byteRate & 0xff);              // Average bytes per second
    header[29] = (byte) ((byteRate >> 8) & 0xff);
    header[30] = (byte) ((byteRate >> 16) & 0xff);
    header[31] = (byte) ((byteRate >> 24) & 0xff);
    header[32] = (byte) (channels * RECORDER_BPP / 8);  // Block align (number of bytes per sample slice)
    header[33] = 0;
    header[34] = RECORDER_BPP;                          // bits per sample (Significant bits per sample)
    header[35] = 0;                                     // Extra format bytes
    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);
  }
  
  static final int version = android.os.Build.VERSION.SDK_INT;
  
  @SuppressLint("NewApi")
  @SuppressWarnings("deprecation")
  public double secondsLeft() {
    long byteLeft;
    if (version >= 9) {
      byteLeft = outPath.getFreeSpace();  // Need API level 9
    } else {
      StatFs statFs = new StatFs(outPath.getAbsolutePath());
      byteLeft = (statFs.getAvailableBlocks() * (long)statFs.getBlockSize());
    }
    if (byteRate == 0 || byteLeft == 0) {
      return 0;
    }
    return (double)byteLeft / byteRate;
  }
  
  public boolean start() {
    if (!isExternalStorageWritable()) {
      return false;
    }
    File path = new File(Environment.getExternalStorageDirectory().getPath() + relativeDir);
    path.mkdirs();
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd_HH'h'mm'm'ss.SSS's'", Locale.US);
    String nowStr = df.format(new Date());
    outPath = new File(path, "rec" + nowStr + ".wav");

    try {
      out = new FileOutputStream(outPath);
      out.write(header, 0, 44);
      // http://developer.android.com/reference/android/os/Environment.html#getExternalStoragePublicDirectory%28java.lang.String%29
    } catch (IOException e) {
      Log.w(TAG, "start(): Error writing " + outPath, e);
      out = null;
    }
    return true;
  }
  
  public void stop() {
    if (out == null) {
      Log.w(TAG, "stop(): Error closing " + outPath + "  null pointer");
      return;
    }
    try {
      out.close();
    } catch (IOException e) {
      Log.w(TAG, "stop(): Error closing " + outPath, e);
    }
    out = null;
    // Modify totalDataLen and totalAudioLen to match data
    RandomAccessFile raf;
    try {
      totalAudioLen = framesWrited * RECORDER_BPP / 8 * channels;
      totalDataLen = header.length + totalAudioLen - 8;
      raf = new RandomAccessFile(outPath, "rw");
      raf.seek(4);
      raf.write((byte) ((totalDataLen >>  0) & 0xff));
      raf.write((byte) ((totalDataLen >>  8) & 0xff));
      raf.write((byte) ((totalDataLen >> 16) & 0xff));
      raf.write((byte) ((totalDataLen >> 24) & 0xff));
      raf.seek(40);
      raf.write((byte) ((totalAudioLen >>  0) & 0xff));
      raf.write((byte) ((totalAudioLen >>  8) & 0xff));
      raf.write((byte) ((totalAudioLen >> 16) & 0xff));
      raf.write((byte) ((totalAudioLen >> 24) & 0xff));
      raf.close();
    } catch (IOException e) {
      Log.w(TAG, "stop(): Error modifying " + outPath, e);
    }
  }

  byte[] byteBuffer;
  
  // Assume RECORDER_BPP == 16 and channels == 1
  public void pushAudioShort(short[] ss, int numOfReadShort) {
    if (out == null) {
      Log.w(TAG, "pushAudioShort(): Error writing " + outPath + "  null pointer");
      return;
    }
    if (byteBuffer == null || byteBuffer.length != ss.length*2) {
      byteBuffer = new byte[ss.length*2];
    }
    for (int i = 0; i < numOfReadShort; i++) {
      byteBuffer[2*i+0] = (byte)(ss[i] & 0xff);
      byteBuffer[2*i+1] = (byte)((ss[i]>>8) & 0xff);
    }
    framesWrited += numOfReadShort;
    try {
      out.write(byteBuffer, 0, numOfReadShort*2);
      // if use out.write(byte), then a lot of GC will generated
    } catch (IOException e) {
      Log.w(TAG, "pushAudioShort(): Error writing " + outPath, e);
      out = null;
    }
  }
  
  public double secondsWritten() {
    return (double)framesWrited/(byteRate*8/RECORDER_BPP/channels);
  }
  
  /* Checks if external storage is available for read and write */
  public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();  // Need API level 8
    if (Environment.MEDIA_MOUNTED.equals(state)) {
      return true;
    }
    return false;
  }
  
  public String getPath() {
    return outPath.getPath();
  }
  
}




Java Source Code List

com.google.corp.productivity.specialprojects.android.fft.RealDoubleFFT_Mixed.java
com.google.corp.productivity.specialprojects.android.fft.RealDoubleFFT.java
com.google.corp.productivity.specialprojects.android.samples.fft.AnalyzeActivity.java
com.google.corp.productivity.specialprojects.android.samples.fft.AnalyzeView.java
com.google.corp.productivity.specialprojects.android.samples.fft.ColorMapArray.java
com.google.corp.productivity.specialprojects.android.samples.fft.DoubleSineGen.java
com.google.corp.productivity.specialprojects.android.samples.fft.FramesPerSecondCounter.java
com.google.corp.productivity.specialprojects.android.samples.fft.InfoRecActivity.java
com.google.corp.productivity.specialprojects.android.samples.fft.MyPreferences.java
com.google.corp.productivity.specialprojects.android.samples.fft.RecorderMonitor.java
com.google.corp.productivity.specialprojects.android.samples.fft.SBNumFormat.java
com.google.corp.productivity.specialprojects.android.samples.fft.STFT.java
com.google.corp.productivity.specialprojects.android.samples.fft.SelectorText.java
com.google.corp.productivity.specialprojects.android.samples.fft.WavWriter.java