Loading api/current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -14910,9 +14910,12 @@ package android.media { method public int getSampleRate(); method public int getState(); method public int read(byte[], int, int); method public int read(byte[], int, int, int); method public int read(short[], int, int); method public int read(short[], int, int, int); method public int read(float[], int, int, int); method public int read(java.nio.ByteBuffer, int); method public int read(java.nio.ByteBuffer, int, int); method public void release(); method public int setNotificationMarkerPosition(int); method public int setPositionNotificationPeriod(int); api/system-current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -16120,9 +16120,12 @@ package android.media { method public int getSampleRate(); method public int getState(); method public int read(byte[], int, int); method public int read(byte[], int, int, int); method public int read(short[], int, int); method public int read(short[], int, int, int); method public int read(float[], int, int, int); method public int read(java.nio.ByteBuffer, int); method public int read(java.nio.ByteBuffer, int, int); method public void release(); method public int setNotificationMarkerPosition(int); method public int setPositionNotificationPeriod(int); core/jni/android_media_AudioRecord.cpp +69 −111 Original line number Diff line number Diff line Loading @@ -356,94 +356,57 @@ static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) { android_media_AudioRecord_release(env, thiz); } // ---------------------------------------------------------------------------- static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz, jbyteArray javaAudioData, jint offsetInBytes, jint sizeInBytes) { jbyte* recordBuff = NULL; // get the audio recorder from which we'll read new audio samples sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); if (lpRecorder == NULL) { ALOGE("Unable to retrieve AudioRecord object, can't record"); return 0; // overloaded JNI array helper functions static inline jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) { return env->GetByteArrayElements(array, isCopy); } if (!javaAudioData) { ALOGE("Invalid Java array to store recorded audio, can't record"); return 0; static inline void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) { env->ReleaseByteArrayElements(array, elems, mode); } // get the pointer to where we'll record the audio // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such // a way that it becomes much more efficient. When doing so, we will have to prevent the // AudioSystem callback to be called while in critical section (in case of media server // process crash for instance) recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); if (recordBuff == NULL) { ALOGE("Error retrieving destination for recorded audio data, can't record"); return 0; static inline jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) { return env->GetShortArrayElements(array, isCopy); } // read the new audio data from the native AudioRecord object ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes, sizeInBytes); env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0); if (readSize < 0) { readSize = (jint)AUDIO_JAVA_INVALID_OPERATION; } return (jint) readSize; static inline void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) { env->ReleaseShortArrayElements(array, elems, mode); } // ---------------------------------------------------------------------------- static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz, jshortArray javaAudioData, jint offsetInShorts, jint sizeInShorts) { jshort* recordBuff = NULL; // get the audio recorder from which we'll read new audio samples sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); if (lpRecorder == NULL) { ALOGE("Unable to retrieve AudioRecord object, can't record"); return 0; static inline jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) { return env->GetFloatArrayElements(array, isCopy); } if (!javaAudioData) { ALOGE("Invalid Java array to store recorded audio, can't record"); return 0; static inline void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) { env->ReleaseFloatArrayElements(array, elems, mode); } // get the pointer to where we'll record the audio // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such // a way that it becomes much more efficient. When doing so, we will have to prevent the // AudioSystem callback to be called while in critical section (in case of media server // process crash for instance) recordBuff = (jshort *)env->GetShortArrayElements(javaAudioData, NULL); if (recordBuff == NULL) { ALOGE("Error retrieving destination for recorded audio data, can't record"); return 0; } // read the new audio data from the native AudioRecord object const size_t sizeInBytes = sizeInShorts * sizeof(short); ssize_t readSize = lpRecorder->read(recordBuff + offsetInShorts, sizeInBytes); env->ReleaseShortArrayElements(javaAudioData, recordBuff, 0); if (readSize < 0) { readSize = (jint)AUDIO_JAVA_INVALID_OPERATION; } else { readSize /= sizeof(short); static inline jint interpretReadSizeError(ssize_t readSize) { ALOGE_IF(readSize != WOULD_BLOCK, "Error %zd during AudioRecord native read", readSize); switch (readSize) { case WOULD_BLOCK: return (jint)0; case BAD_VALUE: return (jint)AUDIO_JAVA_BAD_VALUE; default: // may be possible for other errors such as // NO_INIT to happen if restoreRecord_l fails. case INVALID_OPERATION: return (jint)AUDIO_JAVA_INVALID_OPERATION; } return (jint) readSize; } // ---------------------------------------------------------------------------- static jint android_media_AudioRecord_readInFloatArray(JNIEnv *env, jobject thiz, jfloatArray javaAudioData, jint offsetInFloats, jint sizeInFloats, template <typename T> static jint android_media_AudioRecord_readInArray(JNIEnv *env, jobject thiz, T javaAudioData, jint offsetInSamples, jint sizeInSamples, jboolean isReadBlocking) { // get the audio recorder from which we'll read new audio samples sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); Loading @@ -457,72 +420,64 @@ static jint android_media_AudioRecord_readInFloatArray(JNIEnv *env, jobject thi return (jint)AUDIO_JAVA_BAD_VALUE; } // get the pointer to where we'll record the audio // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such // a way that it becomes much more efficient. When doing so, we will have to prevent the // AudioSystem callback to be called while in critical section (in case of media server // process crash for instance) jfloat *recordBuff = (jfloat *)env->GetFloatArrayElements(javaAudioData, NULL); // get the pointer to where we'll record the audio auto *recordBuff = envGetArrayElements(env, javaAudioData, NULL); if (recordBuff == NULL) { ALOGE("Error retrieving destination for recorded audio data"); return (jint)AUDIO_JAVA_BAD_VALUE; } // read the new audio data from the native AudioRecord object const size_t sizeInBytes = sizeInFloats * sizeof(float); ssize_t readSize = lpRecorder->read(recordBuff + offsetInFloats, sizeInBytes); const size_t sizeInBytes = sizeInSamples * sizeof(*recordBuff); ssize_t readSize = lpRecorder->read( recordBuff + offsetInSamples, sizeInBytes, isReadBlocking == JNI_TRUE /* blocking */); env->ReleaseFloatArrayElements(javaAudioData, recordBuff, 0); envReleaseArrayElements(env, javaAudioData, recordBuff, 0); if (readSize < 0) { ALOGE_IF(readSize != WOULD_BLOCK, "Error %zd during AudioRecord native read", readSize); switch (readSize) { case WOULD_BLOCK: return (jint)0; case BAD_VALUE: return (jint)AUDIO_JAVA_BAD_VALUE; default: // may be possible for other errors such as // NO_INIT to happen if restoreRecord_l fails. case INVALID_OPERATION: return (jint)AUDIO_JAVA_INVALID_OPERATION; return interpretReadSizeError(readSize); } } return (jint)(readSize / sizeof(float)); return (jint)(readSize / sizeof(*recordBuff)); } // ---------------------------------------------------------------------------- static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz, jobject jBuffer, jint sizeInBytes) { jobject jBuffer, jint sizeInBytes, jboolean isReadBlocking) { // get the audio recorder from which we'll read new audio samples sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); if (lpRecorder==NULL) return 0; return (jint)AUDIO_JAVA_INVALID_OPERATION; // direct buffer and direct access supported? long capacity = env->GetDirectBufferCapacity(jBuffer); if (capacity == -1) { // buffer direct access is not supported ALOGE("Buffer direct access is not supported, can't record"); return 0; return (jint)AUDIO_JAVA_BAD_VALUE; } //ALOGV("capacity = %ld", capacity); jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer); if (nativeFromJavaBuf==NULL) { ALOGE("Buffer direct access is not supported, can't record"); return 0; return (jint)AUDIO_JAVA_BAD_VALUE; } // read new data from the recorder ssize_t readSize = lpRecorder->read(nativeFromJavaBuf, capacity < sizeInBytes ? capacity : sizeInBytes); capacity < sizeInBytes ? capacity : sizeInBytes, isReadBlocking == JNI_TRUE /* blocking */); if (readSize < 0) { readSize = (jint)AUDIO_JAVA_INVALID_OPERATION; return interpretReadSizeError(readSize); } return (jint)readSize; } // ---------------------------------------------------------------------------- static jint android_media_AudioRecord_get_native_frame_count(JNIEnv *env, jobject thiz) { sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); Loading Loading @@ -633,12 +588,15 @@ static JNINativeMethod gMethods[] = { {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize}, {"native_release", "()V", (void *)android_media_AudioRecord_release}, {"native_read_in_byte_array", "([BII)I", (void *)android_media_AudioRecord_readInByteArray}, "([BIIZ)I", (void *)android_media_AudioRecord_readInArray<jbyteArray>}, {"native_read_in_short_array", "([SII)I", (void *)android_media_AudioRecord_readInShortArray}, "([SIIZ)I", (void *)android_media_AudioRecord_readInArray<jshortArray>}, {"native_read_in_float_array", "([FIIZ)I", (void *)android_media_AudioRecord_readInFloatArray}, {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I", "([FIIZ)I", (void *)android_media_AudioRecord_readInArray<jfloatArray>}, {"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I", (void *)android_media_AudioRecord_readInDirectBuffer}, {"native_get_native_frame_count", "()I", (void *)android_media_AudioRecord_get_native_frame_count}, Loading media/java/android/media/AudioRecord.java +114 −17 Original line number Diff line number Diff line Loading @@ -901,20 +901,63 @@ public class AudioRecord * the parameters don't resolve to valid data and indexes. * The number of bytes will not exceed sizeInBytes. */ public int read(byte[] audioData, int offsetInBytes, int sizeInBytes) { if (mState != STATE_INITIALIZED) { public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes) { return read(audioData, offsetInBytes, sizeInBytes, READ_BLOCKING); } /** * Reads audio data from the audio hardware for recording into a byte array. * The format specified in the AudioRecord constructor should be * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array. * @param audioData the array to which the recorded audio data is written. * @param offsetInBytes index in audioData from which the data is written expressed in bytes. * @param sizeInBytes the number of requested bytes. * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}. * <br>With {@link #READ_BLOCKING}, the read will block until all the requested data * is read. * <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after * reading as much audio data as possible without blocking. * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION} * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if * the parameters don't resolve to valid data and indexes. * The number of bytes will not exceed sizeInBytes. */ public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes, @ReadMode int readMode) { if (mState != STATE_INITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { return ERROR_INVALID_OPERATION; } if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) { Log.e(TAG, "AudioRecord.read() called with invalid blocking mode"); return ERROR_BAD_VALUE; } if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0) || (offsetInBytes + sizeInBytes < 0) // detect integer overflow || (offsetInBytes + sizeInBytes > audioData.length)) { return ERROR_BAD_VALUE; } return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes); return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes, readMode == READ_BLOCKING); } /** * Reads audio data from the audio hardware for recording into a short array. * The format specified in the AudioRecord constructor should be * {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array. * @param audioData the array to which the recorded audio data is written. * @param offsetInShorts index in audioData from which the data is written expressed in shorts. * @param sizeInShorts the number of requested shorts. * @return the number of shorts that were read or {@link #ERROR_INVALID_OPERATION} * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if * the parameters don't resolve to valid data and indexes. * The number of shorts will not exceed sizeInShorts. */ public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts) { return read(audioData, offsetInShorts, sizeInShorts, READ_BLOCKING); } /** * Reads audio data from the audio hardware for recording into a short array. Loading @@ -923,23 +966,35 @@ public class AudioRecord * @param audioData the array to which the recorded audio data is written. * @param offsetInShorts index in audioData from which the data is written expressed in shorts. * @param sizeInShorts the number of requested shorts. * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}. * <br>With {@link #READ_BLOCKING}, the read will block until all the requested data * is read. * <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after * reading as much audio data as possible without blocking. * @return the number of shorts that were read or {@link #ERROR_INVALID_OPERATION} * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if * the parameters don't resolve to valid data and indexes. * The number of shorts will not exceed sizeInShorts. */ public int read(short[] audioData, int offsetInShorts, int sizeInShorts) { if (mState != STATE_INITIALIZED) { public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts, @ReadMode int readMode) { if (mState != STATE_INITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { return ERROR_INVALID_OPERATION; } if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) { Log.e(TAG, "AudioRecord.read() called with invalid blocking mode"); return ERROR_BAD_VALUE; } if ( (audioData == null) || (offsetInShorts < 0 ) || (sizeInShorts < 0) || (offsetInShorts + sizeInShorts < 0) // detect integer overflow || (offsetInShorts + sizeInShorts > audioData.length)) { return ERROR_BAD_VALUE; } return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts); return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts, readMode == READ_BLOCKING); } /** Loading @@ -950,21 +1005,32 @@ public class AudioRecord * @param offsetInFloats index in audioData from which the data is written. * @param sizeInFloats the number of requested floats. * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}. * <BR>With {@link #READ_BLOCKING}, the read will block until all the requested data * <br>With {@link #READ_BLOCKING}, the read will block until all the requested data * is read. * <BR>With {@link #READ_NON_BLOCKING}, the read will return immediately after * <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after * reading as much audio data as possible without blocking. * @return the number of floats that were read or {@link #ERROR_INVALID_OPERATION} * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if * the parameters don't resolve to valid data and indexes. * The number of floats will not exceed sizeInFloats. */ public int read(float[] audioData, int offsetInFloats, int sizeInFloats, public int read(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats, @ReadMode int readMode) { if (mState != STATE_INITIALIZED) { if (mState == STATE_UNINITIALIZED) { Log.e(TAG, "AudioRecord.read() called in invalid state STATE_UNINITIALIZED"); return ERROR_INVALID_OPERATION; } if (mAudioFormat != AudioFormat.ENCODING_PCM_FLOAT) { Log.e(TAG, "AudioRecord.read(float[] ...) requires format ENCODING_PCM_FLOAT"); return ERROR_INVALID_OPERATION; } if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) { Log.e(TAG, "AudioRecord.read() called with invalid blocking mode"); return ERROR_BAD_VALUE; } if ((audioData == null) || (offsetInFloats < 0) || (sizeInFloats < 0) || (offsetInFloats + sizeInFloats < 0) // detect integer overflow || (offsetInFloats + sizeInFloats > audioData.length)) { Loading Loading @@ -992,18 +1058,48 @@ public class AudioRecord * The number of bytes will not exceed sizeInBytes. * The number of bytes read will truncated to be a multiple of the frame size. */ public int read(ByteBuffer audioBuffer, int sizeInBytes) { public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes) { return read(audioBuffer, sizeInBytes, READ_BLOCKING); } /** * Reads audio data from the audio hardware for recording into a direct buffer. If this buffer * is not a direct buffer, this method will always return 0. * Note that the value returned by {@link java.nio.Buffer#position()} on this buffer is * unchanged after a call to this method. * The representation of the data in the buffer will depend on the format specified in * the AudioRecord constructor, and will be native endian. * @param audioBuffer the direct buffer to which the recorded audio data is written. * @param sizeInBytes the number of requested bytes. It is recommended but not enforced * that the number of bytes requested be a multiple of the frame size (sample size in * bytes multiplied by the channel count). * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}. * <br>With {@link #READ_BLOCKING}, the read will block until all the requested data * is read. * <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after * reading as much audio data as possible without blocking. * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION} * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if * the parameters don't resolve to valid data and indexes. * The number of bytes will not exceed sizeInBytes. * The number of bytes read will truncated to be a multiple of the frame size. */ public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes, @ReadMode int readMode) { if (mState != STATE_INITIALIZED) { return ERROR_INVALID_OPERATION; } if ( (audioBuffer == null) || (sizeInBytes < 0) ) { if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) { Log.e(TAG, "AudioRecord.read() called with invalid blocking mode"); return ERROR_BAD_VALUE; } return native_read_in_direct_buffer(audioBuffer, sizeInBytes); if ( (audioBuffer == null) || (sizeInBytes < 0) ) { return ERROR_BAD_VALUE; } return native_read_in_direct_buffer(audioBuffer, sizeInBytes, readMode == READ_BLOCKING); } //-------------------------------------------------------------------------- // Initialization / configuration Loading Loading @@ -1186,15 +1282,16 @@ public class AudioRecord private native final void native_stop(); private native final int native_read_in_byte_array(byte[] audioData, int offsetInBytes, int sizeInBytes); int offsetInBytes, int sizeInBytes, boolean isBlocking); private native final int native_read_in_short_array(short[] audioData, int offsetInShorts, int sizeInShorts); int offsetInShorts, int sizeInShorts, boolean isBlocking); private native final int native_read_in_float_array(float[] audioData, int offsetInFloats, int sizeInFloats, boolean isBlocking); private native final int native_read_in_direct_buffer(Object jBuffer, int sizeInBytes); private native final int native_read_in_direct_buffer(Object jBuffer, int sizeInBytes, boolean isBlocking); private native final int native_get_native_frame_count(); Loading Loading
api/current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -14910,9 +14910,12 @@ package android.media { method public int getSampleRate(); method public int getState(); method public int read(byte[], int, int); method public int read(byte[], int, int, int); method public int read(short[], int, int); method public int read(short[], int, int, int); method public int read(float[], int, int, int); method public int read(java.nio.ByteBuffer, int); method public int read(java.nio.ByteBuffer, int, int); method public void release(); method public int setNotificationMarkerPosition(int); method public int setPositionNotificationPeriod(int);
api/system-current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -16120,9 +16120,12 @@ package android.media { method public int getSampleRate(); method public int getState(); method public int read(byte[], int, int); method public int read(byte[], int, int, int); method public int read(short[], int, int); method public int read(short[], int, int, int); method public int read(float[], int, int, int); method public int read(java.nio.ByteBuffer, int); method public int read(java.nio.ByteBuffer, int, int); method public void release(); method public int setNotificationMarkerPosition(int); method public int setPositionNotificationPeriod(int);
core/jni/android_media_AudioRecord.cpp +69 −111 Original line number Diff line number Diff line Loading @@ -356,94 +356,57 @@ static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) { android_media_AudioRecord_release(env, thiz); } // ---------------------------------------------------------------------------- static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz, jbyteArray javaAudioData, jint offsetInBytes, jint sizeInBytes) { jbyte* recordBuff = NULL; // get the audio recorder from which we'll read new audio samples sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); if (lpRecorder == NULL) { ALOGE("Unable to retrieve AudioRecord object, can't record"); return 0; // overloaded JNI array helper functions static inline jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) { return env->GetByteArrayElements(array, isCopy); } if (!javaAudioData) { ALOGE("Invalid Java array to store recorded audio, can't record"); return 0; static inline void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) { env->ReleaseByteArrayElements(array, elems, mode); } // get the pointer to where we'll record the audio // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such // a way that it becomes much more efficient. When doing so, we will have to prevent the // AudioSystem callback to be called while in critical section (in case of media server // process crash for instance) recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); if (recordBuff == NULL) { ALOGE("Error retrieving destination for recorded audio data, can't record"); return 0; static inline jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) { return env->GetShortArrayElements(array, isCopy); } // read the new audio data from the native AudioRecord object ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes, sizeInBytes); env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0); if (readSize < 0) { readSize = (jint)AUDIO_JAVA_INVALID_OPERATION; } return (jint) readSize; static inline void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) { env->ReleaseShortArrayElements(array, elems, mode); } // ---------------------------------------------------------------------------- static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz, jshortArray javaAudioData, jint offsetInShorts, jint sizeInShorts) { jshort* recordBuff = NULL; // get the audio recorder from which we'll read new audio samples sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); if (lpRecorder == NULL) { ALOGE("Unable to retrieve AudioRecord object, can't record"); return 0; static inline jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) { return env->GetFloatArrayElements(array, isCopy); } if (!javaAudioData) { ALOGE("Invalid Java array to store recorded audio, can't record"); return 0; static inline void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) { env->ReleaseFloatArrayElements(array, elems, mode); } // get the pointer to where we'll record the audio // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such // a way that it becomes much more efficient. When doing so, we will have to prevent the // AudioSystem callback to be called while in critical section (in case of media server // process crash for instance) recordBuff = (jshort *)env->GetShortArrayElements(javaAudioData, NULL); if (recordBuff == NULL) { ALOGE("Error retrieving destination for recorded audio data, can't record"); return 0; } // read the new audio data from the native AudioRecord object const size_t sizeInBytes = sizeInShorts * sizeof(short); ssize_t readSize = lpRecorder->read(recordBuff + offsetInShorts, sizeInBytes); env->ReleaseShortArrayElements(javaAudioData, recordBuff, 0); if (readSize < 0) { readSize = (jint)AUDIO_JAVA_INVALID_OPERATION; } else { readSize /= sizeof(short); static inline jint interpretReadSizeError(ssize_t readSize) { ALOGE_IF(readSize != WOULD_BLOCK, "Error %zd during AudioRecord native read", readSize); switch (readSize) { case WOULD_BLOCK: return (jint)0; case BAD_VALUE: return (jint)AUDIO_JAVA_BAD_VALUE; default: // may be possible for other errors such as // NO_INIT to happen if restoreRecord_l fails. case INVALID_OPERATION: return (jint)AUDIO_JAVA_INVALID_OPERATION; } return (jint) readSize; } // ---------------------------------------------------------------------------- static jint android_media_AudioRecord_readInFloatArray(JNIEnv *env, jobject thiz, jfloatArray javaAudioData, jint offsetInFloats, jint sizeInFloats, template <typename T> static jint android_media_AudioRecord_readInArray(JNIEnv *env, jobject thiz, T javaAudioData, jint offsetInSamples, jint sizeInSamples, jboolean isReadBlocking) { // get the audio recorder from which we'll read new audio samples sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); Loading @@ -457,72 +420,64 @@ static jint android_media_AudioRecord_readInFloatArray(JNIEnv *env, jobject thi return (jint)AUDIO_JAVA_BAD_VALUE; } // get the pointer to where we'll record the audio // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such // a way that it becomes much more efficient. When doing so, we will have to prevent the // AudioSystem callback to be called while in critical section (in case of media server // process crash for instance) jfloat *recordBuff = (jfloat *)env->GetFloatArrayElements(javaAudioData, NULL); // get the pointer to where we'll record the audio auto *recordBuff = envGetArrayElements(env, javaAudioData, NULL); if (recordBuff == NULL) { ALOGE("Error retrieving destination for recorded audio data"); return (jint)AUDIO_JAVA_BAD_VALUE; } // read the new audio data from the native AudioRecord object const size_t sizeInBytes = sizeInFloats * sizeof(float); ssize_t readSize = lpRecorder->read(recordBuff + offsetInFloats, sizeInBytes); const size_t sizeInBytes = sizeInSamples * sizeof(*recordBuff); ssize_t readSize = lpRecorder->read( recordBuff + offsetInSamples, sizeInBytes, isReadBlocking == JNI_TRUE /* blocking */); env->ReleaseFloatArrayElements(javaAudioData, recordBuff, 0); envReleaseArrayElements(env, javaAudioData, recordBuff, 0); if (readSize < 0) { ALOGE_IF(readSize != WOULD_BLOCK, "Error %zd during AudioRecord native read", readSize); switch (readSize) { case WOULD_BLOCK: return (jint)0; case BAD_VALUE: return (jint)AUDIO_JAVA_BAD_VALUE; default: // may be possible for other errors such as // NO_INIT to happen if restoreRecord_l fails. case INVALID_OPERATION: return (jint)AUDIO_JAVA_INVALID_OPERATION; return interpretReadSizeError(readSize); } } return (jint)(readSize / sizeof(float)); return (jint)(readSize / sizeof(*recordBuff)); } // ---------------------------------------------------------------------------- static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz, jobject jBuffer, jint sizeInBytes) { jobject jBuffer, jint sizeInBytes, jboolean isReadBlocking) { // get the audio recorder from which we'll read new audio samples sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); if (lpRecorder==NULL) return 0; return (jint)AUDIO_JAVA_INVALID_OPERATION; // direct buffer and direct access supported? long capacity = env->GetDirectBufferCapacity(jBuffer); if (capacity == -1) { // buffer direct access is not supported ALOGE("Buffer direct access is not supported, can't record"); return 0; return (jint)AUDIO_JAVA_BAD_VALUE; } //ALOGV("capacity = %ld", capacity); jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer); if (nativeFromJavaBuf==NULL) { ALOGE("Buffer direct access is not supported, can't record"); return 0; return (jint)AUDIO_JAVA_BAD_VALUE; } // read new data from the recorder ssize_t readSize = lpRecorder->read(nativeFromJavaBuf, capacity < sizeInBytes ? capacity : sizeInBytes); capacity < sizeInBytes ? capacity : sizeInBytes, isReadBlocking == JNI_TRUE /* blocking */); if (readSize < 0) { readSize = (jint)AUDIO_JAVA_INVALID_OPERATION; return interpretReadSizeError(readSize); } return (jint)readSize; } // ---------------------------------------------------------------------------- static jint android_media_AudioRecord_get_native_frame_count(JNIEnv *env, jobject thiz) { sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); Loading Loading @@ -633,12 +588,15 @@ static JNINativeMethod gMethods[] = { {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize}, {"native_release", "()V", (void *)android_media_AudioRecord_release}, {"native_read_in_byte_array", "([BII)I", (void *)android_media_AudioRecord_readInByteArray}, "([BIIZ)I", (void *)android_media_AudioRecord_readInArray<jbyteArray>}, {"native_read_in_short_array", "([SII)I", (void *)android_media_AudioRecord_readInShortArray}, "([SIIZ)I", (void *)android_media_AudioRecord_readInArray<jshortArray>}, {"native_read_in_float_array", "([FIIZ)I", (void *)android_media_AudioRecord_readInFloatArray}, {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I", "([FIIZ)I", (void *)android_media_AudioRecord_readInArray<jfloatArray>}, {"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I", (void *)android_media_AudioRecord_readInDirectBuffer}, {"native_get_native_frame_count", "()I", (void *)android_media_AudioRecord_get_native_frame_count}, Loading
media/java/android/media/AudioRecord.java +114 −17 Original line number Diff line number Diff line Loading @@ -901,20 +901,63 @@ public class AudioRecord * the parameters don't resolve to valid data and indexes. * The number of bytes will not exceed sizeInBytes. */ public int read(byte[] audioData, int offsetInBytes, int sizeInBytes) { if (mState != STATE_INITIALIZED) { public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes) { return read(audioData, offsetInBytes, sizeInBytes, READ_BLOCKING); } /** * Reads audio data from the audio hardware for recording into a byte array. * The format specified in the AudioRecord constructor should be * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array. * @param audioData the array to which the recorded audio data is written. * @param offsetInBytes index in audioData from which the data is written expressed in bytes. * @param sizeInBytes the number of requested bytes. * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}. * <br>With {@link #READ_BLOCKING}, the read will block until all the requested data * is read. * <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after * reading as much audio data as possible without blocking. * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION} * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if * the parameters don't resolve to valid data and indexes. * The number of bytes will not exceed sizeInBytes. */ public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes, @ReadMode int readMode) { if (mState != STATE_INITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { return ERROR_INVALID_OPERATION; } if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) { Log.e(TAG, "AudioRecord.read() called with invalid blocking mode"); return ERROR_BAD_VALUE; } if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0) || (offsetInBytes + sizeInBytes < 0) // detect integer overflow || (offsetInBytes + sizeInBytes > audioData.length)) { return ERROR_BAD_VALUE; } return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes); return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes, readMode == READ_BLOCKING); } /** * Reads audio data from the audio hardware for recording into a short array. * The format specified in the AudioRecord constructor should be * {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array. * @param audioData the array to which the recorded audio data is written. * @param offsetInShorts index in audioData from which the data is written expressed in shorts. * @param sizeInShorts the number of requested shorts. * @return the number of shorts that were read or {@link #ERROR_INVALID_OPERATION} * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if * the parameters don't resolve to valid data and indexes. * The number of shorts will not exceed sizeInShorts. */ public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts) { return read(audioData, offsetInShorts, sizeInShorts, READ_BLOCKING); } /** * Reads audio data from the audio hardware for recording into a short array. Loading @@ -923,23 +966,35 @@ public class AudioRecord * @param audioData the array to which the recorded audio data is written. * @param offsetInShorts index in audioData from which the data is written expressed in shorts. * @param sizeInShorts the number of requested shorts. * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}. * <br>With {@link #READ_BLOCKING}, the read will block until all the requested data * is read. * <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after * reading as much audio data as possible without blocking. * @return the number of shorts that were read or {@link #ERROR_INVALID_OPERATION} * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if * the parameters don't resolve to valid data and indexes. * The number of shorts will not exceed sizeInShorts. */ public int read(short[] audioData, int offsetInShorts, int sizeInShorts) { if (mState != STATE_INITIALIZED) { public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts, @ReadMode int readMode) { if (mState != STATE_INITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { return ERROR_INVALID_OPERATION; } if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) { Log.e(TAG, "AudioRecord.read() called with invalid blocking mode"); return ERROR_BAD_VALUE; } if ( (audioData == null) || (offsetInShorts < 0 ) || (sizeInShorts < 0) || (offsetInShorts + sizeInShorts < 0) // detect integer overflow || (offsetInShorts + sizeInShorts > audioData.length)) { return ERROR_BAD_VALUE; } return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts); return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts, readMode == READ_BLOCKING); } /** Loading @@ -950,21 +1005,32 @@ public class AudioRecord * @param offsetInFloats index in audioData from which the data is written. * @param sizeInFloats the number of requested floats. * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}. * <BR>With {@link #READ_BLOCKING}, the read will block until all the requested data * <br>With {@link #READ_BLOCKING}, the read will block until all the requested data * is read. * <BR>With {@link #READ_NON_BLOCKING}, the read will return immediately after * <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after * reading as much audio data as possible without blocking. * @return the number of floats that were read or {@link #ERROR_INVALID_OPERATION} * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if * the parameters don't resolve to valid data and indexes. * The number of floats will not exceed sizeInFloats. */ public int read(float[] audioData, int offsetInFloats, int sizeInFloats, public int read(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats, @ReadMode int readMode) { if (mState != STATE_INITIALIZED) { if (mState == STATE_UNINITIALIZED) { Log.e(TAG, "AudioRecord.read() called in invalid state STATE_UNINITIALIZED"); return ERROR_INVALID_OPERATION; } if (mAudioFormat != AudioFormat.ENCODING_PCM_FLOAT) { Log.e(TAG, "AudioRecord.read(float[] ...) requires format ENCODING_PCM_FLOAT"); return ERROR_INVALID_OPERATION; } if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) { Log.e(TAG, "AudioRecord.read() called with invalid blocking mode"); return ERROR_BAD_VALUE; } if ((audioData == null) || (offsetInFloats < 0) || (sizeInFloats < 0) || (offsetInFloats + sizeInFloats < 0) // detect integer overflow || (offsetInFloats + sizeInFloats > audioData.length)) { Loading Loading @@ -992,18 +1058,48 @@ public class AudioRecord * The number of bytes will not exceed sizeInBytes. * The number of bytes read will truncated to be a multiple of the frame size. */ public int read(ByteBuffer audioBuffer, int sizeInBytes) { public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes) { return read(audioBuffer, sizeInBytes, READ_BLOCKING); } /** * Reads audio data from the audio hardware for recording into a direct buffer. If this buffer * is not a direct buffer, this method will always return 0. * Note that the value returned by {@link java.nio.Buffer#position()} on this buffer is * unchanged after a call to this method. * The representation of the data in the buffer will depend on the format specified in * the AudioRecord constructor, and will be native endian. * @param audioBuffer the direct buffer to which the recorded audio data is written. * @param sizeInBytes the number of requested bytes. It is recommended but not enforced * that the number of bytes requested be a multiple of the frame size (sample size in * bytes multiplied by the channel count). * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}. * <br>With {@link #READ_BLOCKING}, the read will block until all the requested data * is read. * <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after * reading as much audio data as possible without blocking. * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION} * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if * the parameters don't resolve to valid data and indexes. * The number of bytes will not exceed sizeInBytes. * The number of bytes read will truncated to be a multiple of the frame size. */ public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes, @ReadMode int readMode) { if (mState != STATE_INITIALIZED) { return ERROR_INVALID_OPERATION; } if ( (audioBuffer == null) || (sizeInBytes < 0) ) { if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) { Log.e(TAG, "AudioRecord.read() called with invalid blocking mode"); return ERROR_BAD_VALUE; } return native_read_in_direct_buffer(audioBuffer, sizeInBytes); if ( (audioBuffer == null) || (sizeInBytes < 0) ) { return ERROR_BAD_VALUE; } return native_read_in_direct_buffer(audioBuffer, sizeInBytes, readMode == READ_BLOCKING); } //-------------------------------------------------------------------------- // Initialization / configuration Loading Loading @@ -1186,15 +1282,16 @@ public class AudioRecord private native final void native_stop(); private native final int native_read_in_byte_array(byte[] audioData, int offsetInBytes, int sizeInBytes); int offsetInBytes, int sizeInBytes, boolean isBlocking); private native final int native_read_in_short_array(short[] audioData, int offsetInShorts, int sizeInShorts); int offsetInShorts, int sizeInShorts, boolean isBlocking); private native final int native_read_in_float_array(float[] audioData, int offsetInFloats, int sizeInFloats, boolean isBlocking); private native final int native_read_in_direct_buffer(Object jBuffer, int sizeInBytes); private native final int native_read_in_direct_buffer(Object jBuffer, int sizeInBytes, boolean isBlocking); private native final int native_get_native_frame_count(); Loading