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