Loading core/jni/android_media_AudioTrack.cpp +41 −8 Original line number Diff line number Diff line Loading @@ -17,10 +17,12 @@ #define LOG_TAG "AudioTrack-JNI" #include <jni.h> #include <JNIHelp.h> #include <JniConstants.h> #include <android_runtime/AndroidRuntime.h> #include "ScopedBytes.h" #include <utils/Log.h> #include <media/AudioSystem.h> #include <media/AudioTrack.h> Loading Loading @@ -503,13 +505,13 @@ static void android_media_AudioTrack_finalize(JNIEnv *env, jobject thiz) { } // ---------------------------------------------------------------------------- jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, jbyte* data, jint offsetInBytes, jint sizeInBytes) { jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* data, jint offsetInBytes, jint sizeInBytes, bool blocking = true) { // 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? if (track->sharedBuffer() == 0) { written = track->write(data + offsetInBytes, sizeInBytes); written = track->write(data + offsetInBytes, sizeInBytes, blocking); // for compatibility with earlier behavior of write(), return 0 in this case if (written == (ssize_t) WOULD_BLOCK) { written = 0; Loading Loading @@ -563,7 +565,8 @@ jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, jbyte* data, static jint android_media_AudioTrack_write_byte(JNIEnv *env, jobject thiz, jbyteArray javaAudioData, jint offsetInBytes, jint sizeInBytes, jint javaAudioFormat) { jint javaAudioFormat, jboolean isWriteBlocking) { //ALOGV("android_media_AudioTrack_write_byte(offset=%d, sizeInBytes=%d) called", // offsetInBytes, sizeInBytes); sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); Loading @@ -590,7 +593,8 @@ static jint android_media_AudioTrack_write_byte(JNIEnv *env, jobject thiz, return 0; } jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes); jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */); env->ReleaseByteArrayElements(javaAudioData, cAudioData, 0); Loading @@ -600,6 +604,31 @@ static jint android_media_AudioTrack_write_byte(JNIEnv *env, jobject thiz, } // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz, jbyteArray javaBytes, jint byteOffset, jint offsetInBytes, jint sizeInBytes, jint javaAudioFormat, jboolean isWriteBlocking) { //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called", // offsetInBytes, sizeInBytes); sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioTrack pointer for write()"); return 0; } ScopedBytesRO bytes(env, javaBytes); if (bytes.get() == NULL) { ALOGE("Error retrieving source of audio data to play, can't play"); return AUDIOTRACK_ERROR_BAD_VALUE; } jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get() + byteOffset, offsetInBytes, sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */); return written; } // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_write_short(JNIEnv *env, jobject thiz, jshortArray javaAudioData, Loading @@ -608,7 +637,8 @@ static jint android_media_AudioTrack_write_short(JNIEnv *env, jobject thiz, jint written = android_media_AudioTrack_write_byte(env, thiz, (jbyteArray) javaAudioData, offsetInShorts*2, sizeInShorts*2, javaAudioFormat); javaAudioFormat, JNI_TRUE /*blocking write, legacy behavior*/); if (written > 0) { written /= 2; } Loading Loading @@ -890,7 +920,10 @@ 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", "([BIII)I", (void *)android_media_AudioTrack_write_byte}, {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_write_byte}, {"native_write_native_bytes", "(Ljava/lang/Object;IIIIZ)I", (void *)android_media_AudioTrack_write_native_bytes}, {"native_write_short", "([SIII)I", (void *)android_media_AudioTrack_write_short}, {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume}, {"native_get_native_frame_count", Loading media/java/android/media/AudioTrack.java +103 −2 Original line number Diff line number Diff line Loading @@ -16,8 +16,13 @@ package android.media; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.nio.ByteBuffer; import java.nio.NioUtils; import android.annotation.IntDef; import android.os.Handler; import android.os.Looper; import android.os.Message; Loading Loading @@ -145,6 +150,28 @@ public class AudioTrack private final static String TAG = "android.media.AudioTrack"; /** @hide */ @IntDef({ WRITE_BLOCKING, WRITE_NON_BLOCKING }) @Retention(RetentionPolicy.SOURCE) public @interface WriteMode {} /** * @hide CANDIDATE FOR PUBLIC API * The write mode indicating the write operation will block until all data has been written, * to be used in {@link #write(ByteBuffer, int, int, int)}. */ public final static int WRITE_BLOCKING = 0; /** * @hide CANDIDATE FOR PUBLIC API * The write mode indicating the write operation will return immediately after * queuing as much audio data for playback as possible without blocking, to be used in * {@link #write(ByteBuffer, int, int, int)}. */ public final static int WRITE_NON_BLOCKING = 1; //-------------------------------------------------------------------------- // Member variables //-------------------- Loading Loading @@ -1084,7 +1111,8 @@ public class AudioTrack return ERROR_BAD_VALUE; } int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat); int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat, true /*isBlocking*/); if ((mDataLoadMode == MODE_STATIC) && (mState == STATE_NO_STATIC_DATA) Loading Loading @@ -1140,6 +1168,75 @@ public class AudioTrack } /** * @hide CANDIDATE FOR PUBLIC API * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). * In static buffer mode, copies the data to the buffer starting at its 0 offset, and the write * mode is ignored. * In streaming mode, the blocking behavior will depend on the write mode. * @param audioData the buffer that holds the data to play, starting at the position reported * by <code>audioData.position()</code>. * <BR>Note that this method will not update the position in this buffer, therefore when * writing a loop to write all the data in the buffer, you should increment the * <code>offsetInBytes</code> parameter at each pass by the amount that was previously * written for this buffer. * @param offsetInBytes offset to read from in bytes (note this differs from * <code>audioData.position()</code>). * @param sizeInBytes number of bytes to read (note this differs from * <code>audioData.remaining()</code>). * @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 0 or a positive number of bytes that were written, or * {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION} */ public int write(ByteBuffer audioData, int offsetInBytes, int sizeInBytes, @WriteMode int writeMode) { if (mState == STATE_UNINITIALIZED) { Log.e(TAG, "AudioTrack.write() called in invalid state STATE_UNINITIALIZED"); 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.remaining())) { Log.e(TAG, "AudioTrack.write() called with invalid size/offset values"); return ERROR_BAD_VALUE; } int ret = 0; if (audioData.isDirect()) { ret = native_write_native_bytes(audioData, audioData.position(), offsetInBytes, sizeInBytes, mAudioFormat, writeMode == WRITE_BLOCKING); } else { ret = native_write_byte(NioUtils.unsafeArray(audioData), NioUtils.unsafeArrayOffset(audioData) + audioData.position() + offsetInBytes, sizeInBytes, mAudioFormat, writeMode == WRITE_BLOCKING); } if ((mDataLoadMode == MODE_STATIC) && (mState == STATE_NO_STATIC_DATA) && (ret > 0)) { // benign race with respect to other APIs that read mState mState = STATE_INITIALIZED; } return ret; } /** * Notifies the native resource to reuse the audio data already loaded in the native * layer, that is to rewind to start of buffer. Loading Loading @@ -1339,11 +1436,15 @@ public class AudioTrack private native final void native_flush(); private native final int native_write_byte(byte[] audioData, int offsetInBytes, int sizeInBytes, int format); int offsetInBytes, int sizeInBytes, int format, boolean isBlocking); private native final int native_write_short(short[] audioData, int offsetInShorts, int sizeInShorts, int format); private native final int native_write_native_bytes(Object audioData, int positionInBytes, int offsetInBytes, int sizeInBytes, int format, boolean blocking); private native final int native_reload_static(); private native final int native_get_native_frame_count(); Loading Loading
core/jni/android_media_AudioTrack.cpp +41 −8 Original line number Diff line number Diff line Loading @@ -17,10 +17,12 @@ #define LOG_TAG "AudioTrack-JNI" #include <jni.h> #include <JNIHelp.h> #include <JniConstants.h> #include <android_runtime/AndroidRuntime.h> #include "ScopedBytes.h" #include <utils/Log.h> #include <media/AudioSystem.h> #include <media/AudioTrack.h> Loading Loading @@ -503,13 +505,13 @@ static void android_media_AudioTrack_finalize(JNIEnv *env, jobject thiz) { } // ---------------------------------------------------------------------------- jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, jbyte* data, jint offsetInBytes, jint sizeInBytes) { jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* data, jint offsetInBytes, jint sizeInBytes, bool blocking = true) { // 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? if (track->sharedBuffer() == 0) { written = track->write(data + offsetInBytes, sizeInBytes); written = track->write(data + offsetInBytes, sizeInBytes, blocking); // for compatibility with earlier behavior of write(), return 0 in this case if (written == (ssize_t) WOULD_BLOCK) { written = 0; Loading Loading @@ -563,7 +565,8 @@ jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, jbyte* data, static jint android_media_AudioTrack_write_byte(JNIEnv *env, jobject thiz, jbyteArray javaAudioData, jint offsetInBytes, jint sizeInBytes, jint javaAudioFormat) { jint javaAudioFormat, jboolean isWriteBlocking) { //ALOGV("android_media_AudioTrack_write_byte(offset=%d, sizeInBytes=%d) called", // offsetInBytes, sizeInBytes); sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); Loading @@ -590,7 +593,8 @@ static jint android_media_AudioTrack_write_byte(JNIEnv *env, jobject thiz, return 0; } jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes); jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */); env->ReleaseByteArrayElements(javaAudioData, cAudioData, 0); Loading @@ -600,6 +604,31 @@ static jint android_media_AudioTrack_write_byte(JNIEnv *env, jobject thiz, } // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz, jbyteArray javaBytes, jint byteOffset, jint offsetInBytes, jint sizeInBytes, jint javaAudioFormat, jboolean isWriteBlocking) { //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called", // offsetInBytes, sizeInBytes); sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioTrack pointer for write()"); return 0; } ScopedBytesRO bytes(env, javaBytes); if (bytes.get() == NULL) { ALOGE("Error retrieving source of audio data to play, can't play"); return AUDIOTRACK_ERROR_BAD_VALUE; } jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get() + byteOffset, offsetInBytes, sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */); return written; } // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_write_short(JNIEnv *env, jobject thiz, jshortArray javaAudioData, Loading @@ -608,7 +637,8 @@ static jint android_media_AudioTrack_write_short(JNIEnv *env, jobject thiz, jint written = android_media_AudioTrack_write_byte(env, thiz, (jbyteArray) javaAudioData, offsetInShorts*2, sizeInShorts*2, javaAudioFormat); javaAudioFormat, JNI_TRUE /*blocking write, legacy behavior*/); if (written > 0) { written /= 2; } Loading Loading @@ -890,7 +920,10 @@ 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", "([BIII)I", (void *)android_media_AudioTrack_write_byte}, {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_write_byte}, {"native_write_native_bytes", "(Ljava/lang/Object;IIIIZ)I", (void *)android_media_AudioTrack_write_native_bytes}, {"native_write_short", "([SIII)I", (void *)android_media_AudioTrack_write_short}, {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume}, {"native_get_native_frame_count", Loading
media/java/android/media/AudioTrack.java +103 −2 Original line number Diff line number Diff line Loading @@ -16,8 +16,13 @@ package android.media; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.nio.ByteBuffer; import java.nio.NioUtils; import android.annotation.IntDef; import android.os.Handler; import android.os.Looper; import android.os.Message; Loading Loading @@ -145,6 +150,28 @@ public class AudioTrack private final static String TAG = "android.media.AudioTrack"; /** @hide */ @IntDef({ WRITE_BLOCKING, WRITE_NON_BLOCKING }) @Retention(RetentionPolicy.SOURCE) public @interface WriteMode {} /** * @hide CANDIDATE FOR PUBLIC API * The write mode indicating the write operation will block until all data has been written, * to be used in {@link #write(ByteBuffer, int, int, int)}. */ public final static int WRITE_BLOCKING = 0; /** * @hide CANDIDATE FOR PUBLIC API * The write mode indicating the write operation will return immediately after * queuing as much audio data for playback as possible without blocking, to be used in * {@link #write(ByteBuffer, int, int, int)}. */ public final static int WRITE_NON_BLOCKING = 1; //-------------------------------------------------------------------------- // Member variables //-------------------- Loading Loading @@ -1084,7 +1111,8 @@ public class AudioTrack return ERROR_BAD_VALUE; } int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat); int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat, true /*isBlocking*/); if ((mDataLoadMode == MODE_STATIC) && (mState == STATE_NO_STATIC_DATA) Loading Loading @@ -1140,6 +1168,75 @@ public class AudioTrack } /** * @hide CANDIDATE FOR PUBLIC API * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). * In static buffer mode, copies the data to the buffer starting at its 0 offset, and the write * mode is ignored. * In streaming mode, the blocking behavior will depend on the write mode. * @param audioData the buffer that holds the data to play, starting at the position reported * by <code>audioData.position()</code>. * <BR>Note that this method will not update the position in this buffer, therefore when * writing a loop to write all the data in the buffer, you should increment the * <code>offsetInBytes</code> parameter at each pass by the amount that was previously * written for this buffer. * @param offsetInBytes offset to read from in bytes (note this differs from * <code>audioData.position()</code>). * @param sizeInBytes number of bytes to read (note this differs from * <code>audioData.remaining()</code>). * @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 0 or a positive number of bytes that were written, or * {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION} */ public int write(ByteBuffer audioData, int offsetInBytes, int sizeInBytes, @WriteMode int writeMode) { if (mState == STATE_UNINITIALIZED) { Log.e(TAG, "AudioTrack.write() called in invalid state STATE_UNINITIALIZED"); 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.remaining())) { Log.e(TAG, "AudioTrack.write() called with invalid size/offset values"); return ERROR_BAD_VALUE; } int ret = 0; if (audioData.isDirect()) { ret = native_write_native_bytes(audioData, audioData.position(), offsetInBytes, sizeInBytes, mAudioFormat, writeMode == WRITE_BLOCKING); } else { ret = native_write_byte(NioUtils.unsafeArray(audioData), NioUtils.unsafeArrayOffset(audioData) + audioData.position() + offsetInBytes, sizeInBytes, mAudioFormat, writeMode == WRITE_BLOCKING); } if ((mDataLoadMode == MODE_STATIC) && (mState == STATE_NO_STATIC_DATA) && (ret > 0)) { // benign race with respect to other APIs that read mState mState = STATE_INITIALIZED; } return ret; } /** * Notifies the native resource to reuse the audio data already loaded in the native * layer, that is to rewind to start of buffer. Loading Loading @@ -1339,11 +1436,15 @@ public class AudioTrack private native final void native_flush(); private native final int native_write_byte(byte[] audioData, int offsetInBytes, int sizeInBytes, int format); int offsetInBytes, int sizeInBytes, int format, boolean isBlocking); private native final int native_write_short(short[] audioData, int offsetInShorts, int sizeInShorts, int format); private native final int native_write_native_bytes(Object audioData, int positionInBytes, int offsetInBytes, int sizeInBytes, int format, boolean blocking); private native final int native_reload_static(); private native final int native_get_native_frame_count(); Loading