AudioRecord.java revision d24b8183b93e781080b2c16c487e60d51c12da31
1/* 2 * Copyright (C) 2008 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 17package android.media; 18 19import java.lang.ref.WeakReference; 20import java.io.OutputStream; 21import java.io.IOException; 22import java.lang.IllegalArgumentException; 23import java.lang.IllegalStateException; 24import java.lang.Thread; 25import java.nio.ByteBuffer; 26 27import android.os.Handler; 28import android.os.Looper; 29import android.os.Message; 30import android.util.Log; 31 32/** 33 * The AudioRecord class manages the audio resources for Java applications 34 * to record audio from the audio input hardware of the platform. This is 35 * achieved by "pulling" (reading) the data from the AudioRecord object. The 36 * application is responsible for polling the AudioRecord object in time using one of 37 * the following three methods: {@link #read(byte[],int, int)}, {@link #read(short[], int, int)} 38 * or {@link #read(ByteBuffer, int)}. The choice of which method to use will be based 39 * on the audio data storage format that is the most convenient for the user of AudioRecord. 40 * <p>Upon creation, an AudioRecord object initializes its associated audio buffer that it will 41 * fill with the new audio data. The size of this buffer, specified during the construction, 42 * determines how long an AudioRecord can record before "over-running" data that has not 43 * been read yet. Data should be from the audio hardware in chunks of sizes inferior to 44 * the total recording buffer size. 45 */ 46public class AudioRecord 47{ 48 //--------------------------------------------------------- 49 // Constants 50 //-------------------- 51 /** 52 * State of an AudioRecord that was not successfully initialized upon creation 53 */ 54 public static final int STATE_UNINITIALIZED = 0; 55 /** 56 * State of an AudioRecord that is ready to be used 57 */ 58 public static final int STATE_INITIALIZED = 1; 59 60 /** 61 * State of an AudioRecord this is not recording 62 */ 63 public static final int RECORDSTATE_STOPPED = 1; // matches SL_RECORDSTATE_STOPPED 64 /** 65 * State of an AudioRecord this is recording 66 */ 67 public static final int RECORDSTATE_RECORDING = 3;// matches SL_RECORDSTATE_RECORDING 68 69 // Error codes: 70 // to keep in sync with frameworks/base/core/jni/android_media_AudioRecord.cpp 71 /** 72 * Denotes a successful operation. 73 */ 74 public static final int SUCCESS = 0; 75 /** 76 * Denotes a generic operation failure. 77 */ 78 public static final int ERROR = -1; 79 /** 80 * Denotes a failure due to the use of an invalid value. 81 */ 82 public static final int ERROR_BAD_VALUE = -2; 83 /** 84 * Denotes a failure due to the improper use of a method. 85 */ 86 public static final int ERROR_INVALID_OPERATION = -3; 87 88 private static final int AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT = -16; 89 private static final int AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT = -17; 90 private static final int AUDIORECORD_ERROR_SETUP_INVALIDFORMAT = -18; 91 private static final int AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE = -19; 92 private static final int AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED = -20; 93 94 // Events: 95 // to keep in sync with frameworks/base/include/media/AudioRecord.h 96 /** 97 * Event id for when the recording head has reached a previously set marker. 98 */ 99 private static final int NATIVE_EVENT_MARKER = 2; 100 /** 101 * Event id for when the previously set update period has passed during recording. 102 */ 103 private static final int NATIVE_EVENT_NEW_POS = 3; 104 105 private final static String TAG = "AudioRecord-Java"; 106 107 108 //--------------------------------------------------------- 109 // Used exclusively by native code 110 //-------------------- 111 /** 112 * Accessed by native methods: provides access to C++ AudioRecord object 113 */ 114 @SuppressWarnings("unused") 115 private int mNativeRecorderInJavaObj; 116 /** 117 * Accessed by native methods: provides access to record source constants 118 */ 119 @SuppressWarnings("unused") 120 private final static int SOURCE_DEFAULT = MediaRecorder.AudioSource.DEFAULT; 121 @SuppressWarnings("unused") 122 private final static int SOURCE_MIC = MediaRecorder.AudioSource.MIC; 123 /** 124 * Accessed by native methods: provides access to the callback data. 125 */ 126 @SuppressWarnings("unused") 127 private int mNativeCallbackCookie; 128 129 130 //--------------------------------------------------------- 131 // Member variables 132 //-------------------- 133 /** 134 * The audio data sampling rate in Hz. 135 */ 136 private int mSampleRate = 22050; 137 /** 138 * The number of input audio channels (1 is mono, 2 is stereo) 139 */ 140 private int mChannelCount = 1; 141 /** 142 * The current audio channel configuration 143 */ 144 private int mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; 145 /** 146 * The encoding of the audio samples. 147 * @see AudioFormat#ENCODING_PCM_8BIT 148 * @see AudioFormat#ENCODING_PCM_16BIT 149 */ 150 private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; 151 /** 152 * Where the audio data is recorded from. 153 */ 154 private int mRecordSource = MediaRecorder.AudioSource.DEFAULT; 155 /** 156 * Indicates the state of the AudioRecord instance. 157 */ 158 private int mState = STATE_UNINITIALIZED; 159 /** 160 * Indicates the recording state of the AudioRecord instance. 161 */ 162 private int mRecordingState = RECORDSTATE_STOPPED; 163 /** 164 * Lock to make sure mRecordingState updates are reflecting the actual state of the object. 165 */ 166 private Object mRecordingStateLock = new Object(); 167 /** 168 * The listener the AudioRecord notifies when a previously set marker is reached. 169 * @see #setMarkerReachedListener(OnMarkerReachedListener) 170 */ 171 private OnMarkerReachedListener mMarkerListener = null; 172 /** 173 * Lock to protect marker listener updates against event notifications 174 */ 175 private final Object mMarkerListenerLock = new Object(); 176 /** 177 * The listener the AudioRecord notifies periodically during recording. 178 * @see #setPeriodicNotificationListener(OnPeriodicNotificationListener) 179 */ 180 private OnPeriodicNotificationListener mPeriodicListener = null; 181 /** 182 * Lock to protect periodic listener updates against event notifications 183 */ 184 private final Object mPeriodicListenerLock = new Object(); 185 /** 186 * Handler for events coming from the native code 187 */ 188 private NativeEventHandler mNativeEventHandler = null; 189 /** 190 * Size of the native audio buffer. 191 */ 192 private int mNativeBufferSizeInBytes = 0; 193 194 195 //--------------------------------------------------------- 196 // Constructor, Finalize 197 //-------------------- 198 /** 199 * Class constructor. 200 * @param audioSource the recording source. See {@link MediaRecorder.AudioSource} for 201 * recording source definitions. 202 * @param sampleRateInHz the sample rate expressed in Hertz. Examples of rates are (but 203 * not limited to) 44100, 22050 and 11025. 204 * @param channelConfig describes the configuration of the audio channels. 205 * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} and 206 * {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO} 207 * @param audioFormat the format in which the audio data is represented. 208 * See {@link AudioFormat#ENCODING_PCM_16BIT} and 209 * {@link AudioFormat#ENCODING_PCM_8BIT} 210 * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written 211 * to during the recording. New audio data can be read from this buffer in smaller chunks 212 * than this size. 213 * @throws java.lang.IllegalArgumentException 214 */ 215 public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, 216 int bufferSizeInBytes) 217 throws IllegalArgumentException { 218 mState = STATE_UNINITIALIZED; 219 mRecordingState = RECORDSTATE_STOPPED; 220 221 audioParamCheck(audioSource, sampleRateInHz, channelConfig, audioFormat); 222 223 audioBuffSizeCheck(bufferSizeInBytes); 224 225 // native initialization 226 //TODO: update native initialization when information about hardware init failure 227 // due to capture device already open is available. 228 int initResult = native_setup( new WeakReference<AudioRecord>(this), 229 mRecordSource, mSampleRate, mChannelCount, mAudioFormat, mNativeBufferSizeInBytes); 230 if (initResult != SUCCESS) { 231 loge("Error code "+initResult+" when initializing native AudioRecord object."); 232 return; // with mState == STATE_UNINITIALIZED 233 } 234 235 mState = STATE_INITIALIZED; 236 } 237 238 239 // Convenience method for the constructor's parameter checks. 240 // This is where constructor IllegalArgumentException-s are thrown 241 // postconditions: 242 // mRecordSource is valid 243 // mChannelCount is valid 244 // mAudioFormat is valid 245 // mSampleRate is valid 246 private void audioParamCheck(int audioSource, int sampleRateInHz, 247 int channelConfig, int audioFormat) { 248 249 //-------------- 250 // audio source 251 if ( (audioSource != MediaRecorder.AudioSource.DEFAULT) 252 && (audioSource != MediaRecorder.AudioSource.MIC) ) { 253 throw (new IllegalArgumentException("Invalid audio source.")); 254 } else { 255 mRecordSource = audioSource; 256 } 257 258 //-------------- 259 // sample rate 260 if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) { 261 throw (new IllegalArgumentException(sampleRateInHz 262 + "Hz is not a supported sample rate.")); 263 } else { 264 mSampleRate = sampleRateInHz; 265 } 266 267 //-------------- 268 // channel config 269 switch (channelConfig) { 270 case AudioFormat.CHANNEL_CONFIGURATION_DEFAULT: 271 case AudioFormat.CHANNEL_CONFIGURATION_MONO: 272 mChannelCount = 1; 273 mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; 274 break; 275 case AudioFormat.CHANNEL_CONFIGURATION_STEREO: 276 mChannelCount = 2; 277 mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_STEREO; 278 break; 279 default: 280 mChannelCount = 0; 281 mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_INVALID; 282 throw (new IllegalArgumentException("Unsupported channel configuration.")); 283 } 284 285 //-------------- 286 // audio format 287 switch (audioFormat) { 288 case AudioFormat.ENCODING_DEFAULT: 289 mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; 290 break; 291 case AudioFormat.ENCODING_PCM_16BIT: 292 case AudioFormat.ENCODING_PCM_8BIT: 293 mAudioFormat = audioFormat; 294 break; 295 default: 296 mAudioFormat = AudioFormat.ENCODING_INVALID; 297 throw (new IllegalArgumentException("Unsupported sample encoding." 298 + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT.")); 299 } 300 } 301 302 303 // Convenience method for the contructor's audio buffer size check. 304 // preconditions: 305 // mChannelCount is valid 306 // mAudioFormat is AudioFormat.ENCODING_PCM_8BIT OR AudioFormat.ENCODING_PCM_16BIT 307 // postcondition: 308 // mNativeBufferSizeInBytes is valid (multiple of frame size, positive) 309 private void audioBuffSizeCheck(int audioBufferSize) { 310 // NB: this section is only valid with PCM data. 311 // To update when supporting compressed formats 312 int frameSizeInBytes = mChannelCount 313 * (mAudioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2); 314 if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) { 315 throw (new IllegalArgumentException("Invalid audio buffer size.")); 316 } 317 318 mNativeBufferSizeInBytes = audioBufferSize; 319 } 320 321 322 // Convenience method for the creation of the native event handler 323 // It is called only when a non-null event listener is set. 324 // precondition: 325 // mNativeEventHandler is null 326 private void createNativeEventHandler() { 327 Looper looper; 328 if ((looper = Looper.myLooper()) != null) { 329 mNativeEventHandler = new NativeEventHandler(this, looper); 330 } else if ((looper = Looper.getMainLooper()) != null) { 331 mNativeEventHandler = new NativeEventHandler(this, looper); 332 } else { 333 mNativeEventHandler = null; 334 } 335 } 336 337 338 /** 339 * Releases the native AudioRecord resources. 340 */ 341 public void release() { 342 stop(); 343 native_release(); 344 mState = STATE_UNINITIALIZED; 345 } 346 347 348 @Override 349 protected void finalize() { 350 native_finalize(); 351 } 352 353 354 //-------------------------------------------------------------------------- 355 // Getters 356 //-------------------- 357 /** 358 * Returns the configured audio data sample rate in Hz 359 */ 360 public int getSampleRate() { 361 return mSampleRate; 362 } 363 364 /** 365 * Returns the audio recording source. 366 * @see MediaRecorder.AudioSource 367 */ 368 public int getAudioSource() { 369 return mRecordSource; 370 } 371 372 /** 373 * Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_16BIT} 374 * and {@link AudioFormat#ENCODING_PCM_8BIT}. 375 */ 376 public int getAudioFormat() { 377 return mAudioFormat; 378 } 379 380 /** 381 * Returns the configured channel configuration. 382 * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} 383 * and {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO}. 384 */ 385 public int getChannelConfiguration() { 386 return mChannelConfiguration; 387 } 388 389 /** 390 * Returns the configured number of channels. 391 */ 392 public int getChannelCount() { 393 return mChannelCount; 394 } 395 396 /** 397 * Returns the state of the AudioRecord instance. This is useful after the 398 * AudioRecord instance has been created to check if it was initialized 399 * properly. This ensures that the appropriate hardware resources have been 400 * acquired. 401 * @see AudioRecord#STATE_INITIALIZED 402 * @see AudioRecord#STATE_UNINITIALIZED 403 */ 404 public int getState() { 405 return mState; 406 } 407 408 /** 409 * Returns the recording state of the AudioRecord instance. 410 * @see AudioRecord#RECORDSTATE_STOPPED 411 * @see AudioRecord#RECORDSTATE_RECORDING 412 */ 413 public int getRecordingState() { 414 return mRecordingState; 415 } 416 417 /** 418 * @return marker position in frames 419 */ 420 public int getNotificationMarkerPosition() { 421 return native_get_marker_pos(); 422 } 423 424 /** 425 * @return update period in frames 426 */ 427 public int getPositionNotificationPeriod() { 428 return native_get_pos_update_period(); 429 } 430 431 432 //--------------------------------------------------------- 433 // Transport control methods 434 //-------------------- 435 /** 436 * Starts recording from the AudioRecord instance. 437 * @throws IllegalStateException 438 */ 439 public void startRecording() 440 throws IllegalStateException { 441 if (mState != STATE_INITIALIZED) { 442 throw(new IllegalStateException("startRecording() called on an " 443 +"uninitialized AudioRecord.")); 444 } 445 446 // start recording 447 synchronized(mRecordingStateLock) { 448 native_start(); 449 mRecordingState = RECORDSTATE_RECORDING; 450 } 451 } 452 453 454 455 /** 456 * Stops recording. 457 * @throws IllegalStateException 458 */ 459 public void stop() 460 throws IllegalStateException { 461 if (mState != STATE_INITIALIZED) { 462 throw(new IllegalStateException("stop() called on an uninitialized AudioRecord.")); 463 } 464 465 // stop recording 466 synchronized(mRecordingStateLock) { 467 native_stop(); 468 mRecordingState = RECORDSTATE_STOPPED; 469 } 470 } 471 472 473 //--------------------------------------------------------- 474 // Audio data supply 475 //-------------------- 476 /** 477 * Reads audio data from the audio hardware for recording into a buffer. 478 * @param audioData the array to which the recorded audio data is written. 479 * @param offsetInBytes index in audioData from which the data is written. 480 * @param sizeInBytes the number of requested bytes. 481 * @return the number of bytes that were read or -1 if the object wasn't properly 482 * initialized. The number of bytes will not exceed sizeInBytes. 483 */ 484 public int read(byte[] audioData, int offsetInBytes, int sizeInBytes) { 485 if (mState != STATE_INITIALIZED) { 486 return -1; 487 } 488 489 return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes); 490 } 491 492 493 /** 494 * Reads audio data from the audio hardware for recording into a buffer. 495 * @param audioData the array to which the recorded audio data is written. 496 * @param offsetInShorts index in audioData from which the data is written. 497 * @param sizeInShorts the number of requested shorts. 498 * @return the number of shorts that were read. or -1 if the object wasn't properly 499 * initialized. The number of shorts will not exceed sizeInShorts 500 */ 501 public int read(short[] audioData, int offsetInShorts, int sizeInShorts) { 502 if (mState != STATE_INITIALIZED) { 503 return -1; 504 } 505 506 return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts); 507 } 508 509 510 /** 511 * Reads audio data from the audio hardware for recording into a direct buffer. If this buffer 512 * is not a direct buffer, this method will always return 0. 513 * @param audioBuffer the direct buffer to which the recorded audio data is written. 514 * @param sizeInBytes the number of requested bytes. 515 * @return the number of bytes that were read or -1 if the object wasn't properly 516 * initialized. The number of bytes will not exceed sizeInBytes. 517 */ 518 public int read(ByteBuffer audioBuffer, int sizeInBytes) { 519 if (mState != STATE_INITIALIZED) { 520 return -1; 521 } 522 523 return native_read_in_direct_buffer(audioBuffer, sizeInBytes); 524 } 525 526 527 //-------------------------------------------------------------------------- 528 // Initialization / configuration 529 //-------------------- 530 /** 531 * Sets the listener the AudioRecord notifies when a previously set marker is reached. 532 * @param listener 533 */ 534 public void setMarkerReachedListener(OnMarkerReachedListener listener) { 535 synchronized (mMarkerListenerLock) { 536 mMarkerListener = listener; 537 } 538 if ((listener != null) && (mNativeEventHandler == null)) { 539 createNativeEventHandler(); 540 } 541 } 542 543 544 /** 545 * Sets the listener the AudioRecord notifies periodically during recording. 546 * @param listener 547 */ 548 public void setPeriodicNotificationListener(OnPeriodicNotificationListener listener) { 549 synchronized (mPeriodicListenerLock) { 550 mPeriodicListener = listener; 551 } 552 if ((listener != null) && (mNativeEventHandler == null)) { 553 createNativeEventHandler(); 554 } 555 } 556 557 558 /** 559 * Sets the marker position at which the listener, if set with 560 * {@link #setMarkerReachedListener(OnMarkerReachedListener)}, is called. 561 * @param markerInFrames marker position expressed in frames 562 * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE}, 563 * {@link #ERROR_INVALID_OPERATION} 564 */ 565 public int setNotificationMarkerPosition(int markerInFrames) { 566 return native_set_marker_pos(markerInFrames); 567 } 568 569 570 /** 571 * Sets the period at which the listener, if set with 572 * {@link #setPositionNotificationPeriod(int)}, is called. 573 * @param periodInFrames update period expressed in frames 574 * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_INVALID_OPERATION} 575 */ 576 public int setPositionNotificationPeriod(int periodInFrames) { 577 return native_set_pos_update_period(periodInFrames); 578 } 579 580 581 //--------------------------------------------------------- 582 // Interface definitions 583 //-------------------- 584 /** 585 * Interface definition for a callback to be invoked when an AudioRecord has 586 * reached a notification marker set by setNotificationMarkerPosition(). 587 */ 588 public interface OnMarkerReachedListener { 589 /** 590 * Called on the listener to notify it that the previously set marker has been reached 591 * by the recording head. 592 */ 593 void onMarkerReached(AudioRecord recorder); 594 } 595 596 597 /** 598 * Interface definition for a callback to be invoked for each periodic AudioRecord 599 * update during recording. The update interval is set by setPositionNotificationPeriod(). 600 */ 601 public interface OnPeriodicNotificationListener { 602 /** 603 * Called on the listener to periodically notify it that the recording head has reached 604 * a multiple of the notification period. 605 */ 606 void onPeriodicNotification(AudioRecord recorder); 607 } 608 609 610 //--------------------------------------------------------- 611 // Inner classes 612 //-------------------- 613 /** 614 * Helper class to handle the forwarding of native events to the appropriate listeners 615 */ 616 private class NativeEventHandler extends Handler 617 { 618 private AudioRecord mAudioRecord; 619 620 public NativeEventHandler(AudioRecord ar, Looper looper) { 621 super(looper); 622 mAudioRecord = ar; 623 } 624 625 @Override 626 public void handleMessage(Message msg) { 627 if (mAudioRecord == null) { 628 return; 629 } 630 switch(msg.what) { 631 case NATIVE_EVENT_MARKER: 632 synchronized (mMarkerListenerLock) { 633 if (mAudioRecord.mMarkerListener != null) { 634 mAudioRecord.mMarkerListener.onMarkerReached(mAudioRecord); 635 } 636 } 637 break; 638 case NATIVE_EVENT_NEW_POS: 639 synchronized (mPeriodicListenerLock) { 640 if (mAudioRecord.mPeriodicListener != null) { 641 mAudioRecord.mPeriodicListener.onPeriodicNotification(mAudioRecord); 642 } 643 } 644 break; 645 default: 646 Log.e(TAG, "[ android.media.AudioRecord.NativeEventHandler ] " + 647 "Unknown event type: " + msg.what); 648 break; 649 } 650 } 651 } 652 653 654 //--------------------------------------------------------- 655 // Java methods called from the native side 656 //-------------------- 657 @SuppressWarnings("unused") 658 private static void postEventFromNative(Object audiorecord_ref, 659 int what, int arg1, int arg2, Object obj) { 660 //logd("Event posted from the native side: event="+ what + " args="+ arg1+" "+arg2); 661 AudioRecord recorder = (AudioRecord)((WeakReference)audiorecord_ref).get(); 662 if (recorder == null) { 663 return; 664 } 665 666 if (recorder.mNativeEventHandler != null) { 667 Message m = recorder.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj); 668 recorder.mNativeEventHandler.sendMessage(m); 669 } 670 671 } 672 673 674 //--------------------------------------------------------- 675 // Native methods called from the Java side 676 //-------------------- 677 678 private native final int native_setup(Object audiorecord_this, 679 int recordSource, int sampleRate, int nbChannels, int audioFormat, int buffSizeInBytes); 680 681 private native final void native_finalize(); 682 683 private native final void native_release(); 684 685 private native final void native_start(); 686 687 private native final void native_stop(); 688 689 private native final int native_read_in_byte_array(byte[] audioData, 690 int offsetInBytes, int sizeInBytes); 691 692 private native final int native_read_in_short_array(short[] audioData, 693 int offsetInShorts, int sizeInShorts); 694 695 private native final int native_read_in_direct_buffer(Object jBuffer, int sizeInBytes); 696 697 private native final int native_set_marker_pos(int marker); 698 private native final int native_get_marker_pos(); 699 700 private native final int native_set_pos_update_period(int updatePeriod); 701 private native final int native_get_pos_update_period(); 702 703 704 //--------------------------------------------------------- 705 // Utility methods 706 //------------------ 707 708 private static void logd(String msg) { 709 Log.d(TAG, "[ android.media.AudioRecord ] " + msg); 710 } 711 712 private static void loge(String msg) { 713 Log.e(TAG, "[ android.media.AudioRecord ] " + msg); 714 } 715 716} 717 718 719 720 721 722