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