Skip to content

Most visited

Recently visited

navigation
WearSpeakerSample / src / com.example.android.wearable.speaker /

SoundRecorder.java

1
/*
2
 * Copyright (C) 2015 Google Inc. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
 
17
package com.example.android.wearable.speaker;
18
 
19
import android.content.Context;
20
import android.media.AudioFormat;
21
import android.media.AudioManager;
22
import android.media.AudioRecord;
23
import android.media.AudioTrack;
24
import android.media.MediaRecorder;
25
import android.os.AsyncTask;
26
import android.os.Handler;
27
import android.os.Looper;
28
import android.util.Log;
29
 
30
import java.io.BufferedInputStream;
31
import java.io.BufferedOutputStream;
32
import java.io.File;
33
import java.io.FileInputStream;
34
import java.io.IOException;
35
 
36
/**
37
 * A helper class to provide methods to record audio input from the MIC to the internal storage
38
 * and to playback the same recorded audio file.
39
 */
40
public class SoundRecorder {
41
 
42
    private static final String TAG = "SoundRecorder";
43
    private static final int RECORDING_RATE = 8000; // can go up to 44K, if needed
44
    private static final int CHANNEL_IN = AudioFormat.CHANNEL_IN_MONO;
45
    private static final int CHANNELS_OUT = AudioFormat.CHANNEL_OUT_MONO;
46
    private static final int FORMAT = AudioFormat.ENCODING_PCM_16BIT;
47
    private static int BUFFER_SIZE = AudioRecord
48
            .getMinBufferSize(RECORDING_RATE, CHANNEL_IN, FORMAT);
49
 
50
    private final String mOutputFileName;
51
    private final AudioManager mAudioManager;
52
    private final Handler mHandler;
53
    private final Context mContext;
54
    private State mState = State.IDLE;
55
 
56
    private OnVoicePlaybackStateChangedListener mListener;
57
    private AsyncTask<Void, Void, Void> mRecordingAsyncTask;
58
    private AsyncTask<Void, Void, Void> mPlayingAsyncTask;
59
 
60
    enum State {
61
        IDLE, RECORDING, PLAYING
62
    }
63
 
64
    public SoundRecorder(Context context, String outputFileName,
65
            OnVoicePlaybackStateChangedListener listener) {
66
        mOutputFileName = outputFileName;
67
        mListener = listener;
68
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
69
        mHandler = new Handler(Looper.getMainLooper());
70
        mContext = context;
71
    }
72
 
73
    /**
74
     * Starts recording from the MIC.
75
     */
76
    public void startRecording() {
77
        if (mState != State.IDLE) {
78
            Log.w(TAG, "Requesting to start recording while state was not IDLE");
79
            return;
80
        }
81
 
82
        mRecordingAsyncTask = new AsyncTask<Void, Void, Void>() {
83
 
84
            private AudioRecord mAudioRecord;
85
 
86
            @Override
87
            protected void onPreExecute() {
88
                mState = State.RECORDING;
89
            }
90
 
91
            @Override
92
            protected Void doInBackground(Void... params) {
93
                mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
94
                        RECORDING_RATE, CHANNEL_IN, FORMAT, BUFFER_SIZE * 3);
95
                BufferedOutputStream bufferedOutputStream = null;
96
                try {
97
                    bufferedOutputStream = new BufferedOutputStream(
98
                            mContext.openFileOutput(mOutputFileName, Context.MODE_PRIVATE));
99
                    byte[] buffer = new byte[BUFFER_SIZE];
100
                    mAudioRecord.startRecording();
101
                    while (!isCancelled()) {
102
                        int read = mAudioRecord.read(buffer, 0, buffer.length);
103
                        bufferedOutputStream.write(buffer, 0, read);
104
                    }
105
                } catch (IOException | NullPointerException | IndexOutOfBoundsException e) {
106
                    Log.e(TAG, "Failed to record data: " + e);
107
                } finally {
108
                    if (bufferedOutputStream != null) {
109
                        try {
110
                            bufferedOutputStream.close();
111
                        } catch (IOException e) {
112
                            // ignore
113
                        }
114
                    }
115
                    mAudioRecord.release();
116
                    mAudioRecord = null;
117
                }
118
                return null;
119
            }
120
 
121
            @Override
122
            protected void onPostExecute(Void aVoid) {
123
                mState = State.IDLE;
124
                mRecordingAsyncTask = null;
125
            }
126
 
127
            @Override
128
            protected void onCancelled() {
129
                if (mState == State.RECORDING) {
130
                    Log.d(TAG, "Stopping the recording ...");
131
                    mState = State.IDLE;
132
                } else {
133
                    Log.w(TAG, "Requesting to stop recording while state was not RECORDING");
134
                }
135
                mRecordingAsyncTask = null;
136
            }
137
        };
138
 
139
        mRecordingAsyncTask.execute();
140
    }
141
 
142
    public void stopRecording() {
143
        if (mRecordingAsyncTask != null) {
144
            mRecordingAsyncTask.cancel(true);
145
        }
146
    }
147
 
148
    public void stopPlaying() {
149
        if (mPlayingAsyncTask != null) {
150
            mPlayingAsyncTask.cancel(true);
151
        }
152
    }
153
 
154
    /**
155
     * Starts playback of the recorded audio file.
156
     */
157
    public void startPlay() {
158
        if (mState != State.IDLE) {
159
            Log.w(TAG, "Requesting to play while state was not IDLE");
160
            return;
161
        }
162
 
163
        if (!new File(mContext.getFilesDir(), mOutputFileName).exists()) {
164
            // there is no recording to play
165
            if (mListener != null) {
166
                mHandler.post(new Runnable() {
167
                    @Override
168
                    public void run() {
169
                        mListener.onPlaybackStopped();
170
                    }
171
                });
172
            }
173
            return;
174
        }
175
        final int intSize = AudioTrack.getMinBufferSize(RECORDING_RATE, CHANNELS_OUT, FORMAT);
176
 
177
        mPlayingAsyncTask = new AsyncTask<Void, Void, Void>() {
178
 
179
            private AudioTrack mAudioTrack;
180
 
181
            @Override
182
            protected void onPreExecute() {
183
                mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
184
                        mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0 /* flags */);
185
                mState = State.PLAYING;
186
            }
187
 
188
            @Override
189
            protected Void doInBackground(Void... params) {
190
                try {
191
                    mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, RECORDING_RATE,
192
                            CHANNELS_OUT, FORMAT, intSize, AudioTrack.MODE_STREAM);
193
                    byte[] buffer = new byte[intSize * 2];
194
                    FileInputStream in = null;
195
                    BufferedInputStream bis = null;
196
                    mAudioTrack.setVolume(AudioTrack.getMaxVolume());
197
                    mAudioTrack.play();
198
                    try {
199
                        in = mContext.openFileInput(mOutputFileName);
200
                        bis = new BufferedInputStream(in);
201
                        int read;
202
                        while (!isCancelled() && (read = bis.read(buffer, 0, buffer.length)) > 0) {
203
                            mAudioTrack.write(buffer, 0, read);
204
                        }
205
                    } catch (IOException e) {
206
                        Log.e(TAG, "Failed to read the sound file into a byte array", e);
207
                    } finally {
208
                        try {
209
                            if (in != null) {
210
                                in.close();
211
                            }
212
                            if (bis != null) {
213
                                bis.close();
214
                            }
215
                        } catch (IOException e) { /* ignore */}
216
 
217
                        mAudioTrack.release();
218
                    }
219
                } catch (IllegalStateException e) {
220
                    Log.e(TAG, "Failed to start playback", e);
221
                }
222
                return null;
223
            }
224
 
225
            @Override
226
            protected void onPostExecute(Void aVoid) {
227
                cleanup();
228
            }
229
 
230
            @Override
231
            protected void onCancelled() {
232
                cleanup();
233
            }
234
 
235
            private void cleanup() {
236
                if (mListener != null) {
237
                    mListener.onPlaybackStopped();
238
                }
239
                mState = State.IDLE;
240
                mPlayingAsyncTask = null;
241
            }
242
        };
243
 
244
        mPlayingAsyncTask.execute();
245
    }
246
 
247
    public interface OnVoicePlaybackStateChangedListener {
248
 
249
        /**
250
         * Called when the playback of the audio file ends. This should be called on the UI thread.
251
         */
252
        void onPlaybackStopped();
253
    }
254
 
255
    /**
256
     * Cleans up some resources related to {@link AudioTrack} and {@link AudioRecord}
257
     */
258
    public void cleanup() {
259
        Log.d(TAG, "cleanup() is called");
260
        stopPlaying();
261
        stopRecording();
262
    }
263
}
This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android develop