Skip to content

Commit

Permalink
Add audio recording classes: AudioRecorder (a service) and AudioRecor…
Browse files Browse the repository at this point in the history
…dThread (a thread which gets spawned by AudioRecorder)
  • Loading branch information
blakeelias committed Jun 10, 2014
1 parent 879cd50 commit 37b7351
Show file tree
Hide file tree
Showing 2 changed files with 264 additions and 0 deletions.
201 changes: 201 additions & 0 deletions video/src/main/java/com/wearscript/audio/AudioRecordThread.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
package com.wearscript.audio;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder.AudioSource;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;

public class AudioRecordThread extends Thread{

private static final String LOG_TAG = "Audio";

private static final int RECORDER_SAMPLERATE = 8000;
private static final int ENCODING_TYPE = AudioFormat.ENCODING_PCM_16BIT;
private final int CHANNEL_TYPE = AudioFormat.CHANNEL_IN_MONO;
private final int NUM_CHANNELS = 1;
private byte BITS_PER_SAMPLE = 16;
private final int AUDIO_SOURCE = AudioSource.MIC;
private final int BYTE_RATE = RECORDER_SAMPLERATE * NUM_CHANNELS * (BITS_PER_SAMPLE / 8);
private final int RECORDING_SECS = 20;

public static final String directory = Environment.getExternalStorageDirectory() + File.separator + "wearscript";
public static final String directoryAudio = directory + File.separator+"audio";

private final int bufferSize = 160; //Each buffer holds 1/100th of a second.
private int numBuffers = 100 * RECORDING_SECS;
private boolean pollingBuffer = false;
private String latestFilePath = null;

AudioRecord recorder = null;

/**
* Give the thread high priority so that it's not cancelled unexpectedly, and start it
*/

private ArrayList<byte[]> buffers;
private ArrayList<Byte> totalBuffer;

public AudioRecordThread()
{
//Try this at lower priorities.
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);

This comment has been minimized.

Copy link
@christianvazquez7

christianvazquez7 Jun 17, 2014

Collaborator

Should we try it at lower priorities?

This comment has been minimized.

Copy link
@blakeelias

blakeelias Jun 17, 2014

Author Collaborator

Fixed in 6060a41

}

@Override
public void run()
{
Log.d(LOG_TAG, "Running Audio Thread");

buffers = new ArrayList<byte[]>();
totalBuffer = new ArrayList<Byte>();
int N = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,CHANNEL_TYPE,ENCODING_TYPE);
Log.d(LOG_TAG, ""+N);
try{
recorder = new AudioRecord(AUDIO_SOURCE, RECORDER_SAMPLERATE, CHANNEL_TYPE, ENCODING_TYPE, N*10);
}
catch(Throwable e){
Log.d(LOG_TAG, e.toString());
return;
}
recorder.startRecording();

try{
while(!interrupted())
{
if (!pollingBuffer){
buffers.add(new byte[bufferSize]);
recorder.read(buffers.get(buffers.size()-1),0,bufferSize);
}
else{
pollRingBuffer(0);
writeAudioDataToFile();
pollingBuffer = false;
Log.d(LOG_TAG, "Audio Saved");
}
}
Log.d(LOG_TAG, "interrupted");
}
catch(Throwable x)
{
Log.d(LOG_TAG, "Error reading voice audio", x);
}
/*
* Frees the thread's resources after the loop completes so that it can be run again
*/
finally
{
recorder.stop();
recorder.release();
Log.d(LOG_TAG, "Thread Terminated");
}
}

public String startPolling(long millis){
pollingBuffer = true;
latestFilePath = audioFileName(millis);
return latestFilePath;
}

private void pollRingBuffer(int ix){

This comment has been minimized.

Copy link
@christianvazquez7

christianvazquez7 Jun 17, 2014

Collaborator

Maybe rename this to mergeBuffers(int ix).

This comment has been minimized.

Copy link
@blakeelias

blakeelias Jun 17, 2014

Author Collaborator

Fixed in d88b871

Log.d(LOG_TAG, "in pollRingBuffer()");
int i;
int j;
for(i = 0; i<buffers.size(); i++){
for(j = 0; j<bufferSize; j++){
totalBuffer.add(buffers.get(ix + i)[j]);
}
}
}

private String audioFileName(long millis) {
return directoryAudio + File.separator + String.valueOf(millis) + ".wav";
}
//TODO what does this do and why is this here
public String saveAudio(){

This comment has been minimized.

Copy link
@christianvazquez7

christianvazquez7 Jun 17, 2014

Collaborator

Remove it

This comment has been minimized.

Copy link
@blakeelias

blakeelias Jun 17, 2014

Author Collaborator

Fixed in 2e81246

return "Failed";
}

private void writeAudioDataToFile() {
int totalAudioLen = numBuffers * bufferSize;
int totalDataLen = (totalAudioLen * NUM_CHANNELS * BITS_PER_SAMPLE / 8) + 36;
//String filePath = audioFileName();
byte header[] = new byte[44];

This comment has been minimized.

Copy link
@christianvazquez7

christianvazquez7 Jun 17, 2014

Collaborator

44 is a magic number, replace with constant.

This comment has been minimized.

Copy link
@blakeelias

blakeelias Jun 17, 2014

Author Collaborator

Fixed in 1c6904d

byte wavFile[] = new byte[totalBuffer.size() + header.length];

FileOutputStream os = null;
try {
os = new FileOutputStream(latestFilePath);
Log.d(LOG_TAG, "file path: " + latestFilePath);
} catch (FileNotFoundException e) {
e.printStackTrace();
}

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
header[21] = 0;
header[22] = (byte) NUM_CHANNELS;
header[23] = 0;
header[24] = (byte) (RECORDER_SAMPLERATE & 0xff);
header[25] = (byte) ((RECORDER_SAMPLERATE >> 8) & 0xff);
header[26] = (byte) ((RECORDER_SAMPLERATE >> 16) & 0xff);
header[27] = (byte) ((RECORDER_SAMPLERATE >> 24) & 0xff);
header[28] = (byte) (BYTE_RATE & 0xff);
header[29] = (byte) ((BYTE_RATE >> 8) & 0xff);
header[30] = (byte) ((BYTE_RATE >> 16) & 0xff);
header[31] = (byte) ((BYTE_RATE >> 24) & 0xff);
header[32] = (byte) (NUM_CHANNELS * BITS_PER_SAMPLE / 8);//(2 * 16 / 8); // block align (might be half what it should be)

This comment has been minimized.

Copy link
@christianvazquez7

christianvazquez7 Jun 17, 2014

Collaborator

Remove comment.

This comment has been minimized.

Copy link
@blakeelias

blakeelias Jun 17, 2014

Author Collaborator

Fixed in 300c2d0.

header[33] = 0;
header[34] = BITS_PER_SAMPLE; // bits per sample
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);

System.arraycopy(header, 0, wavFile, 0, header.length);
System.arraycopy(totalBuffer, 0, wavFile, header.length, totalBuffer.size());

try {
os.write(wavFile, 0, wavFile.length);
} catch (IOException e) {
e.printStackTrace();
}

try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

63 changes: 63 additions & 0 deletions video/src/main/java/com/wearscript/audio/AudioRecorder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.wearscript.audio;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

import java.io.File;

public class AudioRecorder extends Service {

private final String LOG_TAG = "AudioRecorder";

private AudioRecordThread recorder;
public static String MILLIS_EXTRA_KEY = "millis";

public AudioRecorder() {

This comment has been minimized.

Copy link
@christianvazquez7

christianvazquez7 Jun 17, 2014

Collaborator

Try deleting empty constructor. Is it necessary?

This comment has been minimized.

Copy link
@blakeelias

blakeelias Jun 17, 2014

Author Collaborator

Fixed in a80722e

}

@Override
public void onCreate() {
super.onCreate();

Log.d("Memora", "Service Started");

This comment has been minimized.

Copy link
@christianvazquez7

christianvazquez7 Jun 17, 2014

Collaborator

Remove Memora reference.

This comment has been minimized.

Copy link
@blakeelias

blakeelias Jun 17, 2014

Author Collaborator

Fixed in e907031

createMemoraDirectory();

This comment has been minimized.

Copy link
@christianvazquez7

christianvazquez7 Jun 17, 2014

Collaborator

Change to createDirectory().

This comment has been minimized.

Copy link
@blakeelias

blakeelias Jun 17, 2014

Author Collaborator

Fixed in 7d6ac2a


recorder = new AudioRecordThread();
recorder.start();
}

@Override
public void onDestroy() {
Log.d("Memora", "Service Destroy");

This comment has been minimized.

Copy link
@christianvazquez7

christianvazquez7 Jun 17, 2014

Collaborator

Remove Memora reference.

This comment has been minimized.

Copy link
@blakeelias

blakeelias Jun 17, 2014

Author Collaborator

Fixed in e907031

recorder.interrupt();
super.onDestroy();
}

@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}

private void createMemoraDirectory(){

This comment has been minimized.

Copy link
@christianvazquez7

christianvazquez7 Jun 17, 2014

Collaborator

Rename to createDirectory().

This comment has been minimized.

Copy link
@blakeelias

blakeelias Jun 17, 2014

Author Collaborator

Fixed in 7d6ac2a

File directory = new File(AudioRecordThread.directoryAudio);
if (!directory.isDirectory()){
directory.mkdirs();
}
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction() != null && intent.getAction().equals("save_audio_intent")) {
Log.d(LOG_TAG, "Got message");
long millis = intent.getExtras().getLong(MILLIS_EXTRA_KEY);
Log.d(LOG_TAG, "millis: " + millis);
String filepath = recorder.startPolling(millis);
Log.d(LOG_TAG, "filepath: " + filepath);
}
return 0;
}

}

0 comments on commit 37b7351

Please sign in to comment.