diff --git a/app/src/main/cpp/PlaybackEngine.cpp b/app/src/main/cpp/PlaybackEngine.cpp index 7cd48b3..4b6dcb1 100644 --- a/app/src/main/cpp/PlaybackEngine.cpp +++ b/app/src/main/cpp/PlaybackEngine.cpp @@ -7,7 +7,7 @@ #include "PlaybackEngine.h" #include "logging.h" -PlaybackEngine::PlaybackEngine() { +PlaybackEngine::PlaybackEngine(std::string filesDir): mFilesDir(filesDir) { LOGI("PlaybackEngine()"); LOGI("NDK LOG_LEVEL=%d", LOG_LEVEL); // NDK LOG_LEVEL=3 (DEBUG) diff --git a/app/src/main/cpp/PlaybackEngine.h b/app/src/main/cpp/PlaybackEngine.h index 53286c6..73b0883 100644 --- a/app/src/main/cpp/PlaybackEngine.h +++ b/app/src/main/cpp/PlaybackEngine.h @@ -6,13 +6,15 @@ #define LOCKSTEP_PLAYBACKENGINE_H #include "OboeSinePlayer.h" +#include class PlaybackEngine { public: - PlaybackEngine(); + PlaybackEngine(std::string filesDir); virtual ~PlaybackEngine(); private: OboeSinePlayer *mPlayer; + std::string mFilesDir; }; #endif //LOCKSTEP_PLAYBACKENGINE_H diff --git a/app/src/main/cpp/jni_bridge.cpp b/app/src/main/cpp/jni_bridge.cpp index 8462477..0c56e22 100644 --- a/app/src/main/cpp/jni_bridge.cpp +++ b/app/src/main/cpp/jni_bridge.cpp @@ -4,9 +4,12 @@ #include #include "mpg123.h" +#include extern "C" { +// nice-to: merge with lockstep.cpp + JNIEXPORT jint JNICALL Java_at_lockstep_pb_PlaybackEngine_native_1mpg123_1init (JNIEnv *env, jclass) { diff --git a/app/src/main/cpp/lockstep.cpp b/app/src/main/cpp/lockstep.cpp index 5145ea3..504b95b 100644 --- a/app/src/main/cpp/lockstep.cpp +++ b/app/src/main/cpp/lockstep.cpp @@ -30,15 +30,13 @@ extern "C" { JNIEXPORT jlong JNICALL Java_at_lockstep_pb_PlaybackEngine_native_1createEngine( JNIEnv *env, - jclass /*unused*/) { - /* + jclass /*unused*/, jstring filesDir) { const char* filesDirTemp = env->GetStringUTFChars(filesDir, NULL); std::string filesDirString(filesDirTemp); env->ReleaseStringUTFChars(filesDir, filesDirTemp); - */ // We use std::nothrow so `new` returns a nullptr if the engine creation fails - auto *engine = new(std::nothrow) PlaybackEngine(); + auto *engine = new(std::nothrow) PlaybackEngine(filesDirString); return reinterpret_cast(engine); } @@ -51,4 +49,13 @@ jlong engineHandle) { delete reinterpret_cast(engineHandle); } +JNIEXPORT void JNICALL +Java_at_lockstep_pb_PlaybackEngine_native_1setDefaultStreamValues(JNIEnv *env, + jclass type, + jint sampleRate, + jint framesPerBurst) { + oboe::DefaultStreamValues::SampleRate = (int32_t) sampleRate; + oboe::DefaultStreamValues::FramesPerBurst = (int32_t) framesPerBurst; +} + } // extern "C" diff --git a/app/src/main/java/at/lockstep/pb/AudioResources.java b/app/src/main/java/at/lockstep/pb/AudioResources.java new file mode 100644 index 0000000..94eaed1 --- /dev/null +++ b/app/src/main/java/at/lockstep/pb/AudioResources.java @@ -0,0 +1,60 @@ +package at.lockstep.pb; + +import android.content.Context; +import android.os.SystemClock; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; + +import at.lockstep.R; + +/** + * Resource helpers. + * + * @author David Madl (git@abanbytes.eu) + * @date 2020-05-16 + */ +public class AudioResources { + /** + * Copy raw tracks to filesystem: + * otherwise, packaged resources cannot directly be accessed from C code. + */ + public static void copyRawTracksToFilesystem(Context context) throws IOException { + Field[] fields = R.raw.class.getFields(); + long before = SystemClock.uptimeMillis(); + try { + for (int i = 0; i < fields.length; i++) { + String assetName = fields[i].getName(); + if (assetName.startsWith("track_")) { + int resourceId = fields[i].getInt(null); + copyFileUsingStream(context.getResources().openRawResource(resourceId), new File(context.getFilesDir() + "/" + resourceId + ".mp3")); + } + } + } catch(IllegalAccessException iae) { + // reflection should always work on R.raw + iae.printStackTrace(); + } + long after = SystemClock.uptimeMillis(); + Log.i("LoadTracks", String.format("copyRawTracksToFilesystem() took %.3f s", ((float)(after-before))/1e3f)); + } + + private static void copyFileUsingStream(InputStream is, File dest) throws IOException { + OutputStream os = null; + try { + os = new FileOutputStream(dest); + byte[] buffer = new byte[1024]; + int length; + while ((length = is.read(buffer)) > 0) { + os.write(buffer, 0, length); + } + } finally { + is.close(); + os.close(); + } + } +} diff --git a/app/src/main/java/at/lockstep/pb/PlaybackEngine.java b/app/src/main/java/at/lockstep/pb/PlaybackEngine.java index 1f5dcc2..68b3878 100644 --- a/app/src/main/java/at/lockstep/pb/PlaybackEngine.java +++ b/app/src/main/java/at/lockstep/pb/PlaybackEngine.java @@ -1,11 +1,16 @@ package at.lockstep.pb; import android.content.Context; +import android.media.AudioManager; +import android.os.Build; import android.util.Log; +import java.io.IOException; + public class PlaybackEngine { static long mEngineHandle = 0; static final int MPG123_OK = 0; + static boolean mFilesystemInitialized = false; static { System.loadLibrary("lockstep-native"); @@ -15,14 +20,36 @@ public class PlaybackEngine { } public static boolean create(Context context) { + try { + if(!mFilesystemInitialized) { + AudioResources.copyRawTracksToFilesystem(context); + mFilesystemInitialized = true; + } + } catch (IOException e) { + e.printStackTrace(); + return false; + } + if (mEngineHandle == 0) { - //setDefaultStreamValues(context); // TODO + setDefaultStreamValues(context); Log.i("PlaybackEngine", "Hello PlaybackEngine"); - mEngineHandle = native_createEngine(); + mEngineHandle = native_createEngine(context.getFilesDir().toString()); } return (mEngineHandle != 0); } + private static void setDefaultStreamValues(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){ + AudioManager myAudioMgr = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + String sampleRateStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); + int defaultSampleRate = Integer.parseInt(sampleRateStr); + String framesPerBurstStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER); + int defaultFramesPerBurst = Integer.parseInt(framesPerBurstStr); + + native_setDefaultStreamValues(defaultSampleRate, defaultFramesPerBurst); + } + } + public static void delete() { if (mEngineHandle != 0){ native_deleteEngine(mEngineHandle); @@ -30,8 +57,9 @@ public class PlaybackEngine { mEngineHandle = 0; } - private static native long native_createEngine(); + private static native long native_createEngine(String filesDir); private static native void native_deleteEngine(long engineHandle); + private static native void native_setDefaultStreamValues(int sampleRate, int framesPerBurst); private static native int native_mpg123_init(); } diff --git a/app/src/main/res/raw/track_beat.mp3 b/app/src/main/res/raw/track_beat.mp3 new file mode 100644 index 0000000..ed6a555 Binary files /dev/null and b/app/src/main/res/raw/track_beat.mp3 differ