From 9e706927de9c2866f54381202103d8088706b8af Mon Sep 17 00:00:00 2001 From: Julian Winkler Date: Sun, 18 Jun 2023 11:16:23 +0200 Subject: [PATCH] implement android.media.SoundPool using GtkMediaStream --- meson.build | 1 + .../audio/android_media_SoundPool.c | 32 ++++++++++++++++ .../android_media_SoundPool.h | 37 +++++++++++++++++++ .../content/res/AssetFileDescriptor.java | 1 + .../android/content/res/AssetManager.java | 18 +++++---- src/api-impl/android/media/SoundPool.java | 32 +++++++++++++++- 6 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 src/api-impl-jni/audio/android_media_SoundPool.c create mode 100644 src/api-impl-jni/generated_headers/android_media_SoundPool.h diff --git a/meson.build b/meson.build index 791f83bd..14cbc54a 100644 --- a/meson.build +++ b/meson.build @@ -56,6 +56,7 @@ libtranslationlayer_so = shared_library('translation_layer_main', [ 'src/api-impl-jni/drawables/ninepatch.c', 'src/api-impl-jni/android_content_res_AssetManager.c', 'src/api-impl-jni/audio/android_media_AudioTrack.c', + 'src/api-impl-jni/audio/android_media_SoundPool.c', 'src/api-impl-jni/widgets/android_widget_RelativeLayout.c', 'src/api-impl-jni/widgets/android_widget_ScrollView.c', 'src/api-impl-jni/widgets/android_opengl_GLSurfaceView.c', diff --git a/src/api-impl-jni/audio/android_media_SoundPool.c b/src/api-impl-jni/audio/android_media_SoundPool.c new file mode 100644 index 00000000..781e3e5f --- /dev/null +++ b/src/api-impl-jni/audio/android_media_SoundPool.c @@ -0,0 +1,32 @@ +#include + +#include "../defines.h" +#include "../generated_headers/android_media_SoundPool.h" + +JNIEXPORT jlong JNICALL Java_android_media_SoundPool_native_1constructor(JNIEnv *env, jclass) { + GArray *sound_pool_array = g_array_new(FALSE, FALSE, sizeof(GtkMediaStream *)); + return _INTPTR(sound_pool_array); +} + +static void on_prepared(GtkMediaStream *media_stream) { + // play once muted to ensure file is fully loaded + gtk_media_stream_set_muted(media_stream, TRUE); + gtk_media_stream_play(media_stream); +} + +JNIEXPORT jint JNICALL Java_android_media_SoundPool_nativeLoad(JNIEnv *env, jclass, jlong pool, jstring path) { + GArray *sound_pool_array = _PTR(pool); + const char* nativePath = (*env)->GetStringUTFChars(env, path, NULL); + GtkMediaStream *media_stream = gtk_media_file_new_for_filename(nativePath); + g_signal_connect(media_stream, "notify::prepared", G_CALLBACK(on_prepared), NULL); + (*env)->ReleaseStringUTFChars(env, path, nativePath); + return g_array_append_val(sound_pool_array, media_stream)->len - 1; +} + +JNIEXPORT jint JNICALL Java_android_media_SoundPool_nativePlay(JNIEnv *env, jclass, jlong pool, jint soundID) { + GArray *sound_pool_array = _PTR(pool); + GtkMediaStream *media_stream = g_array_index(sound_pool_array, GtkMediaStream *, soundID); + gtk_media_stream_set_muted(media_stream, FALSE); + gtk_media_stream_play(media_stream); + return 0; +} diff --git a/src/api-impl-jni/generated_headers/android_media_SoundPool.h b/src/api-impl-jni/generated_headers/android_media_SoundPool.h new file mode 100644 index 00000000..b7773bd8 --- /dev/null +++ b/src/api-impl-jni/generated_headers/android_media_SoundPool.h @@ -0,0 +1,37 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class android_media_SoundPool */ + +#ifndef _Included_android_media_SoundPool +#define _Included_android_media_SoundPool +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: android_media_SoundPool + * Method: native_constructor + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_android_media_SoundPool_native_1constructor + (JNIEnv *, jclass); + +/* + * Class: android_media_SoundPool + * Method: nativeLoad + * Signature: (JLjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_android_media_SoundPool_nativeLoad + (JNIEnv *, jclass, jlong, jstring); + +/* + * Class: android_media_SoundPool + * Method: nativePlay + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_android_media_SoundPool_nativePlay + (JNIEnv *, jclass, jlong, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/api-impl/android/content/res/AssetFileDescriptor.java b/src/api-impl/android/content/res/AssetFileDescriptor.java index b60724d1..c2a8db9a 100644 --- a/src/api-impl/android/content/res/AssetFileDescriptor.java +++ b/src/api-impl/android/content/res/AssetFileDescriptor.java @@ -42,6 +42,7 @@ public class AssetFileDescriptor implements Closeable { private final long mStartOffset; private final long mLength; private final Bundle mExtras; + public String fileName; /** * Create a new AssetFileDescriptor from the given values. diff --git a/src/api-impl/android/content/res/AssetManager.java b/src/api-impl/android/content/res/AssetManager.java index 92783032..263ba629 100644 --- a/src/api-impl/android/content/res/AssetManager.java +++ b/src/api-impl/android/content/res/AssetManager.java @@ -27,7 +27,7 @@ import android.os.ParcelFileDescriptor; import android.os.Trace; import android.util.Log; import android.util.TypedValue; - +import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -329,18 +329,22 @@ public final class AssetManager { public final AssetFileDescriptor openFd(String fileName) throws IOException { -/* synchronized (this) { + int asset; + synchronized (this) { if (!mOpen) { throw new RuntimeException("Assetmanager has been closed"); } - ParcelFileDescriptor pfd = openAssetFd(fileName, mOffsets); + asset = openAsset(fileName, 0); + FileDescriptor fd = new FileDescriptor(); + fd.setInt$(asset); + ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd); if (pfd != null) { - return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); + AssetFileDescriptor afd = new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); + afd.fileName = "/assets/" + fileName; + return afd; } } - throw new FileNotFoundException("Asset file: " + fileName);*/ - throw new IOException("FIXME: 'public final AssetFileDescriptor openFd(String fileName)': throwing an exception, which makes e.g SDL2 fall back to using something that we actually have implemented"); -// return null; // FIXME + throw new FileNotFoundException("Asset file: " + fileName); } /** diff --git a/src/api-impl/android/media/SoundPool.java b/src/api-impl/android/media/SoundPool.java index 104a1a88..fe27aff1 100644 --- a/src/api-impl/android/media/SoundPool.java +++ b/src/api-impl/android/media/SoundPool.java @@ -3,10 +3,40 @@ package android.media; import android.content.res.AssetFileDescriptor; public class SoundPool { + + private long nativePool; + + public interface OnLoadCompleteListener { + /** + * Called when a sound has completed loading. + * + * @param soundPool SoundPool object from the load() method + * @param soundPool the sample ID of the sound loaded. + * @param status the status of the load operation (0 = success) + */ + public void onLoadComplete(SoundPool soundPool, int sampleId, int status); + } + public SoundPool (int maxStreams, int streamType, int srcQuality) { + nativePool = native_constructor(); } public int load(AssetFileDescriptor afd, int priority) { - return 0; + return nativeLoad(nativePool, android.os.Environment.getExternalStorageDirectory().getPath() + afd.fileName); } + + /** + * Sets the callback hook for the OnLoadCompleteListener. + */ + public void setOnLoadCompleteListener(OnLoadCompleteListener listener) { + System.out.println("WARNING: SoundPool.setOnLoadCompleteListener not implemented yet"); + } + + public final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate) { + return nativePlay(nativePool, soundID); + } + + private static native long native_constructor(); + private static native int nativeLoad(long nativePool, String path); + public static native int nativePlay(long nativePool, int soundID); }