Skip to content

Commit 37b7351

Browse files
committed
Add audio recording classes: AudioRecorder (a service) and AudioRecordThread (a thread which gets spawned by AudioRecorder)
1 parent 879cd50 commit 37b7351

File tree

2 files changed

+264
-0
lines changed

2 files changed

+264
-0
lines changed
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
package com.wearscript.audio;
2+
3+
import android.media.AudioFormat;
4+
import android.media.AudioRecord;
5+
import android.media.MediaRecorder.AudioSource;
6+
import android.os.Environment;
7+
import android.util.Log;
8+
9+
import java.io.File;
10+
import java.io.FileNotFoundException;
11+
import java.io.FileOutputStream;
12+
import java.io.IOException;
13+
import java.util.ArrayList;
14+
15+
public class AudioRecordThread extends Thread{
16+
17+
private static final String LOG_TAG = "Audio";
18+
19+
private static final int RECORDER_SAMPLERATE = 8000;
20+
private static final int ENCODING_TYPE = AudioFormat.ENCODING_PCM_16BIT;
21+
private final int CHANNEL_TYPE = AudioFormat.CHANNEL_IN_MONO;
22+
private final int NUM_CHANNELS = 1;
23+
private byte BITS_PER_SAMPLE = 16;
24+
private final int AUDIO_SOURCE = AudioSource.MIC;
25+
private final int BYTE_RATE = RECORDER_SAMPLERATE * NUM_CHANNELS * (BITS_PER_SAMPLE / 8);
26+
private final int RECORDING_SECS = 20;
27+
28+
public static final String directory = Environment.getExternalStorageDirectory() + File.separator + "wearscript";
29+
public static final String directoryAudio = directory + File.separator+"audio";
30+
31+
private final int bufferSize = 160; //Each buffer holds 1/100th of a second.
32+
private int numBuffers = 100 * RECORDING_SECS;
33+
private boolean pollingBuffer = false;
34+
private String latestFilePath = null;
35+
36+
AudioRecord recorder = null;
37+
38+
/**
39+
* Give the thread high priority so that it's not cancelled unexpectedly, and start it
40+
*/
41+
42+
private ArrayList<byte[]> buffers;
43+
private ArrayList<Byte> totalBuffer;
44+
45+
public AudioRecordThread()
46+
{
47+
//Try this at lower priorities.
48+
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
49+
}
50+
51+
@Override
52+
public void run()
53+
{
54+
Log.d(LOG_TAG, "Running Audio Thread");
55+
56+
buffers = new ArrayList<byte[]>();
57+
totalBuffer = new ArrayList<Byte>();
58+
int N = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,CHANNEL_TYPE,ENCODING_TYPE);
59+
Log.d(LOG_TAG, ""+N);
60+
try{
61+
recorder = new AudioRecord(AUDIO_SOURCE, RECORDER_SAMPLERATE, CHANNEL_TYPE, ENCODING_TYPE, N*10);
62+
}
63+
catch(Throwable e){
64+
Log.d(LOG_TAG, e.toString());
65+
return;
66+
}
67+
recorder.startRecording();
68+
69+
try{
70+
while(!interrupted())
71+
{
72+
if (!pollingBuffer){
73+
buffers.add(new byte[bufferSize]);
74+
recorder.read(buffers.get(buffers.size()-1),0,bufferSize);
75+
}
76+
else{
77+
pollRingBuffer(0);
78+
writeAudioDataToFile();
79+
pollingBuffer = false;
80+
Log.d(LOG_TAG, "Audio Saved");
81+
}
82+
}
83+
Log.d(LOG_TAG, "interrupted");
84+
}
85+
catch(Throwable x)
86+
{
87+
Log.d(LOG_TAG, "Error reading voice audio", x);
88+
}
89+
/*
90+
* Frees the thread's resources after the loop completes so that it can be run again
91+
*/
92+
finally
93+
{
94+
recorder.stop();
95+
recorder.release();
96+
Log.d(LOG_TAG, "Thread Terminated");
97+
}
98+
}
99+
100+
public String startPolling(long millis){
101+
pollingBuffer = true;
102+
latestFilePath = audioFileName(millis);
103+
return latestFilePath;
104+
}
105+
106+
private void pollRingBuffer(int ix){
107+
Log.d(LOG_TAG, "in pollRingBuffer()");
108+
int i;
109+
int j;
110+
for(i = 0; i<buffers.size(); i++){
111+
for(j = 0; j<bufferSize; j++){
112+
totalBuffer.add(buffers.get(ix + i)[j]);
113+
}
114+
}
115+
}
116+
117+
private String audioFileName(long millis) {
118+
return directoryAudio + File.separator + String.valueOf(millis) + ".wav";
119+
}
120+
//TODO what does this do and why is this here
121+
public String saveAudio(){
122+
return "Failed";
123+
}
124+
125+
private void writeAudioDataToFile() {
126+
int totalAudioLen = numBuffers * bufferSize;
127+
int totalDataLen = (totalAudioLen * NUM_CHANNELS * BITS_PER_SAMPLE / 8) + 36;
128+
//String filePath = audioFileName();
129+
byte header[] = new byte[44];
130+
byte wavFile[] = new byte[totalBuffer.size() + header.length];
131+
132+
FileOutputStream os = null;
133+
try {
134+
os = new FileOutputStream(latestFilePath);
135+
Log.d(LOG_TAG, "file path: " + latestFilePath);
136+
} catch (FileNotFoundException e) {
137+
e.printStackTrace();
138+
}
139+
140+
header[0] = 'R'; // RIFF/WAVE header
141+
header[1] = 'I';
142+
header[2] = 'F';
143+
header[3] = 'F';
144+
header[4] = (byte) (totalDataLen & 0xff);
145+
header[5] = (byte) ((totalDataLen >> 8) & 0xff);
146+
header[6] = (byte) ((totalDataLen >> 16) & 0xff);
147+
header[7] = (byte) ((totalDataLen >> 24) & 0xff);
148+
header[8] = 'W';
149+
header[9] = 'A';
150+
header[10] = 'V';
151+
header[11] = 'E';
152+
header[12] = 'f'; // 'fmt ' chunk
153+
header[13] = 'm';
154+
header[14] = 't';
155+
header[15] = ' ';
156+
header[16] = 16; // 4 bytes: size of 'fmt ' chunk
157+
header[17] = 0;
158+
header[18] = 0;
159+
header[19] = 0;
160+
header[20] = 1; // format = 1
161+
header[21] = 0;
162+
header[22] = (byte) NUM_CHANNELS;
163+
header[23] = 0;
164+
header[24] = (byte) (RECORDER_SAMPLERATE & 0xff);
165+
header[25] = (byte) ((RECORDER_SAMPLERATE >> 8) & 0xff);
166+
header[26] = (byte) ((RECORDER_SAMPLERATE >> 16) & 0xff);
167+
header[27] = (byte) ((RECORDER_SAMPLERATE >> 24) & 0xff);
168+
header[28] = (byte) (BYTE_RATE & 0xff);
169+
header[29] = (byte) ((BYTE_RATE >> 8) & 0xff);
170+
header[30] = (byte) ((BYTE_RATE >> 16) & 0xff);
171+
header[31] = (byte) ((BYTE_RATE >> 24) & 0xff);
172+
header[32] = (byte) (NUM_CHANNELS * BITS_PER_SAMPLE / 8);//(2 * 16 / 8); // block align (might be half what it should be)
173+
header[33] = 0;
174+
header[34] = BITS_PER_SAMPLE; // bits per sample
175+
header[35] = 0;
176+
header[36] = 'd';
177+
header[37] = 'a';
178+
header[38] = 't';
179+
header[39] = 'a';
180+
header[40] = (byte) (totalAudioLen & 0xff);
181+
header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
182+
header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
183+
header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
184+
185+
System.arraycopy(header, 0, wavFile, 0, header.length);
186+
System.arraycopy(totalBuffer, 0, wavFile, header.length, totalBuffer.size());
187+
188+
try {
189+
os.write(wavFile, 0, wavFile.length);
190+
} catch (IOException e) {
191+
e.printStackTrace();
192+
}
193+
194+
try {
195+
os.close();
196+
} catch (IOException e) {
197+
e.printStackTrace();
198+
}
199+
}
200+
}
201+
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.wearscript.audio;
2+
3+
import android.app.Service;
4+
import android.content.Intent;
5+
import android.os.IBinder;
6+
import android.util.Log;
7+
8+
import java.io.File;
9+
10+
public class AudioRecorder extends Service {
11+
12+
private final String LOG_TAG = "AudioRecorder";
13+
14+
private AudioRecordThread recorder;
15+
public static String MILLIS_EXTRA_KEY = "millis";
16+
17+
public AudioRecorder() {
18+
}
19+
20+
@Override
21+
public void onCreate() {
22+
super.onCreate();
23+
24+
Log.d("Memora", "Service Started");
25+
createMemoraDirectory();
26+
27+
recorder = new AudioRecordThread();
28+
recorder.start();
29+
}
30+
31+
@Override
32+
public void onDestroy() {
33+
Log.d("Memora", "Service Destroy");
34+
recorder.interrupt();
35+
super.onDestroy();
36+
}
37+
38+
@Override
39+
public IBinder onBind(Intent intent) {
40+
// TODO: Return the communication channel to the service.
41+
throw new UnsupportedOperationException("Not yet implemented");
42+
}
43+
44+
private void createMemoraDirectory(){
45+
File directory = new File(AudioRecordThread.directoryAudio);
46+
if (!directory.isDirectory()){
47+
directory.mkdirs();
48+
}
49+
}
50+
51+
@Override
52+
public int onStartCommand(Intent intent, int flags, int startId) {
53+
if (intent.getAction() != null && intent.getAction().equals("save_audio_intent")) {
54+
Log.d(LOG_TAG, "Got message");
55+
long millis = intent.getExtras().getLong(MILLIS_EXTRA_KEY);
56+
Log.d(LOG_TAG, "millis: " + millis);
57+
String filepath = recorder.startPolling(millis);
58+
Log.d(LOG_TAG, "filepath: " + filepath);
59+
}
60+
return 0;
61+
}
62+
63+
}

0 commit comments

Comments
 (0)