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 17 package android.media; 18 19 import java.lang.ref.WeakReference; 20 import java.nio.ByteBuffer; 21 22 import android.os.Handler; 23 import android.os.Looper; 24 import android.os.Message; 25 import android.util.Log; 26 27 /** 28 * The AudioRecord class manages the audio resources for Java applications 29 * to record audio from the audio input hardware of the platform. This is 30 * achieved by "pulling" (reading) the data from the AudioRecord object. The 31 * application is responsible for polling the AudioRecord object in time using one of 32 * the following three methods: {@link #read(byte[],int, int)}, {@link #read(short[], int, int)} 33 * or {@link #read(ByteBuffer, int)}. The choice of which method to use will be based 34 * on the audio data storage format that is the most convenient for the user of AudioRecord. 35 * <p>Upon creation, an AudioRecord object initializes its associated audio buffer that it will 36 * fill with the new audio data. The size of this buffer, specified during the construction, 37 * determines how long an AudioRecord can record before "over-running" data that has not 38 * been read yet. Data should be read from the audio hardware in chunks of sizes inferior to 39 * the total recording buffer size. 40 */ 41 public class AudioRecord 42 { 43 //--------------------------------------------------------- 44 // Constants 45 //-------------------- 46 /** 47 * indicates AudioRecord state is not successfully initialized. 48 */ 49 public static final int STATE_UNINITIALIZED = 0; 50 /** 51 * indicates AudioRecord state is ready to be used 52 */ 53 public static final int STATE_INITIALIZED = 1; 54 55 /** 56 * indicates AudioRecord recording state is not recording 57 */ 58 public static final int RECORDSTATE_STOPPED = 1; // matches SL_RECORDSTATE_STOPPED 59 /** 60 * indicates AudioRecord recording state is recording 61 */ 62 public static final int RECORDSTATE_RECORDING = 3;// matches SL_RECORDSTATE_RECORDING 63 64 // Error codes: 65 // to keep in sync with frameworks/base/core/jni/android_media_AudioRecord.cpp 66 /** 67 * Denotes a successful operation. 68 */ 69 public static final int SUCCESS = 0; 70 /** 71 * Denotes a generic operation failure. 72 */ 73 public static final int ERROR = -1; 74 /** 75 * Denotes a failure due to the use of an invalid value. 76 */ 77 public static final int ERROR_BAD_VALUE = -2; 78 /** 79 * Denotes a failure due to the improper use of a method. 80 */ 81 public static final int ERROR_INVALID_OPERATION = -3; 82 83 private static final int AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT = -16; 84 private static final int AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK = -17; 85 private static final int AUDIORECORD_ERROR_SETUP_INVALIDFORMAT = -18; 86 private static final int AUDIORECORD_ERROR_SETUP_INVALIDSOURCE = -19; 87 private static final int AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED = -20; 88 89 // Events: 90 // to keep in sync with frameworks/av/include/media/AudioRecord.h 91 /** 92 * Event id denotes when record head has reached a previously set marker. 93 */ 94 private static final int NATIVE_EVENT_MARKER = 2; 95 /** 96 * Event id denotes when previously set update period has elapsed during recording. 97 */ 98 private static final int NATIVE_EVENT_NEW_POS = 3; 99 100 private final static String TAG = "android.media.AudioRecord"; 101 102 103 //--------------------------------------------------------- 104 // Used exclusively by native code 105 //-------------------- 106 /** 107 * Accessed by native methods: provides access to C++ AudioRecord object 108 */ 109 @SuppressWarnings("unused") 110 private int mNativeRecorderInJavaObj; 111 112 /** 113 * Accessed by native methods: provides access to the callback data. 114 */ 115 @SuppressWarnings("unused") 116 private int mNativeCallbackCookie; 117 118 119 //--------------------------------------------------------- 120 // Member variables 121 //-------------------- 122 /** 123 * The audio data sampling rate in Hz. 124 */ 125 private int mSampleRate; 126 /** 127 * The number of input audio channels (1 is mono, 2 is stereo) 128 */ 129 private int mChannelCount; 130 /** 131 * The audio channel mask 132 */ 133 private int mChannelMask; 134 /** 135 * The encoding of the audio samples. 136 * @see AudioFormat#ENCODING_PCM_8BIT 137 * @see AudioFormat#ENCODING_PCM_16BIT 138 */ 139 private int mAudioFormat; 140 /** 141 * Where the audio data is recorded from. 142 */ 143 private int mRecordSource; 144 /** 145 * Indicates the state of the AudioRecord instance. 146 */ 147 private int mState = STATE_UNINITIALIZED; 148 /** 149 * Indicates the recording state of the AudioRecord instance. 150 */ 151 private int mRecordingState = RECORDSTATE_STOPPED; 152 /** 153 * Lock to make sure mRecordingState updates are reflecting the actual state of the object. 154 */ 155 private final Object mRecordingStateLock = new Object(); 156 /** 157 * The listener the AudioRecord notifies when the record position reaches a marker 158 * or for periodic updates during the progression of the record head. 159 * @see #setRecordPositionUpdateListener(OnRecordPositionUpdateListener) 160 * @see #setRecordPositionUpdateListener(OnRecordPositionUpdateListener, Handler) 161 */ 162 private OnRecordPositionUpdateListener mPositionListener = null; 163 /** 164 * Lock to protect position listener updates against event notifications 165 */ 166 private final Object mPositionListenerLock = new Object(); 167 /** 168 * Handler for marker events coming from the native code 169 */ 170 private NativeEventHandler mEventHandler = null; 171 /** 172 * Looper associated with the thread that creates the AudioRecord instance 173 */ 174 private Looper mInitializationLooper = null; 175 /** 176 * Size of the native audio buffer. 177 */ 178 private int mNativeBufferSizeInBytes = 0; 179 /** 180 * Audio session ID 181 */ 182 private int mSessionId = 0; 183 184 //--------------------------------------------------------- 185 // Constructor, Finalize 186 //-------------------- 187 /** 188 * Class constructor. 189 * @param audioSource the recording source. See {@link MediaRecorder.AudioSource} for 190 * recording source definitions. 191 * @param sampleRateInHz the sample rate expressed in Hertz. 44100Hz is currently the only 192 * rate that is guaranteed to work on all devices, but other rates such as 22050, 193 * 16000, and 11025 may work on some devices. 194 * @param channelConfig describes the configuration of the audio channels. 195 * See {@link AudioFormat#CHANNEL_IN_MONO} and 196 * {@link AudioFormat#CHANNEL_IN_STEREO}. {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed 197 * to work on all devices. 198 * @param audioFormat the format in which the audio data is represented. 199 * See {@link AudioFormat#ENCODING_PCM_16BIT} and 200 * {@link AudioFormat#ENCODING_PCM_8BIT} 201 * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written 202 * to during the recording. New audio data can be read from this buffer in smaller chunks 203 * than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum 204 * required buffer size for the successful creation of an AudioRecord instance. Using values 205 * smaller than getMinBufferSize() will result in an initialization failure. 206 * @throws java.lang.IllegalArgumentException 207 */ 208 public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, 209 int bufferSizeInBytes) 210 throws IllegalArgumentException { 211 mRecordingState = RECORDSTATE_STOPPED; 212 213 // remember which looper is associated with the AudioRecord instanciation 214 if ((mInitializationLooper = Looper.myLooper()) == null) { 215 mInitializationLooper = Looper.getMainLooper(); 216 } 217 218 audioParamCheck(audioSource, sampleRateInHz, channelConfig, audioFormat); 219 220 audioBuffSizeCheck(bufferSizeInBytes); 221 222 // native initialization 223 int[] session = new int[1]; 224 session[0] = 0; 225 //TODO: update native initialization when information about hardware init failure 226 // due to capture device already open is available. 227 int initResult = native_setup( new WeakReference<AudioRecord>(this), 228 mRecordSource, mSampleRate, mChannelMask, mAudioFormat, mNativeBufferSizeInBytes, 229 session); 230 if (initResult != SUCCESS) { 231 loge("Error code "+initResult+" when initializing native AudioRecord object."); 232 return; // with mState == STATE_UNINITIALIZED 233 } 234 235 mSessionId = session[0]; 236 237 mState = STATE_INITIALIZED; 238 } 239 240 241 // Convenience method for the constructor's parameter checks. 242 // This is where constructor IllegalArgumentException-s are thrown 243 // postconditions: 244 // mRecordSource is valid 245 // mChannelCount is valid 246 // mChannelMask is valid 247 // mAudioFormat is valid 248 // mSampleRate is valid 249 private void audioParamCheck(int audioSource, int sampleRateInHz, 250 int channelConfig, int audioFormat) { 251 252 //-------------- 253 // audio source 254 if ( (audioSource < MediaRecorder.AudioSource.DEFAULT) || 255 ((audioSource > MediaRecorder.getAudioSourceMax()) && 256 (audioSource != MediaRecorder.AudioSource.HOTWORD)) ) { 257 throw new IllegalArgumentException("Invalid audio source."); 258 } 259 mRecordSource = audioSource; 260 261 //-------------- 262 // sample rate 263 if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) { 264 throw new IllegalArgumentException(sampleRateInHz 265 + "Hz is not a supported sample rate."); 266 } 267 mSampleRate = sampleRateInHz; 268 269 //-------------- 270 // channel config 271 switch (channelConfig) { 272 case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT 273 case AudioFormat.CHANNEL_IN_MONO: 274 case AudioFormat.CHANNEL_CONFIGURATION_MONO: 275 mChannelCount = 1; 276 mChannelMask = AudioFormat.CHANNEL_IN_MONO; 277 break; 278 case AudioFormat.CHANNEL_IN_STEREO: 279 case AudioFormat.CHANNEL_CONFIGURATION_STEREO: 280 mChannelCount = 2; 281 mChannelMask = AudioFormat.CHANNEL_IN_STEREO; 282 break; 283 case (AudioFormat.CHANNEL_IN_FRONT | AudioFormat.CHANNEL_IN_BACK): 284 mChannelCount = 2; 285 mChannelMask = channelConfig; 286 break; 287 default: 288 throw new IllegalArgumentException("Unsupported channel configuration."); 289 } 290 291 //-------------- 292 // audio format 293 switch (audioFormat) { 294 case AudioFormat.ENCODING_DEFAULT: 295 mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; 296 break; 297 case AudioFormat.ENCODING_PCM_16BIT: 298 case AudioFormat.ENCODING_PCM_8BIT: 299 mAudioFormat = audioFormat; 300 break; 301 default: 302 throw new IllegalArgumentException("Unsupported sample encoding." 303 + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT."); 304 } 305 } 306 307 308 // Convenience method for the contructor's audio buffer size check. 309 // preconditions: 310 // mChannelCount is valid 311 // mAudioFormat is AudioFormat.ENCODING_PCM_8BIT OR AudioFormat.ENCODING_PCM_16BIT 312 // postcondition: 313 // mNativeBufferSizeInBytes is valid (multiple of frame size, positive) 314 private void audioBuffSizeCheck(int audioBufferSize) { 315 // NB: this section is only valid with PCM data. 316 // To update when supporting compressed formats 317 int frameSizeInBytes = mChannelCount 318 * (mAudioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2); 319 if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) { 320 throw new IllegalArgumentException("Invalid audio buffer size."); 321 } 322 323 mNativeBufferSizeInBytes = audioBufferSize; 324 } 325 326 327 328 /** 329 * Releases the native AudioRecord resources. 330 * The object can no longer be used and the reference should be set to null 331 * after a call to release() 332 */ 333 public void release() { 334 try { 335 stop(); 336 } catch(IllegalStateException ise) { 337 // don't raise an exception, we're releasing the resources. 338 } 339 native_release(); 340 mState = STATE_UNINITIALIZED; 341 } 342 343 344 @Override 345 protected void finalize() { 346 native_finalize(); 347 } 348 349 350 //-------------------------------------------------------------------------- 351 // Getters 352 //-------------------- 353 /** 354 * Returns the configured audio data sample rate in Hz 355 */ 356 public int getSampleRate() { 357 return mSampleRate; 358 } 359 360 /** 361 * Returns the audio recording source. 362 * @see MediaRecorder.AudioSource 363 */ 364 public int getAudioSource() { 365 return mRecordSource; 366 } 367 368 /** 369 * Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_16BIT} 370 * and {@link AudioFormat#ENCODING_PCM_8BIT}. 371 */ 372 public int getAudioFormat() { 373 return mAudioFormat; 374 } 375 376 /** 377 * Returns the configured channel configuration. 378 * See {@link AudioFormat#CHANNEL_IN_MONO} 379 * and {@link AudioFormat#CHANNEL_IN_STEREO}. 380 */ 381 public int getChannelConfiguration() { 382 return mChannelMask; 383 } 384 385 /** 386 * Returns the configured number of channels. 387 */ 388 public int getChannelCount() { 389 return mChannelCount; 390 } 391 392 /** 393 * Returns the state of the AudioRecord instance. This is useful after the 394 * AudioRecord instance has been created to check if it was initialized 395 * properly. This ensures that the appropriate hardware resources have been 396 * acquired. 397 * @see AudioRecord#STATE_INITIALIZED 398 * @see AudioRecord#STATE_UNINITIALIZED 399 */ 400 public int getState() { 401 return mState; 402 } 403 404 /** 405 * Returns the recording state of the AudioRecord instance. 406 * @see AudioRecord#RECORDSTATE_STOPPED 407 * @see AudioRecord#RECORDSTATE_RECORDING 408 */ 409 public int getRecordingState() { 410 synchronized (mRecordingStateLock) { 411 return mRecordingState; 412 } 413 } 414 415 /** 416 * Returns the notification marker position expressed in frames. 417 */ 418 public int getNotificationMarkerPosition() { 419 return native_get_marker_pos(); 420 } 421 422 /** 423 * Returns the notification update period expressed in frames. 424 */ 425 public int getPositionNotificationPeriod() { 426 return native_get_pos_update_period(); 427 } 428 429 /** 430 * Returns the minimum buffer size required for the successful creation of an AudioRecord 431 * object, in byte units. 432 * Note that this size doesn't guarantee a smooth recording under load, and higher values 433 * should be chosen according to the expected frequency at which the AudioRecord instance 434 * will be polled for new data. 435 * See {@link #AudioRecord(int, int, int, int, int)} for more information on valid 436 * configuration values. 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 * input properties, 447 * or the minimum buffer size expressed in bytes. 448 * @see #AudioRecord(int, int, int, int, int) 449 */ 450 static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) { 451 int channelCount = 0; 452 switch (channelConfig) { 453 case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT 454 case AudioFormat.CHANNEL_IN_MONO: 455 case AudioFormat.CHANNEL_CONFIGURATION_MONO: 456 channelCount = 1; 457 break; 458 case AudioFormat.CHANNEL_IN_STEREO: 459 case AudioFormat.CHANNEL_CONFIGURATION_STEREO: 460 case (AudioFormat.CHANNEL_IN_FRONT | AudioFormat.CHANNEL_IN_BACK): 461 channelCount = 2; 462 break; 463 case AudioFormat.CHANNEL_INVALID: 464 default: 465 loge("getMinBufferSize(): Invalid channel configuration."); 466 return ERROR_BAD_VALUE; 467 } 468 469 // PCM_8BIT is not supported at the moment 470 if (audioFormat != AudioFormat.ENCODING_PCM_16BIT) { 471 loge("getMinBufferSize(): Invalid audio format."); 472 return ERROR_BAD_VALUE; 473 } 474 475 int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat); 476 if (size == 0) { 477 return ERROR_BAD_VALUE; 478 } 479 else if (size == -1) { 480 return ERROR; 481 } 482 else { 483 return size; 484 } 485 } 486 487 /** 488 * Returns the audio session ID. 489 * 490 * @return the ID of the audio session this AudioRecord belongs to. 491 */ 492 public int getAudioSessionId() { 493 return mSessionId; 494 } 495 496 //--------------------------------------------------------- 497 // Transport control methods 498 //-------------------- 499 /** 500 * Starts recording from the AudioRecord instance. 501 * @throws IllegalStateException 502 */ 503 public void startRecording() 504 throws IllegalStateException { 505 if (mState != STATE_INITIALIZED) { 506 throw new IllegalStateException("startRecording() called on an " 507 + "uninitialized AudioRecord."); 508 } 509 510 // start recording 511 synchronized(mRecordingStateLock) { 512 if (native_start(MediaSyncEvent.SYNC_EVENT_NONE, 0) == SUCCESS) { 513 mRecordingState = RECORDSTATE_RECORDING; 514 } 515 } 516 } 517 518 /** 519 * Starts recording from the AudioRecord instance when the specified synchronization event 520 * occurs on the specified audio session. 521 * @throws IllegalStateException 522 * @param syncEvent event that triggers the capture. 523 * @see MediaSyncEvent 524 */ 525 public void startRecording(MediaSyncEvent syncEvent) 526 throws IllegalStateException { 527 if (mState != STATE_INITIALIZED) { 528 throw new IllegalStateException("startRecording() called on an " 529 + "uninitialized AudioRecord."); 530 } 531 532 // start recording 533 synchronized(mRecordingStateLock) { 534 if (native_start(syncEvent.getType(), syncEvent.getAudioSessionId()) == SUCCESS) { 535 mRecordingState = RECORDSTATE_RECORDING; 536 } 537 } 538 } 539 540 /** 541 * Stops recording. 542 * @throws IllegalStateException 543 */ 544 public void stop() 545 throws IllegalStateException { 546 if (mState != STATE_INITIALIZED) { 547 throw new IllegalStateException("stop() called on an uninitialized AudioRecord."); 548 } 549 550 // stop recording 551 synchronized(mRecordingStateLock) { 552 native_stop(); 553 mRecordingState = RECORDSTATE_STOPPED; 554 } 555 } 556 557 558 //--------------------------------------------------------- 559 // Audio data supply 560 //-------------------- 561 /** 562 * Reads audio data from the audio hardware for recording into a buffer. 563 * @param audioData the array to which the recorded audio data is written. 564 * @param offsetInBytes index in audioData from which the data is written expressed in bytes. 565 * @param sizeInBytes the number of requested bytes. 566 * @return the number of bytes that were read or or {@link #ERROR_INVALID_OPERATION} 567 * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if 568 * the parameters don't resolve to valid data and indexes. 569 * The number of bytes will not exceed sizeInBytes. 570 */ 571 public int read(byte[] audioData, int offsetInBytes, int sizeInBytes) { 572 if (mState != STATE_INITIALIZED) { 573 return ERROR_INVALID_OPERATION; 574 } 575 576 if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0) 577 || (offsetInBytes + sizeInBytes < 0) // detect integer overflow 578 || (offsetInBytes + sizeInBytes > audioData.length)) { 579 return ERROR_BAD_VALUE; 580 } 581 582 return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes); 583 } 584 585 586 /** 587 * Reads audio data from the audio hardware for recording into a buffer. 588 * @param audioData the array to which the recorded audio data is written. 589 * @param offsetInShorts index in audioData from which the data is written expressed in shorts. 590 * @param sizeInShorts the number of requested shorts. 591 * @return the number of shorts that were read or or {@link #ERROR_INVALID_OPERATION} 592 * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if 593 * the parameters don't resolve to valid data and indexes. 594 * The number of shorts will not exceed sizeInShorts. 595 */ 596 public int read(short[] audioData, int offsetInShorts, int sizeInShorts) { 597 if (mState != STATE_INITIALIZED) { 598 return ERROR_INVALID_OPERATION; 599 } 600 601 if ( (audioData == null) || (offsetInShorts < 0 ) || (sizeInShorts < 0) 602 || (offsetInShorts + sizeInShorts < 0) // detect integer overflow 603 || (offsetInShorts + sizeInShorts > audioData.length)) { 604 return ERROR_BAD_VALUE; 605 } 606 607 return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts); 608 } 609 610 611 /** 612 * Reads audio data from the audio hardware for recording into a direct buffer. If this buffer 613 * is not a direct buffer, this method will always return 0. 614 * Note that the value returned by {@link java.nio.Buffer#position()} on this buffer is 615 * unchanged after a call to this method. 616 * @param audioBuffer the direct buffer to which the recorded audio data is written. 617 * @param sizeInBytes the number of requested bytes. 618 * @return the number of bytes that were read or or {@link #ERROR_INVALID_OPERATION} 619 * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if 620 * the parameters don't resolve to valid data and indexes. 621 * The number of bytes will not exceed sizeInBytes. 622 */ 623 public int read(ByteBuffer audioBuffer, int sizeInBytes) { 624 if (mState != STATE_INITIALIZED) { 625 return ERROR_INVALID_OPERATION; 626 } 627 628 if ( (audioBuffer == null) || (sizeInBytes < 0) ) { 629 return ERROR_BAD_VALUE; 630 } 631 632 return native_read_in_direct_buffer(audioBuffer, sizeInBytes); 633 } 634 635 636 //-------------------------------------------------------------------------- 637 // Initialization / configuration 638 //-------------------- 639 /** 640 * Sets the listener the AudioRecord notifies when a previously set marker is reached or 641 * for each periodic record head position update. 642 * @param listener 643 */ 644 public void setRecordPositionUpdateListener(OnRecordPositionUpdateListener listener) { 645 setRecordPositionUpdateListener(listener, null); 646 } 647 648 /** 649 * Sets the listener the AudioRecord notifies when a previously set marker is reached or 650 * for each periodic record head position update. 651 * Use this method to receive AudioRecord events in the Handler associated with another 652 * thread than the one in which you created the AudioTrack instance. 653 * @param listener 654 * @param handler the Handler that will receive the event notification messages. 655 */ 656 public void setRecordPositionUpdateListener(OnRecordPositionUpdateListener listener, 657 Handler handler) { 658 synchronized (mPositionListenerLock) { 659 660 mPositionListener = listener; 661 662 if (listener != null) { 663 if (handler != null) { 664 mEventHandler = new NativeEventHandler(this, handler.getLooper()); 665 } else { 666 // no given handler, use the looper the AudioRecord was created in 667 mEventHandler = new NativeEventHandler(this, mInitializationLooper); 668 } 669 } else { 670 mEventHandler = null; 671 } 672 } 673 674 } 675 676 677 /** 678 * Sets the marker position at which the listener is called, if set with 679 * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener)} or 680 * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener, Handler)}. 681 * @param markerInFrames marker position expressed in frames 682 * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE}, 683 * {@link #ERROR_INVALID_OPERATION} 684 */ 685 public int setNotificationMarkerPosition(int markerInFrames) { 686 if (mState == STATE_UNINITIALIZED) { 687 return ERROR_INVALID_OPERATION; 688 } 689 return native_set_marker_pos(markerInFrames); 690 } 691 692 693 /** 694 * Sets the period at which the listener is called, if set with 695 * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener)} or 696 * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener, Handler)}. 697 * It is possible for notifications to be lost if the period is too small. 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