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