diff options
Diffstat (limited to 'audio/out/ao_audiotrack.c')
-rw-r--r-- | audio/out/ao_audiotrack.c | 586 |
1 files changed, 353 insertions, 233 deletions
diff --git a/audio/out/ao_audiotrack.c b/audio/out/ao_audiotrack.c index 7a1715d373..940bffbec9 100644 --- a/audio/out/ao_audiotrack.c +++ b/audio/out/ao_audiotrack.c @@ -54,34 +54,32 @@ struct priv { jfloatArray floatarray; jobject bbuf; - int cfg_pcm_float; + bool cfg_pcm_float; int cfg_session_id; - bool needs_timestamp_offset; - int64_t timestamp_offset; - bool thread_terminate; bool thread_created; - pthread_t thread; - pthread_mutex_t lock; - pthread_cond_t wakeup; + mp_thread thread; + mp_mutex lock; + mp_cond wakeup; }; -struct JNIByteBuffer { +static struct JNIByteBuffer { jclass clazz; jmethodID clear; - struct MPJniField mapping[]; -} ByteBuffer = {.mapping = { - #define OFFSET(member) offsetof(struct JNIByteBuffer, member) - {"java/nio/ByteBuffer", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, - {"java/nio/ByteBuffer", "clear", "()Ljava/nio/Buffer;", MP_JNI_METHOD, OFFSET(clear), 1}, +} ByteBuffer; +#define OFFSET(member) offsetof(struct JNIByteBuffer, member) +static const struct MPJniField ByteBuffer_mapping[] = { + {"java/nio/ByteBuffer", NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, + {"clear", "()Ljava/nio/Buffer;", MP_JNI_METHOD, OFFSET(clear), 1}, {0}, - #undef OFFSET -}}; +}; +#undef OFFSET -struct JNIAudioTrack { +static struct JNIAudioTrack { jclass clazz; jmethodID ctor; + jmethodID ctorV21; jmethodID release; jmethodID getState; jmethodID getPlayState; @@ -91,9 +89,9 @@ struct JNIAudioTrack { jmethodID pause; jmethodID write; jmethodID writeFloat; - jmethodID writeV23; jmethodID writeShortV23; jmethodID writeBufferV21; + jmethodID getBufferSizeInFramesV23; jmethodID getPlaybackHeadPosition; jmethodID getTimestamp; jmethodID getLatency; @@ -109,133 +107,234 @@ struct JNIAudioTrack { jint ERROR_INVALID_OPERATION; jint WRITE_BLOCKING; jint WRITE_NON_BLOCKING; - struct MPJniField mapping[]; -} AudioTrack = {.mapping = { - #define OFFSET(member) offsetof(struct JNIAudioTrack, member) - {"android/media/AudioTrack", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, - {"android/media/AudioTrack", "<init>", "(IIIIIII)V", MP_JNI_METHOD, OFFSET(ctor), 1}, - {"android/media/AudioTrack", "release", "()V", MP_JNI_METHOD, OFFSET(release), 1}, - {"android/media/AudioTrack", "getState", "()I", MP_JNI_METHOD, OFFSET(getState), 1}, - {"android/media/AudioTrack", "getPlayState", "()I", MP_JNI_METHOD, OFFSET(getPlayState), 1}, - {"android/media/AudioTrack", "play", "()V", MP_JNI_METHOD, OFFSET(play), 1}, - {"android/media/AudioTrack", "stop", "()V", MP_JNI_METHOD, OFFSET(stop), 1}, - {"android/media/AudioTrack", "flush", "()V", MP_JNI_METHOD, OFFSET(flush), 1}, - {"android/media/AudioTrack", "pause", "()V", MP_JNI_METHOD, OFFSET(pause), 1}, - {"android/media/AudioTrack", "write", "([BII)I", MP_JNI_METHOD, OFFSET(write), 1}, - {"android/media/AudioTrack", "write", "([FIII)I", MP_JNI_METHOD, OFFSET(writeFloat), 1}, - {"android/media/AudioTrack", "write", "([BIII)I", MP_JNI_METHOD, OFFSET(writeV23), 0}, - {"android/media/AudioTrack", "write", "([SIII)I", MP_JNI_METHOD, OFFSET(writeShortV23), 0}, - {"android/media/AudioTrack", "write", "(Ljava/nio/ByteBuffer;II)I", MP_JNI_METHOD, OFFSET(writeBufferV21), 1}, - {"android/media/AudioTrack", "getTimestamp", "(Landroid/media/AudioTimestamp;)Z", MP_JNI_METHOD, OFFSET(getTimestamp), 1}, - {"android/media/AudioTrack", "getPlaybackHeadPosition", "()I", MP_JNI_METHOD, OFFSET(getPlaybackHeadPosition), 1}, - {"android/media/AudioTrack", "getLatency", "()I", MP_JNI_METHOD, OFFSET(getLatency), 1}, - {"android/media/AudioTrack", "getMinBufferSize", "(III)I", MP_JNI_STATIC_METHOD, OFFSET(getMinBufferSize), 1}, - {"android/media/AudioTrack", "getNativeOutputSampleRate", "(I)I", MP_JNI_STATIC_METHOD, OFFSET(getNativeOutputSampleRate), 1}, - {"android/media/AudioTrack", "WRITE_BLOCKING", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(WRITE_BLOCKING), 0}, - {"android/media/AudioTrack", "WRITE_NON_BLOCKING", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(WRITE_NON_BLOCKING), 0}, - {"android/media/AudioTrack", "STATE_INITIALIZED", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(STATE_INITIALIZED), 1}, - {"android/media/AudioTrack", "PLAYSTATE_STOPPED", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(PLAYSTATE_STOPPED), 1}, - {"android/media/AudioTrack", "PLAYSTATE_PAUSED", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(PLAYSTATE_PAUSED), 1}, - {"android/media/AudioTrack", "PLAYSTATE_PLAYING", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(PLAYSTATE_PLAYING), 1}, - {"android/media/AudioTrack", "MODE_STREAM", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(MODE_STREAM), 1}, - {"android/media/AudioTrack", "ERROR", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR), 1}, - {"android/media/AudioTrack", "ERROR_BAD_VALUE", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR_BAD_VALUE), 1}, - {"android/media/AudioTrack", "ERROR_INVALID_OPERATION", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR_INVALID_OPERATION), 1}, +} AudioTrack; +#define OFFSET(member) offsetof(struct JNIAudioTrack, member) +static const struct MPJniField AudioTrack_mapping[] = { + {"android/media/AudioTrack", NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, + {"<init>", "(IIIIIII)V", MP_JNI_METHOD, OFFSET(ctor), 1}, + {"<init>", "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;III)V", MP_JNI_METHOD, OFFSET(ctorV21), 0}, + {"release", "()V", MP_JNI_METHOD, OFFSET(release), 1}, + {"getState", "()I", MP_JNI_METHOD, OFFSET(getState), 1}, + {"getPlayState", "()I", MP_JNI_METHOD, OFFSET(getPlayState), 1}, + {"play", "()V", MP_JNI_METHOD, OFFSET(play), 1}, + {"stop", "()V", MP_JNI_METHOD, OFFSET(stop), 1}, + {"flush", "()V", MP_JNI_METHOD, OFFSET(flush), 1}, + {"pause", "()V", MP_JNI_METHOD, OFFSET(pause), 1}, + {"write", "([BII)I", MP_JNI_METHOD, OFFSET(write), 1}, + {"write", "([FIII)I", MP_JNI_METHOD, OFFSET(writeFloat), 1}, + {"write", "([SIII)I", MP_JNI_METHOD, OFFSET(writeShortV23), 0}, + {"write", "(Ljava/nio/ByteBuffer;II)I", MP_JNI_METHOD, OFFSET(writeBufferV21), 1}, + {"getBufferSizeInFrames", "()I", MP_JNI_METHOD, OFFSET(getBufferSizeInFramesV23), 0}, + {"getTimestamp", "(Landroid/media/AudioTimestamp;)Z", MP_JNI_METHOD, OFFSET(getTimestamp), 1}, + {"getPlaybackHeadPosition", "()I", MP_JNI_METHOD, OFFSET(getPlaybackHeadPosition), 1}, + {"getLatency", "()I", MP_JNI_METHOD, OFFSET(getLatency), 1}, + {"getMinBufferSize", "(III)I", MP_JNI_STATIC_METHOD, OFFSET(getMinBufferSize), 1}, + {"getNativeOutputSampleRate", "(I)I", MP_JNI_STATIC_METHOD, OFFSET(getNativeOutputSampleRate), 1}, + {"WRITE_BLOCKING", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(WRITE_BLOCKING), 0}, + {"WRITE_NON_BLOCKING", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(WRITE_NON_BLOCKING), 0}, + {"STATE_INITIALIZED", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(STATE_INITIALIZED), 1}, + {"PLAYSTATE_STOPPED", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(PLAYSTATE_STOPPED), 1}, + {"PLAYSTATE_PAUSED", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(PLAYSTATE_PAUSED), 1}, + {"PLAYSTATE_PLAYING", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(PLAYSTATE_PLAYING), 1}, + {"MODE_STREAM", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(MODE_STREAM), 1}, + {"ERROR", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR), 1}, + {"ERROR_BAD_VALUE", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR_BAD_VALUE), 1}, + {"ERROR_INVALID_OPERATION", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR_INVALID_OPERATION), 1}, + {0} +}; +#undef OFFSET + +static struct JNIAudioAttributes { + jclass clazz; + jint CONTENT_TYPE_MOVIE; + jint CONTENT_TYPE_MUSIC; + jint USAGE_MEDIA; +} AudioAttributes; +#define OFFSET(member) offsetof(struct JNIAudioAttributes, member) +static const struct MPJniField AudioAttributes_mapping[] = { + {"android/media/AudioAttributes", NULL, MP_JNI_CLASS, OFFSET(clazz), 0}, + {"CONTENT_TYPE_MOVIE", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CONTENT_TYPE_MOVIE), 0}, + {"CONTENT_TYPE_MUSIC", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CONTENT_TYPE_MUSIC), 0}, + {"USAGE_MEDIA", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(USAGE_MEDIA), 0}, + {0} +}; +#undef OFFSET + +static struct JNIAudioAttributesBuilder { + jclass clazz; + jmethodID ctor; + jmethodID setUsage; + jmethodID setContentType; + jmethodID build; +} AudioAttributesBuilder; +#define OFFSET(member) offsetof(struct JNIAudioAttributesBuilder, member) +static const struct MPJniField AudioAttributesBuilder_mapping[] = { + {"android/media/AudioAttributes$Builder", NULL, MP_JNI_CLASS, OFFSET(clazz), 0}, + {"<init>", "()V", MP_JNI_METHOD, OFFSET(ctor), 0}, + {"setUsage", "(I)Landroid/media/AudioAttributes$Builder;", MP_JNI_METHOD, OFFSET(setUsage), 0}, + {"setContentType", "(I)Landroid/media/AudioAttributes$Builder;", MP_JNI_METHOD, OFFSET(setContentType), 0}, + {"build", "()Landroid/media/AudioAttributes;", MP_JNI_METHOD, OFFSET(build), 0}, {0} - #undef OFFSET -}}; +}; +#undef OFFSET -struct JNIAudioFormat { +static struct JNIAudioFormat { jclass clazz; jint ENCODING_PCM_8BIT; jint ENCODING_PCM_16BIT; jint ENCODING_PCM_FLOAT; jint ENCODING_IEC61937; - jint ENCODING_AC3; jint CHANNEL_OUT_MONO; jint CHANNEL_OUT_STEREO; - jint CHANNEL_OUT_FRONT_LEFT; - jint CHANNEL_OUT_FRONT_RIGHT; - jint CHANNEL_OUT_BACK_LEFT; - jint CHANNEL_OUT_BACK_RIGHT; jint CHANNEL_OUT_FRONT_CENTER; - jint CHANNEL_OUT_LOW_FREQUENCY; - jint CHANNEL_OUT_BACK_CENTER; + jint CHANNEL_OUT_QUAD; jint CHANNEL_OUT_5POINT1; - jint CHANNEL_OUT_SIDE_LEFT; - jint CHANNEL_OUT_SIDE_RIGHT; - struct MPJniField mapping[]; -} AudioFormat = {.mapping = { - #define OFFSET(member) offsetof(struct JNIAudioFormat, member) - {"android/media/AudioFormat", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, - {"android/media/AudioFormat", "ENCODING_PCM_8BIT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_PCM_8BIT), 1}, - {"android/media/AudioFormat", "ENCODING_PCM_16BIT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_PCM_16BIT), 1}, - {"android/media/AudioFormat", "ENCODING_PCM_FLOAT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_PCM_FLOAT), 1}, - {"android/media/AudioFormat", "ENCODING_AC3", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_AC3), 0}, - {"android/media/AudioFormat", "ENCODING_IEC61937", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_IEC61937), 0}, - {"android/media/AudioFormat", "CHANNEL_OUT_MONO", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_MONO), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_STEREO", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_STEREO), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_5POINT1", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_5POINT1), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_FRONT_LEFT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_FRONT_LEFT), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_FRONT_RIGHT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_FRONT_RIGHT), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_FRONT_CENTER", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_FRONT_CENTER), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_LOW_FREQUENCY", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_LOW_FREQUENCY), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_BACK_LEFT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_BACK_LEFT), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_BACK_RIGHT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_BACK_RIGHT), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_BACK_CENTER", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_BACK_CENTER), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_SIDE_LEFT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_SIDE_LEFT), 0}, - {"android/media/AudioFormat", "CHANNEL_OUT_SIDE_RIGHT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_SIDE_RIGHT), 0}, + jint CHANNEL_OUT_BACK_CENTER; + jint CHANNEL_OUT_7POINT1_SURROUND; +} AudioFormat; +#define OFFSET(member) offsetof(struct JNIAudioFormat, member) +static const struct MPJniField AudioFormat_mapping[] = { + {"android/media/AudioFormat", NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, + {"ENCODING_PCM_8BIT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_PCM_8BIT), 1}, + {"ENCODING_PCM_16BIT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_PCM_16BIT), 1}, + {"ENCODING_PCM_FLOAT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_PCM_FLOAT), 1}, + {"ENCODING_IEC61937", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_IEC61937), 0}, + {"CHANNEL_OUT_MONO", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_MONO), 1}, + {"CHANNEL_OUT_STEREO", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_STEREO), 1}, + {"CHANNEL_OUT_FRONT_CENTER", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_FRONT_CENTER), 1}, + {"CHANNEL_OUT_QUAD", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_QUAD), 1}, + {"CHANNEL_OUT_5POINT1", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_5POINT1), 1}, + {"CHANNEL_OUT_BACK_CENTER", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_BACK_CENTER), 1}, + {"CHANNEL_OUT_7POINT1_SURROUND", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_7POINT1_SURROUND), 0}, + {0} +}; +#undef OFFSET + +static struct JNIAudioFormatBuilder { + jclass clazz; + jmethodID ctor; + jmethodID setEncoding; + jmethodID setSampleRate; + jmethodID setChannelMask; + jmethodID build; +} AudioFormatBuilder; +#define OFFSET(member) offsetof(struct JNIAudioFormatBuilder, member) +static const struct MPJniField AudioFormatBuilder_mapping[] = { + {"android/media/AudioFormat$Builder", NULL, MP_JNI_CLASS, OFFSET(clazz), 0}, + {"<init>", "()V", MP_JNI_METHOD, OFFSET(ctor), 0}, + {"setEncoding", "(I)Landroid/media/AudioFormat$Builder;", MP_JNI_METHOD, OFFSET(setEncoding), 0}, + {"setSampleRate", "(I)Landroid/media/AudioFormat$Builder;", MP_JNI_METHOD, OFFSET(setSampleRate), 0}, + {"setChannelMask", "(I)Landroid/media/AudioFormat$Builder;", MP_JNI_METHOD, OFFSET(setChannelMask), 0}, + {"build", "()Landroid/media/AudioFormat;", MP_JNI_METHOD, OFFSET(build), 0}, {0} - #undef OFFSET -}}; +}; +#undef OFFSET -struct JNIAudioManager { +static struct JNIAudioManager { jclass clazz; jint ERROR_DEAD_OBJECT; jint STREAM_MUSIC; - struct MPJniField mapping[]; -} AudioManager = {.mapping = { - #define OFFSET(member) offsetof(struct JNIAudioManager, member) - {"android/media/AudioManager", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, - {"android/media/AudioManager", "STREAM_MUSIC", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(STREAM_MUSIC), 1}, - {"android/media/AudioManager", "ERROR_DEAD_OBJECT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR_DEAD_OBJECT), 0}, +} AudioManager; +#define OFFSET(member) offsetof(struct JNIAudioManager, member) +static const struct MPJniField AudioManager_mapping[] = { + {"android/media/AudioManager", NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, + {"STREAM_MUSIC", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(STREAM_MUSIC), 1}, + {"ERROR_DEAD_OBJECT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR_DEAD_OBJECT), 0}, {0} - #undef OFFSET -}}; +}; +#undef OFFSET -struct JNIAudioTimestamp { +static struct JNIAudioTimestamp { jclass clazz; jmethodID ctor; jfieldID framePosition; jfieldID nanoTime; - struct MPJniField mapping[]; -} AudioTimestamp = {.mapping = { - #define OFFSET(member) offsetof(struct JNIAudioTimestamp, member) - {"android/media/AudioTimestamp", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, - {"android/media/AudioTimestamp", "<init>", "()V", MP_JNI_METHOD, OFFSET(ctor), 1}, - {"android/media/AudioTimestamp", "framePosition", "J", MP_JNI_FIELD, OFFSET(framePosition), 1}, - {"android/media/AudioTimestamp", "nanoTime", "J", MP_JNI_FIELD, OFFSET(nanoTime), 1}, +} AudioTimestamp; +#define OFFSET(member) offsetof(struct JNIAudioTimestamp, member) +static const struct MPJniField AudioTimestamp_mapping[] = { + {"android/media/AudioTimestamp", NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, + {"<init>", "()V", MP_JNI_METHOD, OFFSET(ctor), 1}, + {"framePosition", "J", MP_JNI_FIELD, OFFSET(framePosition), 1}, + {"nanoTime", "J", MP_JNI_FIELD, OFFSET(nanoTime), 1}, {0} - #undef OFFSET -}}; +}; +#undef OFFSET + +#define ENTRY(name) { &name, name ## _mapping } +static const struct { + void *fields; + const struct MPJniField *mapping; +} jclass_list[] = { + ENTRY(ByteBuffer), + ENTRY(AudioTrack), + ENTRY(AudioAttributes), + ENTRY(AudioAttributesBuilder), + ENTRY(AudioFormat), + ENTRY(AudioFormatBuilder), + ENTRY(AudioManager), + ENTRY(AudioTimestamp), +}; +#undef ENTRY static int AudioTrack_New(struct ao *ao) { struct priv *p = ao->priv; JNIEnv *env = MP_JNI_GET_ENV(ao); + jobject audiotrack = NULL; - jobject audiotrack = MP_JNI_NEW( - AudioTrack.clazz, - AudioTrack.ctor, - AudioManager.STREAM_MUSIC, - p->samplerate, - p->channel_config, - p->format, - p->size, - AudioTrack.MODE_STREAM, - p->cfg_session_id - ); - if (!audiotrack || MP_JNI_EXCEPTION_LOG(ao) < 0) { + if (AudioTrack.ctorV21) { + MP_VERBOSE(ao, "Using API21 initializer\n"); + jobject tmp = NULL; + + jobject format_builder = MP_JNI_NEW(AudioFormatBuilder.clazz, AudioFormatBuilder.ctor); + MP_JNI_EXCEPTION_LOG(ao); + tmp = MP_JNI_CALL_OBJECT(format_builder, AudioFormatBuilder.setEncoding, p->format); + MP_JNI_LOCAL_FREEP(&tmp); + tmp = MP_JNI_CALL_OBJECT(format_builder, AudioFormatBuilder.setSampleRate, p->samplerate); + MP_JNI_LOCAL_FREEP(&tmp); + tmp = MP_JNI_CALL_OBJECT(format_builder, AudioFormatBuilder.setChannelMask, p->channel_config); + MP_JNI_LOCAL_FREEP(&tmp); + jobject format = MP_JNI_CALL_OBJECT(format_builder, AudioFormatBuilder.build); + MP_JNI_LOCAL_FREEP(&format_builder); + + jobject attr_builder = MP_JNI_NEW(AudioAttributesBuilder.clazz, AudioAttributesBuilder.ctor); + MP_JNI_EXCEPTION_LOG(ao); + tmp = MP_JNI_CALL_OBJECT(attr_builder, AudioAttributesBuilder.setUsage, AudioAttributes.USAGE_MEDIA); + MP_JNI_LOCAL_FREEP(&tmp); + jint content_type = (ao->init_flags & AO_INIT_MEDIA_ROLE_MUSIC) ? + AudioAttributes.CONTENT_TYPE_MUSIC : AudioAttributes.CONTENT_TYPE_MOVIE; + tmp = MP_JNI_CALL_OBJECT(attr_builder, AudioAttributesBuilder.setContentType, content_type); + MP_JNI_LOCAL_FREEP(&tmp); + jobject attr = MP_JNI_CALL_OBJECT(attr_builder, AudioAttributesBuilder.build); + MP_JNI_LOCAL_FREEP(&attr_builder); + + audiotrack = MP_JNI_NEW( + AudioTrack.clazz, + AudioTrack.ctorV21, + attr, + format, + p->size, + AudioTrack.MODE_STREAM, + p->cfg_session_id + ); + + MP_JNI_LOCAL_FREEP(&format); + MP_JNI_LOCAL_FREEP(&attr); + } else { + MP_VERBOSE(ao, "Using legacy initializer\n"); + audiotrack = MP_JNI_NEW( + AudioTrack.clazz, + AudioTrack.ctor, + AudioManager.STREAM_MUSIC, + p->samplerate, + p->channel_config, + p->format, + p->size, + AudioTrack.MODE_STREAM, + p->cfg_session_id + ); + } + if (MP_JNI_EXCEPTION_LOG(ao) < 0 || !audiotrack) { MP_FATAL(ao, "AudioTrack Init failed\n"); return -1; } @@ -243,13 +342,21 @@ static int AudioTrack_New(struct ao *ao) if (MP_JNI_CALL_INT(audiotrack, AudioTrack.getState) != AudioTrack.STATE_INITIALIZED) { MP_JNI_CALL_VOID(audiotrack, AudioTrack.release); MP_JNI_EXCEPTION_LOG(ao); - (*env)->DeleteLocalRef(env, audiotrack); + MP_JNI_LOCAL_FREEP(&audiotrack); MP_ERR(ao, "AudioTrack.getState failed\n"); return -1; } + if (AudioTrack.getBufferSizeInFramesV23) { + int bufferSize = MP_JNI_CALL_INT(audiotrack, AudioTrack.getBufferSizeInFramesV23); + if (bufferSize > 0) { + MP_VERBOSE(ao, "AudioTrack.getBufferSizeInFrames = %d\n", bufferSize); + ao->device_buffer = bufferSize; + } + } + p->audiotrack = (*env)->NewGlobalRef(env, audiotrack); - (*env)->DeleteLocalRef(env, audiotrack); + MP_JNI_LOCAL_FREEP(&audiotrack); if (!p->audiotrack) return -1; @@ -263,8 +370,7 @@ static int AudioTrack_Recreate(struct ao *ao) MP_JNI_CALL_VOID(p->audiotrack, AudioTrack.release); MP_JNI_EXCEPTION_LOG(ao); - (*env)->DeleteGlobalRef(env, p->audiotrack); - p->audiotrack = NULL; + MP_JNI_GLOBAL_FREEP(&p->audiotrack); return AudioTrack_New(ao); } @@ -275,26 +381,26 @@ static uint32_t AudioTrack_getPlaybackHeadPosition(struct ao *ao) return 0; JNIEnv *env = MP_JNI_GET_ENV(ao); uint32_t pos = 0; - int64_t now = mp_raw_time_us(); + int64_t now = mp_raw_time_ns(); int state = MP_JNI_CALL_INT(p->audiotrack, AudioTrack.getPlayState); int stable_count = 20; - int64_t wait = p->timestamp_stable < stable_count ? 50000 : 3000000; + int64_t wait = p->timestamp_stable < stable_count ? 50000000 : 3000000000; if (state == AudioTrack.PLAYSTATE_PLAYING && p->format != AudioFormat.ENCODING_IEC61937 && (p->timestamp_fetched == 0 || now - p->timestamp_fetched >= wait)) { if (!p->timestamp_fetched) p->timestamp_stable = 0; - int64_t utime1 = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime) / 1000; + int64_t time1 = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime); if (MP_JNI_CALL_BOOL(p->audiotrack, AudioTrack.getTimestamp, p->timestamp)) { p->timestamp_set = true; p->timestamp_fetched = now; if (p->timestamp_stable < stable_count) { uint32_t fpos = 0xFFFFFFFFL & MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.framePosition); - int64_t utime2 = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime) / 1000; - //MP_VERBOSE(ao, "getTimestamp: fpos= %u / time= %"PRId64" / now= %"PRId64" / stable= %d\n", fpos, utime2, now, p->timestamp_stable); - if (utime1 != utime2 && utime2 != 0 && fpos != 0) { + int64_t time2 = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime); + //MP_VERBOSE(ao, "getTimestamp: fpos= %u / time= %"PRId64" / now= %"PRId64" / stable= %d\n", fpos, time2, now, p->timestamp_stable); + if (time1 != time2 && time2 != 0 && fpos != 0) { p->timestamp_stable++; } } @@ -307,19 +413,14 @@ static uint32_t AudioTrack_getPlaybackHeadPosition(struct ao *ao) if (p->timestamp_set) { pos = 0xFFFFFFFFL & MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.framePosition); uint32_t fpos = pos; - int64_t utime = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime) / 1000; - if (utime == 0) + int64_t time = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime); + if (time == 0) fpos = pos = 0; - if (p->needs_timestamp_offset) { - if (utime != 0 && !p->timestamp_offset) - p->timestamp_offset = now - utime; - utime += p->timestamp_offset; - } - if (fpos != 0 && utime != 0 && state == AudioTrack.PLAYSTATE_PLAYING) { - double diff = (double)(now - utime) / 1e6; + if (fpos != 0 && time != 0 && state == AudioTrack.PLAYSTATE_PLAYING) { + double diff = (double)(now - time) / 1e9; pos += diff * ao->samplerate; } - //MP_VERBOSE(ao, "position = %u via getTimestamp (state = %d / fpos= %u / time= %"PRId64")\n", pos, state, fpos, utime); + //MP_VERBOSE(ao, "position = %u via getTimestamp (state = %d / fpos= %u / time= %"PRId64")\n", pos, state, fpos, time); } else { pos = 0xFFFFFFFFL & MP_JNI_CALL_INT(p->audiotrack, AudioTrack.getPlaybackHeadPosition); //MP_VERBOSE(ao, "playbackHeadPosition = %u (reset_pending=%d)\n", pos, p->reset_pending); @@ -372,12 +473,12 @@ static double AudioTrack_getLatency(struct ao *ao) if (!p->timestamp_set && p->format != AudioFormat.ENCODING_IEC61937) delay += (double)MP_JNI_CALL_INT(p->audiotrack, AudioTrack.getLatency)/1000.0; - if (delay > 1.0) { + if (delay > 2.0) { //MP_WARN(ao, "getLatency: written=%u playhead=%u diff=%u delay=%f\n", p->written_frames, playhead, diff, delay); p->timestamp_fetched = 0; return 0; } - return MPCLAMP(delay, 0.0, 1.0); + return MPCLAMP(delay, 0.0, 2.0); } static int AudioTrack_write(struct ao *ao, int len) @@ -396,6 +497,14 @@ static int AudioTrack_write(struct ao *ao, int len) if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; if (ret > 0) ret *= 2; + } else if (AudioTrack.writeBufferV21) { + // reset positions for reading + jobject bbuf = MP_JNI_CALL_OBJECT(p->bbuf, ByteBuffer.clear); + if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; + MP_JNI_LOCAL_FREEP(&bbuf); + ret = MP_JNI_CALL_INT(p->audiotrack, AudioTrack.writeBufferV21, p->bbuf, len, AudioTrack.WRITE_BLOCKING); + if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; + } else if (p->format == AudioFormat.ENCODING_PCM_FLOAT) { (*env)->SetFloatArrayRegion(env, p->floatarray, 0, len / sizeof(float), buf); if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; @@ -403,13 +512,6 @@ static int AudioTrack_write(struct ao *ao, int len) if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; if (ret > 0) ret *= sizeof(float); - } else if (AudioTrack.writeBufferV21) { - jobject bbuf = MP_JNI_CALL_OBJECT(p->bbuf, ByteBuffer.clear); - if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; - (*env)->DeleteLocalRef(env, bbuf); - ret = MP_JNI_CALL_INT(p->audiotrack, AudioTrack.writeBufferV21, p->bbuf, len, AudioTrack.WRITE_BLOCKING); - if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; - } else { (*env)->SetByteArrayRegion(env, p->bytearray, 0, len, buf); if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; @@ -423,35 +525,35 @@ static int AudioTrack_write(struct ao *ao, int len) static void uninit_jni(struct ao *ao) { JNIEnv *env = MP_JNI_GET_ENV(ao); - mp_jni_reset_jfields(env, &AudioTrack, AudioTrack.mapping, 1, ao->log); - mp_jni_reset_jfields(env, &AudioTimestamp, AudioTimestamp.mapping, 1, ao->log); - mp_jni_reset_jfields(env, &AudioManager, AudioManager.mapping, 1, ao->log); - mp_jni_reset_jfields(env, &AudioFormat, AudioFormat.mapping, 1, ao->log); - mp_jni_reset_jfields(env, &ByteBuffer, ByteBuffer.mapping, 1, ao->log); + for (int i = 0; i < MP_ARRAY_SIZE(jclass_list); i++) { + mp_jni_reset_jfields(env, jclass_list[i].fields, + jclass_list[i].mapping, 1, ao->log); + } } static int init_jni(struct ao *ao) { JNIEnv *env = MP_JNI_GET_ENV(ao); - if (mp_jni_init_jfields(env, &AudioTrack, AudioTrack.mapping, 1, ao->log) < 0 || - mp_jni_init_jfields(env, &ByteBuffer, ByteBuffer.mapping, 1, ao->log) < 0 || - mp_jni_init_jfields(env, &AudioTimestamp, AudioTimestamp.mapping, 1, ao->log) < 0 || - mp_jni_init_jfields(env, &AudioManager, AudioManager.mapping, 1, ao->log) < 0 || - mp_jni_init_jfields(env, &AudioFormat, AudioFormat.mapping, 1, ao->log) < 0) { - uninit_jni(ao); - return -1; + for (int i = 0; i < MP_ARRAY_SIZE(jclass_list); i++) { + if (mp_jni_init_jfields(env, jclass_list[i].fields, + jclass_list[i].mapping, 1, ao->log) < 0) { + goto error; + } } - return 0; + +error: + uninit_jni(ao); + return -1; } -static void *playthread(void *arg) +static MP_THREAD_VOID ao_thread(void *arg) { struct ao *ao = arg; struct priv *p = ao->priv; JNIEnv *env = MP_JNI_GET_ENV(ao); - mpthread_set_name("audiotrack"); - pthread_mutex_lock(&p->lock); + mp_thread_set_name("ao/audiotrack"); + mp_mutex_lock(&p->lock); while (!p->thread_terminate) { int state = AudioTrack.PLAYSTATE_PAUSED; if (p->audiotrack) { @@ -459,12 +561,11 @@ static void *playthread(void *arg) } if (state == AudioTrack.PLAYSTATE_PLAYING) { int read_samples = p->chunksize / ao->sstride; - int64_t ts = mp_time_us(); - ts += (read_samples / (double)(ao->samplerate)) * 1e6; - ts += AudioTrack_getLatency(ao) * 1e6; - int samples = ao_read_data(ao, &p->chunk, read_samples, ts); - int write_samples = read_samples; - int ret = AudioTrack_write(ao, write_samples * ao->sstride); + int64_t ts = mp_time_ns(); + ts += MP_TIME_S_TO_NS(read_samples / (double)(ao->samplerate)); + ts += MP_TIME_S_TO_NS(AudioTrack_getLatency(ao)); + int samples = ao_read_data(ao, &p->chunk, read_samples, ts, NULL, false, false); + int ret = AudioTrack_write(ao, samples * ao->sstride); if (ret >= 0) { p->written_frames += ret / ao->sstride; } else if (ret == AudioManager.ERROR_DEAD_OBJECT) { @@ -476,12 +577,11 @@ static void *playthread(void *arg) MP_ERR(ao, "AudioTrack.write failed with %d\n", ret); } } else { - struct timespec wait = mp_rel_time_to_timespec(0.300); - pthread_cond_timedwait(&p->wakeup, &p->lock, &wait); + mp_cond_timedwait(&p->wakeup, &p->lock, MP_TIME_MS_TO_NS(300)); } } - pthread_mutex_unlock(&p->lock); - return NULL; + mp_mutex_unlock(&p->lock); + MP_THREAD_RETURN(); } static void uninit(struct ao *ao) @@ -495,53 +595,32 @@ static void uninit(struct ao *ao) MP_JNI_EXCEPTION_LOG(ao); } - pthread_mutex_lock(&p->lock); + mp_mutex_lock(&p->lock); p->thread_terminate = true; - pthread_cond_signal(&p->wakeup); - pthread_mutex_unlock(&p->lock); + mp_cond_signal(&p->wakeup); + mp_mutex_unlock(&p->lock); if (p->thread_created) - pthread_join(p->thread, NULL); + mp_thread_join(p->thread); if (p->audiotrack) { MP_JNI_CALL_VOID(p->audiotrack, AudioTrack.release); MP_JNI_EXCEPTION_LOG(ao); - (*env)->DeleteGlobalRef(env, p->audiotrack); - p->audiotrack = NULL; + MP_JNI_GLOBAL_FREEP(&p->audiotrack); } - if (p->bytearray) { - (*env)->DeleteGlobalRef(env, p->bytearray); - p->bytearray = NULL; - } + MP_JNI_GLOBAL_FREEP(&p->bytearray); - if (p->shortarray) { - (*env)->DeleteGlobalRef(env, p->shortarray); - p->shortarray = NULL; - } - - if (p->floatarray) { - (*env)->DeleteGlobalRef(env, p->floatarray); - p->floatarray = NULL; - } + MP_JNI_GLOBAL_FREEP(&p->shortarray); - if (p->bbuf) { - (*env)->DeleteGlobalRef(env, p->bbuf); - p->bbuf = NULL; - } + MP_JNI_GLOBAL_FREEP(&p->floatarray); - if (p->timestamp) { - (*env)->DeleteGlobalRef(env, p->timestamp); - p->timestamp = NULL; - } + MP_JNI_GLOBAL_FREEP(&p->bbuf); - if (p->chunk) { - free(p->chunk); - p->chunk = NULL; - } + MP_JNI_GLOBAL_FREEP(&p->timestamp); - pthread_cond_destroy(&p->wakeup); - pthread_mutex_destroy(&p->lock); + mp_cond_destroy(&p->wakeup); + mp_mutex_destroy(&p->lock); uninit_jni(ao); } @@ -553,17 +632,21 @@ static int init(struct ao *ao) if (!env) return -1; - pthread_mutex_init(&p->lock, NULL); - pthread_cond_init(&p->wakeup, NULL); + mp_mutex_init(&p->lock); + mp_cond_init(&p->wakeup); if (init_jni(ao) < 0) return -1; if (af_fmt_is_spdif(ao->format)) { p->format = AudioFormat.ENCODING_IEC61937; + if (!p->format || !AudioTrack.writeShortV23) { + MP_ERR(ao, "spdif passthrough not supported by API\n"); + return -1; + } } else if (ao->format == AF_FORMAT_U8) { p->format = AudioFormat.ENCODING_PCM_8BIT; - } else if (p->cfg_pcm_float && (ao->format == AF_FORMAT_FLOAT || ao->format == AF_FORMAT_FLOATP)) { + } else if (p->cfg_pcm_float && af_fmt_is_float(ao->format)) { ao->format = AF_FORMAT_FLOAT; p->format = AudioFormat.ENCODING_PCM_FLOAT; } else { @@ -578,22 +661,48 @@ static int init(struct ao *ao) AudioManager.STREAM_MUSIC ); if (MP_JNI_EXCEPTION_LOG(ao) == 0) { - ao->samplerate = samplerate; MP_VERBOSE(ao, "AudioTrack.nativeOutputSampleRate = %d\n", samplerate); + ao->samplerate = MPMIN(samplerate, ao->samplerate); } } p->samplerate = ao->samplerate; + /* https://developer.android.com/reference/android/media/AudioFormat#channelPositionMask */ + static const struct mp_chmap layouts[] = { + {0}, // empty + MP_CHMAP_INIT_MONO, // mono + MP_CHMAP_INIT_STEREO, // stereo + MP_CHMAP3(FL, FR, FC), // 3.0 + MP_CHMAP4(FL, FR, BL, BR), // quad + MP_CHMAP5(FL, FR, FC, BL, BR), // 5.0 + MP_CHMAP6(FL, FR, FC, LFE, BL, BR), // 5.1 + MP_CHMAP7(FL, FR, FC, LFE, BL, BR, BC), // 6.1 + MP_CHMAP8(FL, FR, FC, LFE, BL, BR, SL, SR), // 7.1 + }; + const jint layout_map[] = { + 0, + AudioFormat.CHANNEL_OUT_MONO, + AudioFormat.CHANNEL_OUT_STEREO, + AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER, + AudioFormat.CHANNEL_OUT_QUAD, + AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER, + AudioFormat.CHANNEL_OUT_5POINT1, + AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER, + AudioFormat.CHANNEL_OUT_7POINT1_SURROUND, + }; + static_assert(MP_ARRAY_SIZE(layout_map) == MP_ARRAY_SIZE(layouts), ""); if (p->format == AudioFormat.ENCODING_IEC61937) { p->channel_config = AudioFormat.CHANNEL_OUT_STEREO; - } else if (ao->channels.num == 1) { - p->channel_config = AudioFormat.CHANNEL_OUT_MONO; - } else if (ao->channels.num == 6) { - p->channel_config = AudioFormat.CHANNEL_OUT_5POINT1; - ao->channels = (struct mp_chmap)MP_CHMAP6(FL, FR, FC, LFE, BL, BR); } else { - p->channel_config = AudioFormat.CHANNEL_OUT_STEREO; - ao->channels = (struct mp_chmap)MP_CHMAP_INIT_STEREO; + struct mp_chmap_sel sel = {0}; + for (int i = 0; i < MP_ARRAY_SIZE(layouts); i++) { + if (layout_map[i]) + mp_chmap_sel_add_map(&sel, &layouts[i]); + } + if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) + goto error; + p->channel_config = layout_map[ao->channels.num]; + assert(p->channel_config); } jint buffer_size = MP_JNI_CALL_STATIC_INT( @@ -603,43 +712,52 @@ static int init(struct ao *ao) p->channel_config, p->format ); - if (buffer_size <= 0 || MP_JNI_EXCEPTION_LOG(ao) < 0) { + if (MP_JNI_EXCEPTION_LOG(ao) < 0 || buffer_size <= 0) { MP_FATAL(ao, "AudioTrack.getMinBufferSize returned an invalid size: %d", buffer_size); return -1; } - p->chunksize = buffer_size; - p->chunk = malloc(buffer_size); - int min = 0.200 * p->samplerate * af_fmt_to_bytes(ao->format); - int max = min * 3 / 2; + // Choose double of the minimum buffer size suggested by the driver, but not + // less than 75ms or more than 150ms. + const int bps = af_fmt_to_bytes(ao->format); + int min = 0.075 * p->samplerate * bps * ao->channels.num; + int max = min * 2; + min = MP_ALIGN_UP(min, bps); + max = MP_ALIGN_UP(max, bps); p->size = MPCLAMP(buffer_size * 2, min, max); MP_VERBOSE(ao, "Setting bufferSize = %d (driver=%d, min=%d, max=%d)\n", p->size, buffer_size, min, max); - ao->device_buffer = p->size / af_fmt_to_bytes(ao->format); + assert(p->size % bps == 0); + ao->device_buffer = p->size / bps; + + p->chunksize = p->size; + p->chunk = talloc_size(ao, p->size); jobject timestamp = MP_JNI_NEW(AudioTimestamp.clazz, AudioTimestamp.ctor); - if (!timestamp || MP_JNI_EXCEPTION_LOG(ao) < 0) { + if (MP_JNI_EXCEPTION_LOG(ao) < 0 || !timestamp) { MP_FATAL(ao, "AudioTimestamp could not be created\n"); return -1; } p->timestamp = (*env)->NewGlobalRef(env, timestamp); - (*env)->DeleteLocalRef(env, timestamp); + MP_JNI_LOCAL_FREEP(×tamp); + // decide and create buffer of right type if (p->format == AudioFormat.ENCODING_IEC61937) { jshortArray shortarray = (*env)->NewShortArray(env, p->chunksize / 2); p->shortarray = (*env)->NewGlobalRef(env, shortarray); - (*env)->DeleteLocalRef(env, shortarray); - } else if (p->format == AudioFormat.ENCODING_PCM_FLOAT) { - jfloatArray floatarray = (*env)->NewFloatArray(env, p->chunksize / sizeof(float)); - p->floatarray = (*env)->NewGlobalRef(env, floatarray); - (*env)->DeleteLocalRef(env, floatarray); + MP_JNI_LOCAL_FREEP(&shortarray); } else if (AudioTrack.writeBufferV21) { + MP_VERBOSE(ao, "Using NIO ByteBuffer\n"); jobject bbuf = (*env)->NewDirectByteBuffer(env, p->chunk, p->chunksize); p->bbuf = (*env)->NewGlobalRef(env, bbuf); - (*env)->DeleteLocalRef(env, bbuf); + MP_JNI_LOCAL_FREEP(&bbuf); + } else if (p->format == AudioFormat.ENCODING_PCM_FLOAT) { + jfloatArray floatarray = (*env)->NewFloatArray(env, p->chunksize / sizeof(float)); + p->floatarray = (*env)->NewGlobalRef(env, floatarray); + MP_JNI_LOCAL_FREEP(&floatarray); } else { jbyteArray bytearray = (*env)->NewByteArray(env, p->chunksize); p->bytearray = (*env)->NewGlobalRef(env, bytearray); - (*env)->DeleteLocalRef(env, bytearray); + MP_JNI_LOCAL_FREEP(&bytearray); } /* create AudioTrack object */ @@ -648,7 +766,7 @@ static int init(struct ao *ao) goto error; } - if (pthread_create(&p-> |