Loading api/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -15001,7 +15001,9 @@ package android.media { method public int setVolume(float); method public void stop() throws java.lang.IllegalStateException; method public int write(byte[], int, int); method public int write(byte[], int, int, int); method public int write(short[], int, int); method public int write(short[], int, int, int); method public int write(float[], int, int, int); method public int write(java.nio.ByteBuffer, int, int); field public static final int ERROR = -1; // 0xffffffff api/system-current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -16213,7 +16213,9 @@ package android.media { method public int setVolume(float); method public void stop() throws java.lang.IllegalStateException; method public int write(byte[], int, int); method public int write(byte[], int, int, int); method public int write(short[], int, int); method public int write(short[], int, int, int); method public int write(float[], int, int, int); method public int write(java.nio.ByteBuffer, int, int); field public static final int ERROR = -1; // 0xffffffff core/jni/android_media_AudioTrack.cpp +71 −118 Original line number Diff line number Diff line Loading @@ -510,14 +510,47 @@ static void android_media_AudioTrack_finalize(JNIEnv *env, jobject thiz) { android_media_AudioTrack_release(env, thiz); } // overloaded JNI array helper functions (same as in android_media_AudioRecord) static inline jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) { return env->GetByteArrayElements(array, isCopy); } static inline void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) { env->ReleaseByteArrayElements(array, elems, mode); } static inline jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) { return env->GetShortArrayElements(array, isCopy); } static inline void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) { env->ReleaseShortArrayElements(array, elems, mode); } static inline jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) { return env->GetFloatArrayElements(array, isCopy); } static inline void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) { env->ReleaseFloatArrayElements(array, elems, mode); } // ---------------------------------------------------------------------------- jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* data, jint offsetInBytes, jint sizeInBytes, bool blocking = true) { template <typename T> static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data, jint offsetInSamples, jint sizeInSamples, bool blocking) { // give the data to the native AudioTrack object (the data starts at the offset) ssize_t written = 0; // regular write() or copy the data to the AudioTrack's shared memory? size_t sizeInBytes = sizeInSamples * sizeof(T); if (track->sharedBuffer() == 0) { written = track->write(data + offsetInBytes, sizeInBytes, blocking); written = track->write(data + offsetInSamples, sizeInBytes, blocking); // for compatibility with earlier behavior of write(), return 0 in this case if (written == (ssize_t) WOULD_BLOCK) { written = 0; Loading @@ -527,55 +560,59 @@ jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* da if ((size_t)sizeInBytes > track->sharedBuffer()->size()) { sizeInBytes = track->sharedBuffer()->size(); } memcpy(track->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes); memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes); written = sizeInBytes; } if (written > 0) { return written / sizeof(T); } // for compatibility, error codes pass through unchanged return written; } // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_write_byte(JNIEnv *env, jobject thiz, jbyteArray javaAudioData, jint offsetInBytes, jint sizeInBytes, template <typename T> static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz, T javaAudioData, jint offsetInSamples, jint sizeInSamples, jint javaAudioFormat, jboolean isWriteBlocking) { //ALOGV("android_media_AudioTrack_write_byte(offset=%d, sizeInBytes=%d) called", // offsetInBytes, sizeInBytes); //ALOGV("android_media_AudioTrack_writeArray(offset=%d, sizeInSamples=%d) called", // offsetInSamples, sizeInSamples); sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioTrack pointer for write()"); return 0; return (jint)AUDIO_JAVA_INVALID_OPERATION; } if (javaAudioData == NULL) { ALOGE("NULL java array of audio data to play"); return (jint)AUDIO_JAVA_BAD_VALUE; } // get the pointer for the audio data from the java array // 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) jbyte* cAudioData = NULL; if (javaAudioData) { cAudioData = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); // get the pointer for the audio data from the java array auto cAudioData = envGetArrayElements(env, javaAudioData, NULL); if (cAudioData == NULL) { ALOGE("Error retrieving source of audio data to play, can't play"); return 0; // out of memory or no data to load } } else { ALOGE("NULL java array of audio data to play, can't play"); return 0; ALOGE("Error retrieving source of audio data to play"); return (jint)AUDIO_JAVA_BAD_VALUE; // out of memory or no data to load } jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */); jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */); env->ReleaseByteArrayElements(javaAudioData, cAudioData, 0); envReleaseArrayElements(env, javaAudioData, cAudioData, 0); //ALOGV("write wrote %d (tried %d) bytes in the native AudioTrack with offset %d", // (int)written, (int)(sizeInBytes), (int)offsetInBytes); return written; //ALOGV("write wrote %d (tried %d) samples in the native AudioTrack with offset %d", // (int)samplesWritten, (int)(sizeInSamples), (int)offsetInSamples); return samplesWritten; } // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz, jbyteArray javaBytes, jint byteOffset, jint sizeInBytes, Loading @@ -586,7 +623,7 @@ static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject th if (lpTrack == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioTrack pointer for write()"); return 0; return (jint)AUDIO_JAVA_INVALID_OPERATION; } ScopedBytesRO bytes(env, javaBytes); Loading @@ -601,90 +638,6 @@ static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject th return written; } // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_write_short(JNIEnv *env, jobject thiz, jshortArray javaAudioData, jint offsetInShorts, jint sizeInShorts, jint javaAudioFormat) { //ALOGV("android_media_AudioTrack_write_short(offset=%d, sizeInShorts=%d) called", // offsetInShorts, sizeInShorts); sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioTrack pointer for write()"); return 0; } // get the pointer for the audio data from the java array // 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) jshort* cAudioData = NULL; if (javaAudioData) { cAudioData = (jshort *)env->GetShortArrayElements(javaAudioData, NULL); if (cAudioData == NULL) { ALOGE("Error retrieving source of audio data to play, can't play"); return 0; // out of memory or no data to load } } else { ALOGE("NULL java array of audio data to play, can't play"); return 0; } jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData, offsetInShorts * sizeof(short), sizeInShorts * sizeof(short), true /*blocking write, legacy behavior*/); env->ReleaseShortArrayElements(javaAudioData, cAudioData, 0); if (written > 0) { written /= sizeof(short); } //ALOGV("write wrote %d (tried %d) shorts in the native AudioTrack with offset %d", // (int)written, (int)(sizeInShorts), (int)offsetInShorts); return written; } // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_write_float(JNIEnv *env, jobject thiz, jfloatArray javaAudioData, jint offsetInFloats, jint sizeInFloats, jint javaAudioFormat, jboolean isWriteBlocking) { sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioTrack pointer for write()"); return 0; } jfloat* cAudioData = NULL; if (javaAudioData) { cAudioData = (jfloat *)env->GetFloatArrayElements(javaAudioData, NULL); if (cAudioData == NULL) { ALOGE("Error retrieving source of audio data to play, can't play"); return 0; // out of memory or no data to load } } else { ALOGE("NULL java array of audio data to play, can't play"); return 0; } jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData, offsetInFloats * sizeof(float), sizeInFloats * sizeof(float), isWriteBlocking == JNI_TRUE /* blocking */); env->ReleaseFloatArrayElements(javaAudioData, cAudioData, 0); if (written > 0) { written /= sizeof(float); } return written; } // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env, jobject thiz) { sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); Loading Loading @@ -976,12 +929,12 @@ static JNINativeMethod gMethods[] = { (void *)android_media_AudioTrack_setup}, {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize}, {"native_release", "()V", (void *)android_media_AudioTrack_release}, {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_write_byte}, {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>}, {"native_write_native_bytes", "(Ljava/lang/Object;IIIZ)I", (void *)android_media_AudioTrack_write_native_bytes}, {"native_write_short", "([SIII)I", (void *)android_media_AudioTrack_write_short}, {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_write_float}, {"native_write_short", "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>}, {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>}, {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume}, {"native_get_native_frame_count", "()I", (void *)android_media_AudioTrack_get_native_frame_count}, Loading media/java/android/media/AudioTrack.java +89 −11 Original line number Diff line number Diff line Loading @@ -1540,6 +1540,8 @@ public class AudioTrack /** * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). * The format specified in the AudioTrack constructor should be * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array. * In streaming mode, will block until all data has been written to the audio sink. * In static buffer mode, copies the data to the buffer starting at offset 0. * Note that the actual playback of this data might occur after this function Loading @@ -1556,13 +1558,49 @@ public class AudioTrack * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and * needs to be recreated. */ public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes) { return write(audioData, offsetInBytes, sizeInBytes, WRITE_BLOCKING); } public int write(byte[] audioData, int offsetInBytes, int sizeInBytes) { /** * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). * The format specified in the AudioTrack constructor should be * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array. * In streaming mode, will block until all data has been written to the audio sink. * In static buffer mode, copies the data to the buffer starting at offset 0. * Note that the actual playback of this data might occur after this function * returns. This function is thread safe with respect to {@link #stop} calls, * in which case all of the specified data might not be written to the audio sink. * * @param audioData the array that holds the data to play. * @param offsetInBytes the offset expressed in bytes in audioData where the data to play * starts. * @param sizeInBytes the number of bytes to read in audioData after the offset. * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no * effect in static mode. * <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written * to the audio sink. * <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after * queuing as much audio data for playback as possible without blocking. * @return the number of bytes that were written 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, or * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and * needs to be recreated. */ public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes, @WriteMode int writeMode) { if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { return ERROR_INVALID_OPERATION; } if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) { Log.e(TAG, "AudioTrack.write() 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)) { Loading @@ -1570,7 +1608,7 @@ public class AudioTrack } int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat, true /*isBlocking*/); writeMode == WRITE_BLOCKING); if ((mDataLoadMode == MODE_STATIC) && (mState == STATE_NO_STATIC_DATA) Loading @@ -1582,10 +1620,11 @@ public class AudioTrack return ret; } /** * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). * The format specified in the AudioTrack constructor should be * {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array. * In streaming mode, will block until all data has been written to the audio sink. * In static buffer mode, copies the data to the buffer starting at offset 0. * Note that the actual playback of this data might occur after this function Loading @@ -1602,20 +1641,57 @@ public class AudioTrack * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and * needs to be recreated. */ public int write(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts) { return write(audioData, offsetInShorts, sizeInShorts, WRITE_BLOCKING); } public int write(short[] audioData, int offsetInShorts, int sizeInShorts) { /** * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). * The format specified in the AudioTrack constructor should be * {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array. * In streaming mode, will block until all data has been written to the audio sink. * In static buffer mode, copies the data to the buffer starting at offset 0. * Note that the actual playback of this data might occur after this function * returns. This function is thread safe with respect to {@link #stop} calls, * in which case all of the specified data might not be written to the audio sink. * * @param audioData the array that holds the data to play. * @param offsetInShorts the offset expressed in shorts in audioData where the data to play * starts. * @param sizeInShorts the number of shorts to read in audioData after the offset. * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no * effect in static mode. * <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written * to the audio sink. * <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after * queuing as much audio data for playback as possible without blocking. * @return the number of shorts that were written 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, or * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and * needs to be recreated. */ public int write(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts, @WriteMode int writeMode) { if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { return ERROR_INVALID_OPERATION; } if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) { Log.e(TAG, "AudioTrack.write() 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; } int ret = native_write_short(audioData, offsetInShorts, sizeInShorts, mAudioFormat); int ret = native_write_short(audioData, offsetInShorts, sizeInShorts, mAudioFormat, writeMode == WRITE_BLOCKING); if ((mDataLoadMode == MODE_STATIC) && (mState == STATE_NO_STATIC_DATA) Loading @@ -1627,10 +1703,11 @@ public class AudioTrack return ret; } /** * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). * The format specified in the AudioTrack constructor should be * {@link AudioFormat#ENCODING_PCM_FLOAT} to correspond to the data in the array. * In static buffer mode, copies the data to the buffer starting at offset 0, * and the write mode is ignored. * In streaming mode, the blocking behavior will depend on the write mode. Loading @@ -1654,9 +1731,9 @@ public class AudioTrack * @param sizeInFloats the number of floats to read in audioData after the offset. * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no * effect in static mode. * <BR>With {@link #WRITE_BLOCKING}, the write will block until all data has been written * <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written * to the audio sink. * <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after * <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after * queuing as much audio data for playback as possible without blocking. * @return the number of floats that were written, or {@link #ERROR_INVALID_OPERATION} * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if Loading @@ -1664,7 +1741,7 @@ public class AudioTrack * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and * needs to be recreated. */ public int write(float[] audioData, int offsetInFloats, int sizeInFloats, public int write(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats, @WriteMode int writeMode) { if (mState == STATE_UNINITIALIZED) { Loading Loading @@ -1727,7 +1804,7 @@ public class AudioTrack * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and * needs to be recreated. */ public int write(ByteBuffer audioData, int sizeInBytes, public int write(@NonNull ByteBuffer audioData, int sizeInBytes, @WriteMode int writeMode) { if (mState == STATE_UNINITIALIZED) { Loading Loading @@ -2017,7 +2094,8 @@ public class AudioTrack boolean isBlocking); private native final int native_write_short(short[] audioData, int offsetInShorts, int sizeInShorts, int format); int offsetInShorts, int sizeInShorts, int format, boolean isBlocking); private native final int native_write_float(float[] audioData, int offsetInFloats, int sizeInFloats, int format, Loading Loading
api/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -15001,7 +15001,9 @@ package android.media { method public int setVolume(float); method public void stop() throws java.lang.IllegalStateException; method public int write(byte[], int, int); method public int write(byte[], int, int, int); method public int write(short[], int, int); method public int write(short[], int, int, int); method public int write(float[], int, int, int); method public int write(java.nio.ByteBuffer, int, int); field public static final int ERROR = -1; // 0xffffffff
api/system-current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -16213,7 +16213,9 @@ package android.media { method public int setVolume(float); method public void stop() throws java.lang.IllegalStateException; method public int write(byte[], int, int); method public int write(byte[], int, int, int); method public int write(short[], int, int); method public int write(short[], int, int, int); method public int write(float[], int, int, int); method public int write(java.nio.ByteBuffer, int, int); field public static final int ERROR = -1; // 0xffffffff
core/jni/android_media_AudioTrack.cpp +71 −118 Original line number Diff line number Diff line Loading @@ -510,14 +510,47 @@ static void android_media_AudioTrack_finalize(JNIEnv *env, jobject thiz) { android_media_AudioTrack_release(env, thiz); } // overloaded JNI array helper functions (same as in android_media_AudioRecord) static inline jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) { return env->GetByteArrayElements(array, isCopy); } static inline void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) { env->ReleaseByteArrayElements(array, elems, mode); } static inline jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) { return env->GetShortArrayElements(array, isCopy); } static inline void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) { env->ReleaseShortArrayElements(array, elems, mode); } static inline jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) { return env->GetFloatArrayElements(array, isCopy); } static inline void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) { env->ReleaseFloatArrayElements(array, elems, mode); } // ---------------------------------------------------------------------------- jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* data, jint offsetInBytes, jint sizeInBytes, bool blocking = true) { template <typename T> static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data, jint offsetInSamples, jint sizeInSamples, bool blocking) { // give the data to the native AudioTrack object (the data starts at the offset) ssize_t written = 0; // regular write() or copy the data to the AudioTrack's shared memory? size_t sizeInBytes = sizeInSamples * sizeof(T); if (track->sharedBuffer() == 0) { written = track->write(data + offsetInBytes, sizeInBytes, blocking); written = track->write(data + offsetInSamples, sizeInBytes, blocking); // for compatibility with earlier behavior of write(), return 0 in this case if (written == (ssize_t) WOULD_BLOCK) { written = 0; Loading @@ -527,55 +560,59 @@ jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* da if ((size_t)sizeInBytes > track->sharedBuffer()->size()) { sizeInBytes = track->sharedBuffer()->size(); } memcpy(track->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes); memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes); written = sizeInBytes; } if (written > 0) { return written / sizeof(T); } // for compatibility, error codes pass through unchanged return written; } // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_write_byte(JNIEnv *env, jobject thiz, jbyteArray javaAudioData, jint offsetInBytes, jint sizeInBytes, template <typename T> static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz, T javaAudioData, jint offsetInSamples, jint sizeInSamples, jint javaAudioFormat, jboolean isWriteBlocking) { //ALOGV("android_media_AudioTrack_write_byte(offset=%d, sizeInBytes=%d) called", // offsetInBytes, sizeInBytes); //ALOGV("android_media_AudioTrack_writeArray(offset=%d, sizeInSamples=%d) called", // offsetInSamples, sizeInSamples); sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioTrack pointer for write()"); return 0; return (jint)AUDIO_JAVA_INVALID_OPERATION; } if (javaAudioData == NULL) { ALOGE("NULL java array of audio data to play"); return (jint)AUDIO_JAVA_BAD_VALUE; } // get the pointer for the audio data from the java array // 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) jbyte* cAudioData = NULL; if (javaAudioData) { cAudioData = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); // get the pointer for the audio data from the java array auto cAudioData = envGetArrayElements(env, javaAudioData, NULL); if (cAudioData == NULL) { ALOGE("Error retrieving source of audio data to play, can't play"); return 0; // out of memory or no data to load } } else { ALOGE("NULL java array of audio data to play, can't play"); return 0; ALOGE("Error retrieving source of audio data to play"); return (jint)AUDIO_JAVA_BAD_VALUE; // out of memory or no data to load } jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */); jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */); env->ReleaseByteArrayElements(javaAudioData, cAudioData, 0); envReleaseArrayElements(env, javaAudioData, cAudioData, 0); //ALOGV("write wrote %d (tried %d) bytes in the native AudioTrack with offset %d", // (int)written, (int)(sizeInBytes), (int)offsetInBytes); return written; //ALOGV("write wrote %d (tried %d) samples in the native AudioTrack with offset %d", // (int)samplesWritten, (int)(sizeInSamples), (int)offsetInSamples); return samplesWritten; } // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz, jbyteArray javaBytes, jint byteOffset, jint sizeInBytes, Loading @@ -586,7 +623,7 @@ static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject th if (lpTrack == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioTrack pointer for write()"); return 0; return (jint)AUDIO_JAVA_INVALID_OPERATION; } ScopedBytesRO bytes(env, javaBytes); Loading @@ -601,90 +638,6 @@ static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject th return written; } // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_write_short(JNIEnv *env, jobject thiz, jshortArray javaAudioData, jint offsetInShorts, jint sizeInShorts, jint javaAudioFormat) { //ALOGV("android_media_AudioTrack_write_short(offset=%d, sizeInShorts=%d) called", // offsetInShorts, sizeInShorts); sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioTrack pointer for write()"); return 0; } // get the pointer for the audio data from the java array // 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) jshort* cAudioData = NULL; if (javaAudioData) { cAudioData = (jshort *)env->GetShortArrayElements(javaAudioData, NULL); if (cAudioData == NULL) { ALOGE("Error retrieving source of audio data to play, can't play"); return 0; // out of memory or no data to load } } else { ALOGE("NULL java array of audio data to play, can't play"); return 0; } jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData, offsetInShorts * sizeof(short), sizeInShorts * sizeof(short), true /*blocking write, legacy behavior*/); env->ReleaseShortArrayElements(javaAudioData, cAudioData, 0); if (written > 0) { written /= sizeof(short); } //ALOGV("write wrote %d (tried %d) shorts in the native AudioTrack with offset %d", // (int)written, (int)(sizeInShorts), (int)offsetInShorts); return written; } // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_write_float(JNIEnv *env, jobject thiz, jfloatArray javaAudioData, jint offsetInFloats, jint sizeInFloats, jint javaAudioFormat, jboolean isWriteBlocking) { sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioTrack pointer for write()"); return 0; } jfloat* cAudioData = NULL; if (javaAudioData) { cAudioData = (jfloat *)env->GetFloatArrayElements(javaAudioData, NULL); if (cAudioData == NULL) { ALOGE("Error retrieving source of audio data to play, can't play"); return 0; // out of memory or no data to load } } else { ALOGE("NULL java array of audio data to play, can't play"); return 0; } jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData, offsetInFloats * sizeof(float), sizeInFloats * sizeof(float), isWriteBlocking == JNI_TRUE /* blocking */); env->ReleaseFloatArrayElements(javaAudioData, cAudioData, 0); if (written > 0) { written /= sizeof(float); } return written; } // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env, jobject thiz) { sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); Loading Loading @@ -976,12 +929,12 @@ static JNINativeMethod gMethods[] = { (void *)android_media_AudioTrack_setup}, {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize}, {"native_release", "()V", (void *)android_media_AudioTrack_release}, {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_write_byte}, {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>}, {"native_write_native_bytes", "(Ljava/lang/Object;IIIZ)I", (void *)android_media_AudioTrack_write_native_bytes}, {"native_write_short", "([SIII)I", (void *)android_media_AudioTrack_write_short}, {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_write_float}, {"native_write_short", "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>}, {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>}, {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume}, {"native_get_native_frame_count", "()I", (void *)android_media_AudioTrack_get_native_frame_count}, Loading
media/java/android/media/AudioTrack.java +89 −11 Original line number Diff line number Diff line Loading @@ -1540,6 +1540,8 @@ public class AudioTrack /** * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). * The format specified in the AudioTrack constructor should be * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array. * In streaming mode, will block until all data has been written to the audio sink. * In static buffer mode, copies the data to the buffer starting at offset 0. * Note that the actual playback of this data might occur after this function Loading @@ -1556,13 +1558,49 @@ public class AudioTrack * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and * needs to be recreated. */ public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes) { return write(audioData, offsetInBytes, sizeInBytes, WRITE_BLOCKING); } public int write(byte[] audioData, int offsetInBytes, int sizeInBytes) { /** * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). * The format specified in the AudioTrack constructor should be * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array. * In streaming mode, will block until all data has been written to the audio sink. * In static buffer mode, copies the data to the buffer starting at offset 0. * Note that the actual playback of this data might occur after this function * returns. This function is thread safe with respect to {@link #stop} calls, * in which case all of the specified data might not be written to the audio sink. * * @param audioData the array that holds the data to play. * @param offsetInBytes the offset expressed in bytes in audioData where the data to play * starts. * @param sizeInBytes the number of bytes to read in audioData after the offset. * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no * effect in static mode. * <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written * to the audio sink. * <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after * queuing as much audio data for playback as possible without blocking. * @return the number of bytes that were written 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, or * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and * needs to be recreated. */ public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes, @WriteMode int writeMode) { if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { return ERROR_INVALID_OPERATION; } if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) { Log.e(TAG, "AudioTrack.write() 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)) { Loading @@ -1570,7 +1608,7 @@ public class AudioTrack } int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat, true /*isBlocking*/); writeMode == WRITE_BLOCKING); if ((mDataLoadMode == MODE_STATIC) && (mState == STATE_NO_STATIC_DATA) Loading @@ -1582,10 +1620,11 @@ public class AudioTrack return ret; } /** * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). * The format specified in the AudioTrack constructor should be * {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array. * In streaming mode, will block until all data has been written to the audio sink. * In static buffer mode, copies the data to the buffer starting at offset 0. * Note that the actual playback of this data might occur after this function Loading @@ -1602,20 +1641,57 @@ public class AudioTrack * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and * needs to be recreated. */ public int write(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts) { return write(audioData, offsetInShorts, sizeInShorts, WRITE_BLOCKING); } public int write(short[] audioData, int offsetInShorts, int sizeInShorts) { /** * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). * The format specified in the AudioTrack constructor should be * {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array. * In streaming mode, will block until all data has been written to the audio sink. * In static buffer mode, copies the data to the buffer starting at offset 0. * Note that the actual playback of this data might occur after this function * returns. This function is thread safe with respect to {@link #stop} calls, * in which case all of the specified data might not be written to the audio sink. * * @param audioData the array that holds the data to play. * @param offsetInShorts the offset expressed in shorts in audioData where the data to play * starts. * @param sizeInShorts the number of shorts to read in audioData after the offset. * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no * effect in static mode. * <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written * to the audio sink. * <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after * queuing as much audio data for playback as possible without blocking. * @return the number of shorts that were written 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, or * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and * needs to be recreated. */ public int write(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts, @WriteMode int writeMode) { if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { return ERROR_INVALID_OPERATION; } if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) { Log.e(TAG, "AudioTrack.write() 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; } int ret = native_write_short(audioData, offsetInShorts, sizeInShorts, mAudioFormat); int ret = native_write_short(audioData, offsetInShorts, sizeInShorts, mAudioFormat, writeMode == WRITE_BLOCKING); if ((mDataLoadMode == MODE_STATIC) && (mState == STATE_NO_STATIC_DATA) Loading @@ -1627,10 +1703,11 @@ public class AudioTrack return ret; } /** * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). * The format specified in the AudioTrack constructor should be * {@link AudioFormat#ENCODING_PCM_FLOAT} to correspond to the data in the array. * In static buffer mode, copies the data to the buffer starting at offset 0, * and the write mode is ignored. * In streaming mode, the blocking behavior will depend on the write mode. Loading @@ -1654,9 +1731,9 @@ public class AudioTrack * @param sizeInFloats the number of floats to read in audioData after the offset. * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no * effect in static mode. * <BR>With {@link #WRITE_BLOCKING}, the write will block until all data has been written * <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written * to the audio sink. * <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after * <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after * queuing as much audio data for playback as possible without blocking. * @return the number of floats that were written, or {@link #ERROR_INVALID_OPERATION} * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if Loading @@ -1664,7 +1741,7 @@ public class AudioTrack * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and * needs to be recreated. */ public int write(float[] audioData, int offsetInFloats, int sizeInFloats, public int write(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats, @WriteMode int writeMode) { if (mState == STATE_UNINITIALIZED) { Loading Loading @@ -1727,7 +1804,7 @@ public class AudioTrack * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and * needs to be recreated. */ public int write(ByteBuffer audioData, int sizeInBytes, public int write(@NonNull ByteBuffer audioData, int sizeInBytes, @WriteMode int writeMode) { if (mState == STATE_UNINITIALIZED) { Loading Loading @@ -2017,7 +2094,8 @@ public class AudioTrack boolean isBlocking); private native final int native_write_short(short[] audioData, int offsetInShorts, int sizeInShorts, int format); int offsetInShorts, int sizeInShorts, int format, boolean isBlocking); private native final int native_write_float(float[] audioData, int offsetInFloats, int sizeInFloats, int format, Loading