Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
37b7351
Add audio recording classes: AudioRecorder (a service) and AudioRecor…
blakeelias Jun 10, 2014
245ceeb
Move audio and video recording to same package
blakeelias Jun 10, 2014
e907031
Record a continuous amount of audio until interrupted, then send a br…
blakeelias Jun 11, 2014
d88b871
Refactor pollRingBuffer() to mergeBuffers()
blakeelias Jun 17, 2014
2e81246
Remove saveAudio()
blakeelias Jun 17, 2014
1c6904d
Create constant WAV_HEADER_LENGTH
blakeelias Jun 17, 2014
300c2d0
Remove comments in WAV header creation
blakeelias Jun 17, 2014
7d6ac2a
Rename createMemoraDirectory() to createDirectory()
blakeelias Jun 17, 2014
a80722e
Remove empty constructor
blakeelias Jun 17, 2014
6060a41
Remove call to setThreadPriority
blakeelias Jun 17, 2014
d3bc49d
Fix code style / indentation issues
blakeelias Jun 17, 2014
9b99698
Convert remaining references to com.wearscript.video into com.wearscr…
blakeelias Jun 17, 2014
bca7cf9
Remove one more reference to com.wearscript.video
blakeelias Jun 18, 2014
d7c07d2
Add constant FILEPATH
blakeelias Jun 18, 2014
a01f430
Remove commented code
blakeelias Jun 18, 2014
f8a0f44
Move save-file logic inside of while recording loop
blakeelias Jun 19, 2014
9aef108
Save WAV header with long length so that we don't need to edit it whe…
blakeelias Jun 19, 2014
dad4016
Add ability to save an updated audio file whenever we want to while c…
blakeelias Jun 19, 2014
440958f
Accept audio file name passed in when recording is started
blakeelias Jun 19, 2014
5b251ea
Have AudioRecordThread accept filePath instead of fileName
blakeelias Jun 20, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions video/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.wearscript.video" >
package="com.wearscript.record" >

<uses-feature android:name="android.hardware.camera" />

Expand All @@ -14,7 +14,7 @@
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".PlaybackActivity"
android:name="com.wearscript.record.PlaybackActivity"
android:label="@string/title_activity_play" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
Expand All @@ -23,7 +23,7 @@
</intent-filter>
</activity>
<activity
android:name="com.wearscript.video.RecordActivity"
android:name="com.wearscript.record.RecordActivity"
android:label="@string/title_activity_record" >
<intent-filter>
<action android:name="android.intent.action.EDIT" />
Expand All @@ -34,14 +34,27 @@
</intent-filter>
</activity>

<receiver android:name=".WearScriptBroadcastReceiver">
<receiver android:name="com.wearscript.record.WearScriptBroadcastReceiver">
<intent-filter>
<action android:name="com.wearscript.video.PLAYBACK"/>
<action android:name="com.wearscript.video.RECORD" />
<action android:name="com.wearscript.video.RECORD_RESULT" />
<action android:name="com.wearscript.record.PLAYBACK"/>
<action android:name="com.wearscript.record.RECORD" />
<action android:name="com.wearscript.record.RECORD_RESULT" />
</intent-filter>
</receiver>

<service
android:name="com.wearscript.record.AudioRecorder"
android:enabled="true"
android:label="@string/app_name">

<intent-filter>
<action android:name="com.wearscript.record.RECORD_AUDIO" />
<action android:name="com.wearscript.record.SAVE_AUDIO" />
<action android:name="com.wearscript.record.STOP_AUDIO" />
</intent-filter>

</service>


</application>

Expand Down
210 changes: 210 additions & 0 deletions video/src/main/java/com/wearscript/record/AudioRecordThread.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package com.wearscript.record;

import android.content.Context;
import android.content.Intent;
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;
public static final int WAV_HEADER_LENGTH = 44;
public static final String FILEPATH = "filepath";
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);

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 boolean pollingBuffer = false;

AudioRecord recorder = null;
FileOutputStream os = null;
String filePath;

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

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

Context context;

public AudioRecordThread(Context context, String filePath)
{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

put open brace at the end of previous line. Please apply this convention throughout

this.context = context;
writeWavHeader(filePath);
}

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

buffers = 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()) {
buffers.add(new byte[bufferSize]);
recorder.read(buffers.get(buffers.size() - 1), 0, bufferSize);
}
}
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();
context.stopService(new Intent(context, AudioRecorder.class));
Log.d(LOG_TAG, "Thread Terminated");
}
}

private void mergeBuffers() {
Log.d(LOG_TAG, "in mergeBuffers()");
int i;
int j;
int ix = 0;

ArrayList<byte[]> copy = (ArrayList<byte[]>) buffers.clone();
buffers = new ArrayList<byte[]>();

totalBuffer = new byte[copy.size() * bufferSize];
for (i = 0; i < copy.size(); i++) {
for(j = 0; j<bufferSize; j++) {
byte x = copy.get(i)[j];
totalBuffer[ix] = x;
ix++;
}
}
}

private String audioFileName(String fileName) {
return directoryAudio + File.separator + fileName + ".wav";
}

private void writeWavHeader(String filePath) {
byte header[] = new byte[WAV_HEADER_LENGTH];

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

/* Test whether Android media player will play back a WAV file whose header indicates the
* wrong length file! */
int totalAudioLen = Integer.MAX_VALUE - 36;
int totalDataLen = Integer.MAX_VALUE;

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);
header[33] = 0;
header[34] = 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);

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

public void stopRecording() {
Log.d(LOG_TAG, "in stopRecording()");
writeAudioDataToFile();

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

interrupt();
}

public void writeAudioDataToFile() {
mergeBuffers();

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

Intent intent = new Intent("com.wearscript.record.FILE_WRITTEN_AUDIO").putExtra(FILEPATH, filePath);
context.sendBroadcast(intent);
Log.d(LOG_TAG, "Sending broadcast: com.wearscript.record.FILE_WRITTEN_AUDIO");
}
}

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

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";

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

Log.d(LOG_TAG, "Service Started");
createDirectory();
}

@Override
public void onDestroy() {
Log.d(LOG_TAG, "Service Destroy");
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 createDirectory() {
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) {
if (intent.getAction().equals("com.wearscript.record.RECORD_AUDIO")) {
recorder = new AudioRecordThread(this, intent.getStringExtra("filePath"));
recorder.start();
} else if (intent.getAction().equals("com.wearscript.record.SAVE_AUDIO")) {
recorder.writeAudioDataToFile();
} else if (intent.getAction().equals("com.wearscript.record.STOP_AUDIO")) {
recorder.stopRecording();
stopSelf();
}
}
return 0;
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.wearscript.video;
package com.wearscript.record;

import android.app.Activity;
import android.content.Intent;
Expand All @@ -7,7 +7,6 @@
import android.os.Bundle;
import android.util.Log;
import android.widget.VideoView;

import java.io.File;

public class PlaybackActivity extends Activity implements MediaPlayer.OnPreparedListener,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.wearscript.video;
package com.wearscript.record;

import android.app.Activity;
import android.content.Intent;
Expand All @@ -21,8 +21,6 @@
import java.util.Date;
import java.util.List;

import com.wearscript.video.WearScriptBroadcastReceiver;

public class RecordActivity extends Activity implements SurfaceHolder.Callback {
private final static String TAG = "RecordActivity";
private final static boolean DBG = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.wearscript.video;
package com.wearscript.record;

import android.content.BroadcastReceiver;
import android.content.Context;
Expand All @@ -7,9 +7,9 @@

public class WearScriptBroadcastReceiver extends BroadcastReceiver {

public static final String PLAYBACK_ACTION = "com.wearscript.video.PLAYBACK";
public static final String RECORD_ACTION = "com.wearscript.video.RECORD";
public static final String RECORD_RESULT_ACTION = "com.wearscript.video.RECORD_RESULT";
public static final String PLAYBACK_ACTION = "com.wearscript.record.PLAYBACK";
public static final String RECORD_ACTION = "com.wearscript.record.RECORD";
public static final String RECORD_RESULT_ACTION = "com.wearscript.record.RECORD_RESULT";
private static final String TAG = "WearScriptBroadcastReceiver";
private static final boolean DBG = true;

Expand Down
2 changes: 1 addition & 1 deletion video/src/main/res/layout/activity_play.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.wearscript.video.PlaybackActivity">
tools:context="com.wearscript.record.PlaybackActivity">

<VideoView
android:id = "@+id/video"
Expand Down
2 changes: 1 addition & 1 deletion video/src/main/res/layout/activity_record.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
android:layout_height="match_parent"
android:id="@+id/camera_preview"
tools:ignore="MergeRootFrame"
tools:context="com.wearscript.video.RecordActivity">
tools:context="com.wearscript.record.RecordActivity">

</FrameLayout>
Loading