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