Loading core/jni/android_media_AudioTrack.cpp +45 −3 Original line number Diff line number Diff line Loading @@ -526,12 +526,15 @@ jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* da switch (format) { default: // TODO Currently the only possible values for format are AUDIO_FORMAT_PCM_16_BIT // and AUDIO_FORMAT_PCM_8_BIT, due to the limited set of values for audioFormat. // TODO Currently the only possible values for format are AUDIO_FORMAT_PCM_16_BIT, // AUDIO_FORMAT_PCM_8_BIT, and AUDIO_FORMAT_PCM_FLOAT, // due to the limited set of values for audioFormat. // The next section of the switch will probably work for more formats, but it has only // been tested for AUDIO_FORMAT_PCM_16_BIT, so that's why the "default" case fails. // been tested for AUDIO_FORMAT_PCM_16_BIT and AUDIO_FORMAT_PCM_FLOAT, // so that's why the "default" case fails. break; case AUDIO_FORMAT_PCM_FLOAT: case AUDIO_FORMAT_PCM_16_BIT: { // writing to shared memory, check for capacity if ((size_t)sizeInBytes > track->sharedBuffer()->size()) { Loading Loading @@ -678,6 +681,44 @@ static jint android_media_AudioTrack_write_short(JNIEnv *env, jobject thiz, } // ---------------------------------------------------------------------------- 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 @@ -963,6 +1004,7 @@ static JNINativeMethod gMethods[] = { "(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_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 +98 −6 Original line number Diff line number Diff line Loading @@ -72,6 +72,7 @@ import com.android.internal.app.IAppOpsService; * * AudioTrack is not final and thus permits subclasses, but such use is not recommended. */ // add {@link #write(float[], int, int)} when @hide removed public class AudioTrack { //--------------------------------------------------------- Loading Loading @@ -245,6 +246,7 @@ public class AudioTrack * @see AudioFormat#ENCODING_PCM_8BIT * @see AudioFormat#ENCODING_PCM_16BIT */ // add @see AudioFormat#ENCODING_PCM_FLOAT when @hide removed private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; /** * Audio session ID Loading Loading @@ -300,6 +302,7 @@ public class AudioTrack * @param mode streaming or static buffer. See {@link #MODE_STATIC} and {@link #MODE_STREAM} * @throws java.lang.IllegalArgumentException */ // add {@link AudioFormat#ENCODING_PCM_FLOAT} to audioFormat section above, when @hide removed public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode) throws IllegalArgumentException { Loading Loading @@ -341,6 +344,7 @@ public class AudioTrack * @param sessionId Id of audio session the AudioTrack must be attached to * @throws java.lang.IllegalArgumentException */ // add {@link AudioFormat#ENCODING_PCM_FLOAT} to audioFormat section above, when @hide removed public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode, int sessionId) throws IllegalArgumentException { Loading Loading @@ -459,11 +463,14 @@ public class AudioTrack break; case AudioFormat.ENCODING_PCM_16BIT: case AudioFormat.ENCODING_PCM_8BIT: case AudioFormat.ENCODING_PCM_FLOAT: mAudioFormat = audioFormat; break; default: throw new IllegalArgumentException("Unsupported sample encoding." + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT."); + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT" // + " or ENCODING_PCM_FLOAT" when @hide removed + "."); } //-------------- Loading Loading @@ -728,6 +735,7 @@ public class AudioTrack * or {@link #ERROR} if unable to query for output properties, * or the minimum buffer size expressed in bytes. */ // add {@link AudioFormat#ENCODING_PCM_FLOAT} to audioFormat section above, when @hide removed static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) { int channelCount = 0; switch(channelConfig) { Loading @@ -750,7 +758,8 @@ public class AudioTrack } if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT) && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) { && (audioFormat != AudioFormat.ENCODING_PCM_8BIT) && (audioFormat != AudioFormat.ENCODING_PCM_FLOAT)) { loge("getMinBufferSize(): Invalid audio format."); return ERROR_BAD_VALUE; } Loading Loading @@ -1150,7 +1159,7 @@ public class AudioTrack public int write(byte[] audioData, int offsetInBytes, int sizeInBytes) { if (mState == STATE_UNINITIALIZED) { if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { return ERROR_INVALID_OPERATION; } Loading Loading @@ -1194,7 +1203,7 @@ public class AudioTrack public int write(short[] audioData, int offsetInShorts, int sizeInShorts) { if (mState == STATE_UNINITIALIZED) { if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { return ERROR_INVALID_OPERATION; } Loading @@ -1217,6 +1226,80 @@ 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). * 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. * <p> * 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. * <p> * @param audioData the array that holds the data to play. * The implementation does not clip for sample values within the nominal range * [-1.0f, 1.0f], provided that all gains in the audio pipeline are * less than or equal to unity (1.0f), and in the absence of post-processing effects * that could add energy, such as reverb. For the convenience of applications * that compute samples using filters with non-unity gain, * sample values +3 dB beyond the nominal range are permitted. * However such values may eventually be limited or clipped, depending on various gains * and later processing in the audio path. Therefore applications are encouraged * to provide samples values within the nominal range. * @param offsetInFloats the offset, expressed as a number of floats, * in audioData where the data to play starts. * @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 * 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 floats 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. * @hide candidate for public API */ public int write(float[] audioData, int offsetInFloats, int sizeInFloats, @WriteMode int writeMode) { if (mState == STATE_UNINITIALIZED) { Log.e(TAG, "AudioTrack.write() called in invalid state STATE_UNINITIALIZED"); return ERROR_INVALID_OPERATION; } if (mAudioFormat != AudioFormat.ENCODING_PCM_FLOAT) { Log.e(TAG, "AudioTrack.write(float[] ...) requires format 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) || (offsetInFloats < 0 ) || (sizeInFloats < 0) || (offsetInFloats + sizeInFloats < 0) // detect integer overflow || (offsetInFloats + sizeInFloats > audioData.length)) { Log.e(TAG, "AudioTrack.write() called with invalid array, offset, or size"); return ERROR_BAD_VALUE; } int ret = native_write_float(audioData, offsetInFloats, sizeInFloats, 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; } /** * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). Loading Loading @@ -1247,6 +1330,11 @@ public class AudioTrack return ERROR_INVALID_OPERATION; } if (mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { Log.e(TAG, "AudioTrack.write(ByteBuffer ...) not yet supported for 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; Loading Loading @@ -1487,6 +1575,10 @@ public class AudioTrack private native final int native_write_short(short[] audioData, int offsetInShorts, int sizeInShorts, int format); private native final int native_write_float(float[] audioData, int offsetInFloats, int sizeInFloats, int format, boolean isBlocking); private native final int native_write_native_bytes(Object audioData, int positionInBytes, int sizeInBytes, int format, boolean blocking); Loading Loading
core/jni/android_media_AudioTrack.cpp +45 −3 Original line number Diff line number Diff line Loading @@ -526,12 +526,15 @@ jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* da switch (format) { default: // TODO Currently the only possible values for format are AUDIO_FORMAT_PCM_16_BIT // and AUDIO_FORMAT_PCM_8_BIT, due to the limited set of values for audioFormat. // TODO Currently the only possible values for format are AUDIO_FORMAT_PCM_16_BIT, // AUDIO_FORMAT_PCM_8_BIT, and AUDIO_FORMAT_PCM_FLOAT, // due to the limited set of values for audioFormat. // The next section of the switch will probably work for more formats, but it has only // been tested for AUDIO_FORMAT_PCM_16_BIT, so that's why the "default" case fails. // been tested for AUDIO_FORMAT_PCM_16_BIT and AUDIO_FORMAT_PCM_FLOAT, // so that's why the "default" case fails. break; case AUDIO_FORMAT_PCM_FLOAT: case AUDIO_FORMAT_PCM_16_BIT: { // writing to shared memory, check for capacity if ((size_t)sizeInBytes > track->sharedBuffer()->size()) { Loading Loading @@ -678,6 +681,44 @@ static jint android_media_AudioTrack_write_short(JNIEnv *env, jobject thiz, } // ---------------------------------------------------------------------------- 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 @@ -963,6 +1004,7 @@ static JNINativeMethod gMethods[] = { "(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_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 +98 −6 Original line number Diff line number Diff line Loading @@ -72,6 +72,7 @@ import com.android.internal.app.IAppOpsService; * * AudioTrack is not final and thus permits subclasses, but such use is not recommended. */ // add {@link #write(float[], int, int)} when @hide removed public class AudioTrack { //--------------------------------------------------------- Loading Loading @@ -245,6 +246,7 @@ public class AudioTrack * @see AudioFormat#ENCODING_PCM_8BIT * @see AudioFormat#ENCODING_PCM_16BIT */ // add @see AudioFormat#ENCODING_PCM_FLOAT when @hide removed private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; /** * Audio session ID Loading Loading @@ -300,6 +302,7 @@ public class AudioTrack * @param mode streaming or static buffer. See {@link #MODE_STATIC} and {@link #MODE_STREAM} * @throws java.lang.IllegalArgumentException */ // add {@link AudioFormat#ENCODING_PCM_FLOAT} to audioFormat section above, when @hide removed public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode) throws IllegalArgumentException { Loading Loading @@ -341,6 +344,7 @@ public class AudioTrack * @param sessionId Id of audio session the AudioTrack must be attached to * @throws java.lang.IllegalArgumentException */ // add {@link AudioFormat#ENCODING_PCM_FLOAT} to audioFormat section above, when @hide removed public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode, int sessionId) throws IllegalArgumentException { Loading Loading @@ -459,11 +463,14 @@ public class AudioTrack break; case AudioFormat.ENCODING_PCM_16BIT: case AudioFormat.ENCODING_PCM_8BIT: case AudioFormat.ENCODING_PCM_FLOAT: mAudioFormat = audioFormat; break; default: throw new IllegalArgumentException("Unsupported sample encoding." + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT."); + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT" // + " or ENCODING_PCM_FLOAT" when @hide removed + "."); } //-------------- Loading Loading @@ -728,6 +735,7 @@ public class AudioTrack * or {@link #ERROR} if unable to query for output properties, * or the minimum buffer size expressed in bytes. */ // add {@link AudioFormat#ENCODING_PCM_FLOAT} to audioFormat section above, when @hide removed static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) { int channelCount = 0; switch(channelConfig) { Loading @@ -750,7 +758,8 @@ public class AudioTrack } if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT) && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) { && (audioFormat != AudioFormat.ENCODING_PCM_8BIT) && (audioFormat != AudioFormat.ENCODING_PCM_FLOAT)) { loge("getMinBufferSize(): Invalid audio format."); return ERROR_BAD_VALUE; } Loading Loading @@ -1150,7 +1159,7 @@ public class AudioTrack public int write(byte[] audioData, int offsetInBytes, int sizeInBytes) { if (mState == STATE_UNINITIALIZED) { if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { return ERROR_INVALID_OPERATION; } Loading Loading @@ -1194,7 +1203,7 @@ public class AudioTrack public int write(short[] audioData, int offsetInShorts, int sizeInShorts) { if (mState == STATE_UNINITIALIZED) { if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { return ERROR_INVALID_OPERATION; } Loading @@ -1217,6 +1226,80 @@ 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). * 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. * <p> * 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. * <p> * @param audioData the array that holds the data to play. * The implementation does not clip for sample values within the nominal range * [-1.0f, 1.0f], provided that all gains in the audio pipeline are * less than or equal to unity (1.0f), and in the absence of post-processing effects * that could add energy, such as reverb. For the convenience of applications * that compute samples using filters with non-unity gain, * sample values +3 dB beyond the nominal range are permitted. * However such values may eventually be limited or clipped, depending on various gains * and later processing in the audio path. Therefore applications are encouraged * to provide samples values within the nominal range. * @param offsetInFloats the offset, expressed as a number of floats, * in audioData where the data to play starts. * @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 * 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 floats 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. * @hide candidate for public API */ public int write(float[] audioData, int offsetInFloats, int sizeInFloats, @WriteMode int writeMode) { if (mState == STATE_UNINITIALIZED) { Log.e(TAG, "AudioTrack.write() called in invalid state STATE_UNINITIALIZED"); return ERROR_INVALID_OPERATION; } if (mAudioFormat != AudioFormat.ENCODING_PCM_FLOAT) { Log.e(TAG, "AudioTrack.write(float[] ...) requires format 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) || (offsetInFloats < 0 ) || (sizeInFloats < 0) || (offsetInFloats + sizeInFloats < 0) // detect integer overflow || (offsetInFloats + sizeInFloats > audioData.length)) { Log.e(TAG, "AudioTrack.write() called with invalid array, offset, or size"); return ERROR_BAD_VALUE; } int ret = native_write_float(audioData, offsetInFloats, sizeInFloats, 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; } /** * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). Loading Loading @@ -1247,6 +1330,11 @@ public class AudioTrack return ERROR_INVALID_OPERATION; } if (mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { Log.e(TAG, "AudioTrack.write(ByteBuffer ...) not yet supported for 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; Loading Loading @@ -1487,6 +1575,10 @@ public class AudioTrack private native final int native_write_short(short[] audioData, int offsetInShorts, int sizeInShorts, int format); private native final int native_write_float(float[] audioData, int offsetInFloats, int sizeInFloats, int format, boolean isBlocking); private native final int native_write_native_bytes(Object audioData, int positionInBytes, int sizeInBytes, int format, boolean blocking); Loading