AudioRecord.java revision 8dfc10571a83389593fc11b92fbf9fc1f22c9f21
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 read 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 * indicates AudioRecord state is not successfully initialized. 53 */ 54 public static final int STATE_UNINITIALIZED = 0; 55 /** 56 * indicates AudioRecord state is ready to be used 57 */ 58 public static final int STATE_INITIALIZED = 1; 59 60 /** 61 * indicates AudioRecord recording state is not recording 62 */ 63 public static final int RECORDSTATE_STOPPED = 1; // matches SL_RECORDSTATE_STOPPED 64 /** 65 * indicates AudioRecord recording state 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_INVALIDCHANNELMASK = -17; 90 private static final int AUDIORECORD_ERROR_SETUP_INVALIDFORMAT = -18; 91 private static final int AUDIORECORD_ERROR_SETUP_INVALIDSOURCE = -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 denotes when record head has reached a previously set marker. 98 */ 99 private static final int NATIVE_EVENT_MARKER = 2; 100 /** 101 * Event id denotes when previously set update period has elapsed 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 /** 118 * Accessed by native methods: provides access to the callback data. 119 */ 120 @SuppressWarnings("unused") 121 private int mNativeCallbackCookie; 122 123 124 //--------------------------------------------------------- 125 // Member variables 126 //-------------------- 127 /** 128 * The audio data sampling rate in Hz. 129 */ 130 private int mSampleRate = 22050; 131 /** 132 * The number of input audio channels (1 is mono, 2 is stereo) 133 */ 134 private int mChannelCount = 1; 135 /** 136 * The audio channel mask 137 */ 138 private int mChannels = AudioFormat.CHANNEL_IN_MONO; 139 /** 140 * The current audio channel configuration 141 */ 142 private int mChannelConfiguration = AudioFormat.CHANNEL_IN_MONO; 143 /** 144 * The encoding of the audio samples. 145 * @see AudioFormat#ENCODING_PCM_8BIT 146 * @see AudioFormat#ENCODING_PCM_16BIT 147 */ 148 private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; 149 /** 150 * Where the audio data is recorded from. 151 */ 152 private int mRecordSource = MediaRecorder.AudioSource.DEFAULT; 153 /** 154 * Indicates the state of the AudioRecord instance. 155 */ 156 private int mState = STATE_UNINITIALIZED; 157 /** 158 * Indicates the recording state of the AudioRecord instance. 159 */ 160 private int mRecordingState = RECORDSTATE_STOPPED; 161 /** 162 * Lock to make sure mRecordingState updates are reflecting the actual state of the object. 163 */ 164 private final Object mRecordingStateLock = new Object(); 165 /** 166 * The listener the AudioRecord notifies when the record position reaches a marker 167 * or for periodic updates during the progression of the record head. 168 * @see #setRecordPositionUpdateListener(OnRecordPositionUpdateListener) 169 * @see #setRecordPositionUpdateListener(OnRecordPositionUpdateListener, Handler) 170 */ 171 private OnRecordPositionUpdateListener mPositionListener = null; 172 /** 173 * Lock to protect position listener updates against event notifications 174 */ 175 private final Object mPositionListenerLock = new Object(); 176 /** 177 * Handler for marker events coming from the native code 178 */ 179 private NativeEventHandler mEventHandler = null; 180 /** 181 * Looper associated with the thread that creates the AudioRecord instance 182 */ 183 private Looper mInitializationLooper = null; 184 /** 185 * Size of the native audio buffer. 186 */ 187 private int mNativeBufferSizeInBytes = 0; 188 /** 189 * Audio session ID 190 */ 191 private int mSessionId = 0; 192 193 //--------------------------------------------------------- 194 // Constructor, Finalize 195 //-------------------- 196 /** 197 * Class constructor. 198 * @param audioSource the recording source. See {@link MediaRecorder.AudioSource} for 199 * recording source definitions. 200 * @param sampleRateInHz the sample rate expressed in Hertz. 44100Hz is currently the only 201 * rate that is guaranteed to work on all devices, but other rates such as 22050, 202 * 16000, and 11025 may work on some devices. 203 * @param channelConfig describes the configuration of the audio channels. 204 * See {@link AudioFormat#CHANNEL_IN_MONO} and 205 * {@link AudioFormat#CHANNEL_IN_STEREO}. {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed 206 * to work on all devices. 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. See {@link #getMinBufferSize(int, int, int)} to determine the minimum 213 * required buffer size for the successful creation of an AudioRecord instance. Using values 214 * smaller than getMinBufferSize() will result in an initialization failure. 215 * @throws java.lang.IllegalArgumentException 216 */ 217 public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, 218 int bufferSizeInBytes) 219 throws IllegalArgumentException { 220 mState = STATE_UNINITIALIZED; 221 mRecordingState = RECORDSTATE_STOPPED; 222 223 // remember which looper is associated with the AudioRecord instanciation 224 if ((mInitializationLooper = Looper.myLooper()) == null) { 225 mInitializationLooper = Looper.getMainLooper(); 226 } 227 228 audioParamCheck(audioSource, sampleRateInHz, channelConfig, audioFormat); 229 230 audioBuffSizeCheck(bufferSizeInBytes); 231 232 // native initialization 233 int[] session = new int[1]; 234 session[0] = 0; 235 //TODO: update native initialization when information about hardware init failure 236 // due to capture device already open is available. 237 int initResult = native_setup( new WeakReference<AudioRecord>(this), 238 mRecordSource, mSampleRate, mChannels, mAudioFormat, mNativeBufferSizeInBytes, 239 session); 240 if (initResult != SUCCESS) { 241 loge("Error code "+initResult+" when initializing native AudioRecord object."); 242 return; // with mState == STATE_UNINITIALIZED 243 } 244 245 mSessionId = session[0]; 246 247 mState = STATE_INITIALIZED; 248 } 249 250 251 // Convenience method for the constructor's parameter checks. 252 // This is where constructor IllegalArgumentException-s are thrown 253 // postconditions: 254 // mRecordSource is valid 255 // mChannelCount is valid 256 // mChannels is valid 257 // mAudioFormat is valid 258 // mSampleRate is valid 259 private void audioParamCheck(int audioSource, int sampleRateInHz, 260 int channelConfig, int audioFormat) { 261 262 //-------------- 263 // audio source 264 if ( (audioSource < MediaRecorder.AudioSource.DEFAULT) || 265 (audioSource > MediaRecorder.getAudioSourceMax()) ) { 266 throw (new IllegalArgumentException("Invalid audio source.")); 267 } else { 268 mRecordSource = audioSource; 269 } 270 271 //-------------- 272 // sample rate 273 if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) { 274 throw (new IllegalArgumentException(sampleRateInHz 275 + "Hz is not a supported sample rate.")); 276 } else { 277 mSampleRate = sampleRateInHz; 278 } 279 280 //-------------- 281 // channel config 282 mChannelConfiguration = channelConfig; 283 284 switch (channelConfig) { 285 case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT 286 case AudioFormat.CHANNEL_IN_MONO: 287 case AudioFormat.CHANNEL_CONFIGURATION_MONO: 288 mChannelCount = 1; 289 mChannels = AudioFormat.CHANNEL_IN_MONO; 290 break; 291 case AudioFormat.CHANNEL_IN_STEREO: 292 case AudioFormat.CHANNEL_CONFIGURATION_STEREO: 293 mChannelCount = 2; 294 mChannels = AudioFormat.CHANNEL_IN_STEREO; 295 break; 296 default: 297 mChannelCount = 0; 298 mChannels = AudioFormat.CHANNEL_INVALID; 299 mChannelConfiguration = AudioFormat.CHANNEL_INVALID; 300 throw (new IllegalArgumentException("Unsupported channel configuration.")); 301 } 302 303 //-------------- 304 // audio format 305 switch (audioFormat) { 306 case AudioFormat.ENCODING_DEFAULT: 307 mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; 308 break; 309 case AudioFormat.ENCODING_PCM_16BIT: 310 case AudioFormat.ENCODING_PCM_8BIT: 311 mAudioFormat = audioFormat; 312 break; 313 default: 314 mAudioFormat = AudioFormat.ENCODING_INVALID; 315 throw (new IllegalArgumentException("Unsupported sample encoding." 316 + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT.")); 317 } 318 } 319 320 321 // Convenience method for the contructor's audio buffer size check. 322 // preconditions: 323 // mChannelCount is valid 324 // mAudioFormat is AudioFormat.ENCODING_PCM_8BIT OR AudioFormat.ENCODING_PCM_16BIT 325 // postcondition: 326 // mNativeBufferSizeInBytes is valid (multiple of frame size, positive) 327 private void audioBuffSizeCheck(int audioBufferSize) { 328 // NB: this section is only valid with PCM data. 329 // To update when supporting compressed formats 330 int frameSizeInBytes = mChannelCount 331 * (mAudioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2); 332 if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) { 333 throw (new IllegalArgumentException("Invalid audio buffer size.")); 334 } 335 336 mNativeBufferSizeInBytes = audioBufferSize; 337 } 338 339 340 341 /** 342 * Releases the native AudioRecord resources. 343 * The object can no longer be used and the reference should be set to null 344 * after a call to release() 345 */ 346 public void release() { 347 try { 348 stop(); 349 } catch(IllegalStateException ise) { 350 // don't raise an exception, we're releasing the resources. 351 } 352 native_release(); 353 mState = STATE_UNINITIALIZED; 354 } 355 356 357 @Override 358 protected void finalize() { 359 native_finalize(); 360 } 361 362 363 //-------------------------------------------------------------------------- 364 // Getters 365 //-------------------- 366 /** 367 * Returns the configured audio data sample rate in Hz 368 */ 369 public int getSampleRate() { 370 return mSampleRate; 371 } 372 373 /** 374 * Returns the audio recording source. 375 * @see MediaRecorder.AudioSource 376 */ 377 public int getAudioSource() { 378 return mRecordSource; 379 } 380 381 /** 382 * Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_16BIT} 383 * and {@link AudioFormat#ENCODING_PCM_8BIT}. 384 */ 385 public int getAudioFormat() { 386 return mAudioFormat; 387 } 388 389 /** 390 * Returns the configured channel configuration. 391 * See {@link AudioFormat#CHANNEL_IN_MONO} 392 * and {@link AudioFormat#CHANNEL_IN_STEREO}. 393 */ 394 public int getChannelConfiguration() { 395 return mChannelConfiguration; 396 } 397 398 /** 399 * Returns the configured number of channels. 400 */ 401 public int getChannelCount() { 402 return mChannelCount; 403 } 404 405 /** 406 * Returns the state of the AudioRecord instance. This is useful after the 407 * AudioRecord instance has been created to check if it was initialized 408 * properly. This ensures that the appropriate hardware resources have been 409 * acquired. 410 * @see AudioRecord#STATE_INITIALIZED 411 * @see AudioRecord#STATE_UNINITIALIZED 412 */ 413 public int getState() { 414 return mState; 415 } 416 417 /** 418 * Returns the recording state of the AudioRecord instance. 419 * @see AudioRecord#RECORDSTATE_STOPPED 420 * @see AudioRecord#RECORDSTATE_RECORDING 421 */ 422 public int getRecordingState() { 423 return mRecordingState; 424 } 425 426 /** 427 * Returns the notification marker position expressed in frames. 428 */ 429 public int getNotificationMarkerPosition() { 430 return native_get_marker_pos(); 431 } 432 433 /** 434 * Returns the notification update period expressed in frames. 435 */ 436 public int getPositionNotificationPeriod() { 437 return native_get_pos_update_period(); 438 } 439 440 /** 441 * Returns the minimum buffer size required for the successful creation of an AudioRecord 442 * object. 443 * Note that this size doesn't guarantee a smooth recording under load, and higher values 444 * should be chosen according to the expected frequency at which the AudioRecord instance 445 * will be polled for new data. 446 * @param sampleRateInHz the sample rate expressed in Hertz. 447 * @param channelConfig describes the configuration of the audio channels. 448 * See {@link AudioFormat#CHANNEL_IN_MONO} and 449 * {@link AudioFormat#CHANNEL_IN_STEREO} 450 * @param audioFormat the format in which the audio data is represented. 451 * See {@link AudioFormat#ENCODING_PCM_16BIT}. 452 * @return {@link #ERROR_BAD_VALUE} if the recording parameters are not supported by the 453 * hardware, or an invalid parameter was passed, 454 * or {@link #ERROR} if the implementation was unable to query the hardware for its 455 * output properties, 456 * or the minimum buffer size expressed in bytes. 457 * @see #AudioRecord(int, int, int, int, int) for more information on valid 458 * configuration values. 459 */ 460 static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) { 461 int channelCount = 0; 462 switch(channelConfig) { 463 case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT 464 case AudioFormat.CHANNEL_IN_MONO: 465 case AudioFormat.CHANNEL_CONFIGURATION_MONO: 466 channelCount = 1; 467 break; 468 case AudioFormat.CHANNEL_IN_STEREO: 469 case AudioFormat.CHANNEL_CONFIGURATION_STEREO: 470 channelCount = 2; 471 break; 472 case AudioFormat.CHANNEL_INVALID: 473 default: 474 loge("getMinBufferSize(): Invalid channel configuration."); 475 return AudioRecord.ERROR_BAD_VALUE; 476 } 477 478 // PCM_8BIT is not supported at the moment 479 if (audioFormat != AudioFormat.ENCODING_PCM_16BIT) { 480 loge("getMinBufferSize(): Invalid audio format."); 481 return AudioRecord.ERROR_BAD_VALUE; 482 } 483 484 int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat); 485 if (size == 0) { 486 return AudioRecord.ERROR_BAD_VALUE; 487 } 488 else if (size == -1) { 489 return AudioRecord.ERROR; 490 } 491 else { 492 return size; 493 } 494 } 495 496 /** 497 * Returns the audio session ID. 498 * 499 * @return the ID of the audio session this AudioRecord belongs to. 500 */ 501 public int getAudioSessionId() { 502 return mSessionId; 503 } 504 505 //--------------------------------------------------------- 506 // Transport control methods 507 //-------------------- 508 /** 509 * Starts recording from the AudioRecord instance. 510 * @throws IllegalStateException 511 */ 512 public void startRecording() 513 throws IllegalStateException { 514 if (mState != STATE_INITIALIZED) { 515 throw(new IllegalStateException("startRecording() called on an " 516 +"uninitialized AudioRecord.")); 517 } 518 519 // start recording 520 synchronized(mRecordingStateLock) { 521 if (native_start(MediaSyncEvent.SYNC_EVENT_NONE, 0) == SUCCESS) { 522 mRecordingState = RECORDSTATE_RECORDING; 523 } 524 } 525 } 526 527 /** 528 * Starts recording from the AudioRecord instance when the specified synchronization event 529 * occurs on the specified audio session. 530 * @throws IllegalStateException 531 * @param syncEvent event that triggers the capture. 532 * @see MediaSyncEvent 533 * @hide 534 */ 535 public void startRecording(MediaSyncEvent syncEvent) 536 throws IllegalStateException { 537 if (mState != STATE_INITIALIZED) { 538 throw(new IllegalStateException("startRecording() called on an " 539 +"uninitialized AudioRecord.")); 540 } 541 542 // start recording 543 synchronized(mRecordingStateLock) { 544 if (native_start(syncEvent.getType(), syncEvent.getAudioSessionId()) == SUCCESS) { 545 mRecordingState = RECORDSTATE_RECORDING; 546 } 547 } 548 } 549 550 /** 551 * Stops recording. 552 * @throws IllegalStateException 553 */ 554 public void stop() 555 throws IllegalStateException { 556 if (mState != STATE_INITIALIZED) { 557 throw(new IllegalStateException("stop() called on an uninitialized AudioRecord.")); 558 } 559 560 // stop recording 561 synchronized(mRecordingStateLock) { 562 native_stop(); 563 mRecordingState = RECORDSTATE_STOPPED; 564 } 565 } 566 567 568 //--------------------------------------------------------- 569 // Audio data supply 570 //-------------------- 571 /** 572 * Reads audio data from the audio hardware for recording into a buffer. 573 * @param audioData the array to which the recorded audio data is written. 574 * @param offsetInBytes index in audioData from which the data is written expressed in bytes. 575 * @param sizeInBytes the number of requested bytes. 576 * @return the number of bytes that were read or or {@link #ERROR_INVALID_OPERATION} 577 * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if 578 * the parameters don't resolve to valid data and indexes. 579 * The number of bytes will not exceed sizeInBytes. 580 */ 581 public int read(byte[] audioData, int offsetInBytes, int sizeInBytes) { 582 if (mState != STATE_INITIALIZED) { 583 return ERROR_INVALID_OPERATION; 584 } 585 586 if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0) 587 || (offsetInBytes + sizeInBytes > audioData.length)) { 588 return ERROR_BAD_VALUE; 589 } 590 591 return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes); 592 } 593 594 595 /** 596 * Reads audio data from the audio hardware for recording into a buffer. 597 * @param audioData the array to which the recorded audio data is written. 598 * @param offsetInShorts index in audioData from which the data is written expressed in shorts. 599 * @param sizeInShorts the number of requested shorts. 600 * @return the number of shorts that were read or or {@link #ERROR_INVALID_OPERATION} 601 * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if 602 * the parameters don't resolve to valid data and indexes. 603 * The number of shorts will not exceed sizeInShorts. 604 */ 605 public int read(short[] audioData, int offsetInShorts, int sizeInShorts) { 606 if (mState != STATE_INITIALIZED) { 607 return ERROR_INVALID_OPERATION; 608 } 609 610 if ( (audioData == null) || (offsetInShorts < 0 ) || (sizeInShorts < 0) 611 || (offsetInShorts + sizeInShorts > audioData.length)) { 612 return ERROR_BAD_VALUE; 613 } 614 615 return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts); 616 } 617 618 619 /** 620 * Reads audio data from the audio hardware for recording into a direct buffer. If this buffer 621 * is not a direct buffer, this method will always return 0. 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 return native_set_marker_pos(markerInFrames); 693 } 694 695 696 /** 697 * Sets the period at which the listener is called, if set with 698 * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener)} or 699 * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener, Handler)}. 700 * @param periodInFrames update period expressed in frames 701 * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_INVALID_OPERATION} 702 */ 703 public int setPositionNotificationPeriod(int periodInFrames) { 704 return native_set_pos_update_period(periodInFrames); 705 } 706 707 708 //--------------------------------------------------------- 709 // Interface definitions 710 //-------------------- 711 /** 712 * Interface definition for a callback to be invoked when an AudioRecord has 713 * reached a notification marker set by {@link AudioRecord#setNotificationMarkerPosition(int)} 714 * or for periodic updates on the progress of the record head, as set by 715 * {@link AudioRecord#setPositionNotificationPeriod(int)}. 716 */ 717 public interface OnRecordPositionUpdateListener { 718 /** 719 * Called on the listener to notify it that the previously set marker has been reached 720 * by the recording head. 721 */ 722 void onMarkerReached(AudioRecord recorder); 723 724 /** 725 * Called on the listener to periodically notify it that the record head has reached 726 * a multiple of the notification period. 727 */ 728 void onPeriodicNotification(AudioRecord recorder); 729 } 730 731 732 733 //--------------------------------------------------------- 734 // Inner classes 735 //-------------------- 736 737 /** 738 * Helper class to handle the forwarding of native events to the appropriate listener 739 * (potentially) handled in a different thread 740 */ 741 private class NativeEventHandler extends Handler { 742 743 private final AudioRecord mAudioRecord; 744 745 NativeEventHandler(AudioRecord recorder, Looper looper) { 746 super(looper); 747 mAudioRecord = recorder; 748 } 749 750 @Override 751 public void handleMessage(Message msg) { 752 OnRecordPositionUpdateListener listener = null; 753 synchronized (mPositionListenerLock) { 754 listener = mAudioRecord.mPositionListener; 755 } 756 757 switch(msg.what) { 758 case NATIVE_EVENT_MARKER: 759 if (listener != null) { 760 listener.onMarkerReached(mAudioRecord); 761 } 762 break; 763 case NATIVE_EVENT_NEW_POS: 764 if (listener != null) { 765 listener.onPeriodicNotification(mAudioRecord); 766 } 767 break; 768 default: 769 Log.e(TAG, "[ android.media.AudioRecord.NativeEventHandler ] " + 770 "Unknown 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, "[ android.media.AudioRecord ] " + msg); 838 } 839 840 private static void loge(String msg) { 841 Log.e(TAG, "[ android.media.AudioRecord ] " + msg); 842 } 843 844} 845 846