1 /* 2 * Copyright (C) 2011 The Android Open Source Project 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.android.soundrecorder; 18 19 import java.io.File; 20 import java.io.IOException; 21 22 import android.content.Context; 23 import android.media.AudioManager; 24 import android.media.MediaPlayer; 25 import android.media.MediaRecorder; 26 import android.media.MediaPlayer.OnCompletionListener; 27 import android.media.MediaPlayer.OnErrorListener; 28 import android.os.Bundle; 29 import android.os.Environment; 30 import android.util.Log; 31 32 public class Recorder implements OnCompletionListener, OnErrorListener { 33 static final String SAMPLE_PREFIX = "recording"; 34 static final String SAMPLE_PATH_KEY = "sample_path"; 35 static final String SAMPLE_LENGTH_KEY = "sample_length"; 36 37 public static final int IDLE_STATE = 0; 38 public static final int RECORDING_STATE = 1; 39 public static final int PLAYING_STATE = 2; 40 41 int mState = IDLE_STATE; 42 43 public static final int NO_ERROR = 0; 44 public static final int SDCARD_ACCESS_ERROR = 1; 45 public static final int INTERNAL_ERROR = 2; 46 public static final int IN_CALL_RECORD_ERROR = 3; 47 48 public interface OnStateChangedListener { onStateChanged(int state)49 public void onStateChanged(int state); onError(int error)50 public void onError(int error); 51 } 52 OnStateChangedListener mOnStateChangedListener = null; 53 54 long mSampleStart = 0; // time at which latest record or play operation started 55 int mSampleLength = 0; // length of current sample 56 File mSampleFile = null; 57 58 MediaRecorder mRecorder = null; 59 MediaPlayer mPlayer = null; 60 Recorder()61 public Recorder() { 62 } 63 saveState(Bundle recorderState)64 public void saveState(Bundle recorderState) { 65 recorderState.putString(SAMPLE_PATH_KEY, mSampleFile.getAbsolutePath()); 66 recorderState.putInt(SAMPLE_LENGTH_KEY, mSampleLength); 67 } 68 getMaxAmplitude()69 public int getMaxAmplitude() { 70 if (mState != RECORDING_STATE) 71 return 0; 72 return mRecorder.getMaxAmplitude(); 73 } 74 restoreState(Bundle recorderState)75 public void restoreState(Bundle recorderState) { 76 String samplePath = recorderState.getString(SAMPLE_PATH_KEY); 77 if (samplePath == null) 78 return; 79 int sampleLength = recorderState.getInt(SAMPLE_LENGTH_KEY, -1); 80 if (sampleLength == -1) 81 return; 82 83 File file = new File(samplePath); 84 if (!file.exists()) 85 return; 86 if (mSampleFile != null 87 && mSampleFile.getAbsolutePath().compareTo(file.getAbsolutePath()) == 0) 88 return; 89 90 delete(); 91 mSampleFile = file; 92 mSampleLength = sampleLength; 93 94 signalStateChanged(IDLE_STATE); 95 } 96 setOnStateChangedListener(OnStateChangedListener listener)97 public void setOnStateChangedListener(OnStateChangedListener listener) { 98 mOnStateChangedListener = listener; 99 } 100 state()101 public int state() { 102 return mState; 103 } 104 progress()105 public int progress() { 106 if (mState == RECORDING_STATE || mState == PLAYING_STATE) 107 return (int) ((System.currentTimeMillis() - mSampleStart)/1000); 108 return 0; 109 } 110 sampleLength()111 public int sampleLength() { 112 return mSampleLength; 113 } 114 sampleFile()115 public File sampleFile() { 116 return mSampleFile; 117 } 118 119 /** 120 * Resets the recorder state. If a sample was recorded, the file is deleted. 121 */ delete()122 public void delete() { 123 stop(); 124 125 if (mSampleFile != null) 126 mSampleFile.delete(); 127 128 mSampleFile = null; 129 mSampleLength = 0; 130 131 signalStateChanged(IDLE_STATE); 132 } 133 134 /** 135 * Resets the recorder state. If a sample was recorded, the file is left on disk and will 136 * be reused for a new recording. 137 */ clear()138 public void clear() { 139 stop(); 140 141 mSampleLength = 0; 142 143 signalStateChanged(IDLE_STATE); 144 } 145 startRecording(int outputfileformat, String extension, Context context)146 public void startRecording(int outputfileformat, String extension, Context context) { 147 stop(); 148 149 if (mSampleFile == null) { 150 File sampleDir = Environment.getExternalStorageDirectory(); 151 if (!sampleDir.canWrite()) // Workaround for broken sdcard support on the device. 152 sampleDir = new File("/sdcard/sdcard"); 153 154 try { 155 mSampleFile = File.createTempFile(SAMPLE_PREFIX, extension, sampleDir); 156 } catch (IOException e) { 157 setError(SDCARD_ACCESS_ERROR); 158 return; 159 } 160 } 161 162 mRecorder = new MediaRecorder(); 163 mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); 164 mRecorder.setOutputFormat(outputfileformat); 165 mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); 166 mRecorder.setOutputFile(mSampleFile.getAbsolutePath()); 167 168 // Handle IOException 169 try { 170 mRecorder.prepare(); 171 } catch(IOException exception) { 172 setError(INTERNAL_ERROR); 173 mRecorder.reset(); 174 mRecorder.release(); 175 mRecorder = null; 176 return; 177 } 178 // Handle RuntimeException if the recording couldn't start 179 try { 180 mRecorder.start(); 181 } catch (RuntimeException exception) { 182 AudioManager audioMngr = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); 183 boolean isInCall = ((audioMngr.getMode() == AudioManager.MODE_IN_CALL) || 184 (audioMngr.getMode() == AudioManager.MODE_IN_COMMUNICATION)); 185 if (isInCall) { 186 setError(IN_CALL_RECORD_ERROR); 187 } else { 188 setError(INTERNAL_ERROR); 189 } 190 mRecorder.reset(); 191 mRecorder.release(); 192 mRecorder = null; 193 return; 194 } 195 mSampleStart = System.currentTimeMillis(); 196 setState(RECORDING_STATE); 197 } 198 stopRecording()199 public void stopRecording() { 200 if (mRecorder == null) 201 return; 202 203 mRecorder.stop(); 204 mRecorder.release(); 205 mRecorder = null; 206 207 mSampleLength = (int)( (System.currentTimeMillis() - mSampleStart)/1000 ); 208 setState(IDLE_STATE); 209 } 210 startPlayback()211 public void startPlayback() { 212 stop(); 213 214 mPlayer = new MediaPlayer(); 215 try { 216 mPlayer.setDataSource(mSampleFile.getAbsolutePath()); 217 mPlayer.setOnCompletionListener(this); 218 mPlayer.setOnErrorListener(this); 219 mPlayer.prepare(); 220 mPlayer.start(); 221 } catch (IllegalArgumentException e) { 222 setError(INTERNAL_ERROR); 223 mPlayer = null; 224 return; 225 } catch (IOException e) { 226 setError(SDCARD_ACCESS_ERROR); 227 mPlayer = null; 228 return; 229 } 230 231 mSampleStart = System.currentTimeMillis(); 232 setState(PLAYING_STATE); 233 } 234 stopPlayback()235 public void stopPlayback() { 236 if (mPlayer == null) // we were not in playback 237 return; 238 239 mPlayer.stop(); 240 mPlayer.release(); 241 mPlayer = null; 242 setState(IDLE_STATE); 243 } 244 stop()245 public void stop() { 246 stopRecording(); 247 stopPlayback(); 248 } 249 onError(MediaPlayer mp, int what, int extra)250 public boolean onError(MediaPlayer mp, int what, int extra) { 251 stop(); 252 setError(SDCARD_ACCESS_ERROR); 253 return true; 254 } 255 onCompletion(MediaPlayer mp)256 public void onCompletion(MediaPlayer mp) { 257 stop(); 258 } 259 setState(int state)260 private void setState(int state) { 261 if (state == mState) 262 return; 263 264 mState = state; 265 signalStateChanged(mState); 266 } 267 signalStateChanged(int state)268 private void signalStateChanged(int state) { 269 if (mOnStateChangedListener != null) 270 mOnStateChangedListener.onStateChanged(state); 271 } 272 setError(int error)273 private void setError(int error) { 274 if (mOnStateChangedListener != null) 275 mOnStateChangedListener.onError(error); 276 } 277 } 278