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