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