commit 8b6de6a883f97792f6c30d0b7c1662cb39e1a09b Author: David Madl Date: Sun Feb 1 02:54:05 2026 +0100 initial: oboe, logging diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bfbfd07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild +/.idea +/app/src/main/libs/ +app/src/main/obj/ +.cxx +/txts diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt new file mode 100644 index 0000000..e69de29 diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..f1e8103 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,83 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) +} + +android { + namespace 'at.lockstep' + compileSdk 34 + + defaultConfig { + applicationId "at.lockstep" + minSdk 24 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary true + } + externalNativeBuild { + cmake { + //path 'src/main/cpp/CMakeLists.txt' + cppFlags '' + arguments "-DANDROID_STL=c++_shared" + } + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + buildFeatures { + compose true + prefab true + } + composeOptions { + kotlinCompilerExtensionVersion '1.5.1' + } + packaging { + resources { + excludes += '/META-INF/{AL2.0,LGPL2.1}' + } + } + externalNativeBuild { + cmake { + path file('src/main/cpp/CMakeLists.txt') + version '3.22.1' + } + } +} + +dependencies { + implementation libs.oboe + implementation libs.slf4j.api + implementation libs.logback.android + + implementation libs.androidx.core.ktx + implementation libs.androidx.lifecycle.runtime.ktx + implementation libs.androidx.activity.compose + implementation platform(libs.androidx.compose.bom) + implementation libs.androidx.ui + implementation libs.androidx.ui.graphics + implementation libs.androidx.ui.tooling.preview + implementation libs.androidx.material3 + testImplementation libs.junit + androidTestImplementation libs.androidx.junit + androidTestImplementation libs.androidx.espresso.core + androidTestImplementation platform(libs.androidx.compose.bom) + androidTestImplementation libs.androidx.ui.test.junit4 + debugImplementation libs.androidx.ui.tooling + debugImplementation libs.androidx.ui.test.manifest +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..0855b20 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/logback.xml b/app/src/main/assets/logback.xml new file mode 100644 index 0000000..453f552 --- /dev/null +++ b/app/src/main/assets/logback.xml @@ -0,0 +1,27 @@ + + + + + + %msg + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000..e11ba6a --- /dev/null +++ b/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,48 @@ + +# For more information about using CMake with Android Studio, read the +# documentation: https://d.android.com/studio/projects/add-native-code.html. +# For more examples on how to use CMake, see https://github.com/android/ndk-samples. + +# Sets the minimum CMake version required for this project. +cmake_minimum_required(VERSION 3.22.1) + +# Declares the project name. The project name can be accessed via ${ PROJECT_NAME}, +# Since this is the top level CMakeLists.txt, the project name is also accessible +# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level +# build script scope). +project("lockstep-native") + +# Creates and names a library, sets it as either STATIC +# or SHARED, and provides the relative paths to its source code. +# You can define multiple libraries, and CMake builds them for you. +# Gradle automatically packages shared libraries with your APK. +# +# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define +# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME} +# is preferred for the same purpose. +# +# In order to load a library into your app from Java/Kotlin, you must call +# System.loadLibrary() and pass the name of the library defined here; +# for GameActivity/NativeActivity derived applications, the same library name must be +# used in the AndroidManifest.xml file. +add_library(${CMAKE_PROJECT_NAME} SHARED + # List C/C++ source files with relative paths to this CMakeLists.txt. + lockstep.cpp + PlaybackEngine.cpp +) + +find_package (oboe REQUIRED CONFIG) + +add_library(ndk-logger SHARED logging.cpp logging_jni.cpp) +target_link_libraries(ndk-logger log) + +# Specifies libraries CMake should link to your target library. You +# can link libraries from various origins, such as libraries defined in this +# build script, prebuilt third-party libraries, or Android system libraries. +target_link_libraries(${CMAKE_PROJECT_NAME} + # List libraries link to the target library + oboe::oboe + android + log + ndk-logger +) diff --git a/app/src/main/cpp/OboeSinePlayer.h b/app/src/main/cpp/OboeSinePlayer.h new file mode 100644 index 0000000..95863e8 --- /dev/null +++ b/app/src/main/cpp/OboeSinePlayer.h @@ -0,0 +1,78 @@ +// +// Created by david on 01.02.2026. +// + +#ifndef LOCKSTEP_OBOESINEPLAYER_H +#define LOCKSTEP_OBOESINEPLAYER_H + +#include +#include +using namespace oboe; + +class OboeSinePlayer: public oboe::AudioStreamDataCallback { +public: + + virtual ~OboeSinePlayer() = default; + + // Call this from Activity onResume() + int32_t startAudio() { + std::lock_guard lock(mLock); + oboe::AudioStreamBuilder builder; + // The builder set methods can be chained for convenience. + Result result = builder.setSharingMode(oboe::SharingMode::Exclusive) + ->setPerformanceMode(oboe::PerformanceMode::LowLatency) + ->setChannelCount(kChannelCount) + ->setSampleRate(kSampleRate) + ->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::Medium) + ->setFormat(oboe::AudioFormat::Float) + ->setDataCallback(this) + ->openStream(mStream); + if (result != Result::OK) return (int32_t) result; + + // Typically, start the stream after querying some stream information, as well as some input from the user + result = mStream->requestStart(); + return (int32_t) result; + } + + // Call this from Activity onPause() + void stopAudio() { + // Stop, close and delete in case not already closed. + std::lock_guard lock(mLock); + if (mStream) { + mStream->stop(); + mStream->close(); + mStream.reset(); + } + } + + oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) override { + float *floatData = (float *) audioData; + for (int i = 0; i < numFrames; ++i) { + float sampleValue = kAmplitude * sinf(mPhase); + for (int j = 0; j < kChannelCount; j++) { + floatData[i * kChannelCount + j] = sampleValue; + } + mPhase += mPhaseIncrement; + if (mPhase >= kTwoPi) mPhase -= kTwoPi; + } + return oboe::DataCallbackResult::Continue; + } + +private: + std::mutex mLock; + std::shared_ptr mStream; + + // Stream params + static int constexpr kChannelCount = 2; + static int constexpr kSampleRate = 48000; + // Wave params, these could be instance variables in order to modify at runtime + static float constexpr kAmplitude = 0.5f; + static float constexpr kFrequency = 440; + static float constexpr kPI = M_PI; + static float constexpr kTwoPi = kPI * 2; + static double constexpr mPhaseIncrement = kFrequency * kTwoPi / (double) kSampleRate; + // Keeps track of where the wave is + float mPhase = 0.0; +}; + +#endif //LOCKSTEP_OBOESINEPLAYER_H diff --git a/app/src/main/cpp/PlaybackEngine.cpp b/app/src/main/cpp/PlaybackEngine.cpp new file mode 100644 index 0000000..7cd48b3 --- /dev/null +++ b/app/src/main/cpp/PlaybackEngine.cpp @@ -0,0 +1,24 @@ +// +// Created by david on 01.02.2026. +// + +#define LOG_TAG "PlaybackEngine" + +#include "PlaybackEngine.h" +#include "logging.h" + +PlaybackEngine::PlaybackEngine() { + LOGI("PlaybackEngine()"); + LOGI("NDK LOG_LEVEL=%d", LOG_LEVEL); + // NDK LOG_LEVEL=3 (DEBUG) + mPlayer = new OboeSinePlayer(); + int32_t res = mPlayer->startAudio(); + LOGI("startAudio() = %d", res); +} + +PlaybackEngine::~PlaybackEngine() { + LOGI("~PlaybackEngine()"); + mPlayer->stopAudio(); + delete mPlayer; + mPlayer = nullptr; +} diff --git a/app/src/main/cpp/PlaybackEngine.h b/app/src/main/cpp/PlaybackEngine.h new file mode 100644 index 0000000..53286c6 --- /dev/null +++ b/app/src/main/cpp/PlaybackEngine.h @@ -0,0 +1,18 @@ +// +// Created by david on 01.02.2026. +// + +#ifndef LOCKSTEP_PLAYBACKENGINE_H +#define LOCKSTEP_PLAYBACKENGINE_H + +#include "OboeSinePlayer.h" + +class PlaybackEngine { +public: + PlaybackEngine(); + virtual ~PlaybackEngine(); +private: + OboeSinePlayer *mPlayer; +}; + +#endif //LOCKSTEP_PLAYBACKENGINE_H diff --git a/app/src/main/cpp/lockstep.cpp b/app/src/main/cpp/lockstep.cpp new file mode 100644 index 0000000..5145ea3 --- /dev/null +++ b/app/src/main/cpp/lockstep.cpp @@ -0,0 +1,54 @@ +// Write C++ code here. +// +// Do not forget to dynamically load the C++ library into your application. +// +// For instance, +// +// In at.lockstep.app.MainActivity.java: +// static { +// System.loadLibrary("lockstep"); +// } +// +// Or, in at.lockstep.app.MainActivity.kt: +// companion object { +// init { +// System.loadLibrary("lockstep") +// } +// } + +#include "PlaybackEngine.h" +#include +#include + +extern "C" { + +/** + * Creates the audio engine + * + * @return a pointer to the audio engine. This should be passed to other methods + */ +JNIEXPORT jlong JNICALL +Java_at_lockstep_pb_PlaybackEngine_native_1createEngine( + JNIEnv *env, + jclass /*unused*/) { + /* + 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(); + return reinterpret_cast(engine); +} + +JNIEXPORT void JNICALL +Java_at_lockstep_pb_PlaybackEngine_native_1deleteEngine( + JNIEnv *env, +jclass, +jlong engineHandle) { + +delete reinterpret_cast(engineHandle); +} + +} // extern "C" diff --git a/app/src/main/cpp/logging.cpp b/app/src/main/cpp/logging.cpp new file mode 100644 index 0000000..46111a9 --- /dev/null +++ b/app/src/main/cpp/logging.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Logger implement, support writing into stdout and send for java logback + */ + +#include "logging.h" +#include + +extern "C" { + +static JavaVM * __logback_jvm = NULL; +static jclass __logback_jclass_logger; +static jmethodID __logback_jmethod_logWrite; + +void +__stdout_log_print(const int level, const char * format, ...) +{ + va_list args; + static char tags[] = {'?', '?', 'V', 'D', 'I', 'W', 'E', 'F', '?'}; + + va_start(args, format); + + printf("[%c]%s ", tags[level], LOG_TAG); + vprintf(format, args); + printf("\n"); + + va_end(args); +} + +void +__stdout_log_vprint(const int level, const char * format, va_list ap) +{ + static char tags[] = {'?', '?', 'V', 'D', 'I', 'W', 'E', 'F', '?'}; + printf("[%c]%s ", tags[level], LOG_TAG); + vprintf(format, ap); + printf("\n"); +} + +void +__stdout_log_write(const int level, const char * msg) +{ + static char tags[] = {'?', '?', 'V', 'D', 'I', 'W', 'E', 'F', '?'}; + printf("[%c]%s %s\n", tags[level], LOG_TAG, msg); +} + + + +void +logback_init(JavaVM * jVM, JNIEnv * env) +{ + __logback_jvm = jVM; + jclass jclass_logger = env->FindClass("com/rex/logger/NdkLogger"); + if (! jclass_logger) { + __android_log_write(LOG_LEVEL_ERROR, LOG_TAG, "logback_init failed to get logger class reference"); + return; + } + __logback_jclass_logger = (jclass) env->NewGlobalRef(jclass_logger); + + __logback_jmethod_logWrite = env->GetStaticMethodID(__logback_jclass_logger, "logWrite", "(ILjava/lang/String;)V"); + if (! __logback_jmethod_logWrite) { + __android_log_write(LOG_LEVEL_ERROR, LOG_TAG, "logback_init failed to get logwrite method ID"); + return; + } +} + +void +logback_uninit(JNIEnv * env) +{ + env->DeleteGlobalRef(__logback_jclass_logger); + __logback_jclass_logger = NULL; +} + +void +__logback_print(const int level, const char * format, ...) +{ + JNIEnv * env = NULL; + jint err = JNI_OK; + if (! __logback_jvm) return; + if (JNI_EDETACHED == (err = __logback_jvm->GetEnv((void**) &env, JNI_VERSION_1_4))) { + if (__logback_jvm->AttachCurrentThread(&env, NULL) < 0) { + __android_log_write(LOG_LEVEL_WARN, LOG_TAG, "__logback_write failed to get env neither attach current thread"); + } + } + + static char buffer[1024]; + va_list args; + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + jstring jmsg = env->NewStringUTF(buffer); + env->CallStaticVoidMethod(__logback_jclass_logger, __logback_jmethod_logWrite, level, jmsg); + env->DeleteLocalRef(jmsg); + va_end(args); + + if (JNI_EDETACHED == err) __logback_jvm->DetachCurrentThread(); +} + +void +__logback_vprint(const int level, const char * format, va_list ap) +{ + JNIEnv * env = NULL; + jint err = JNI_OK; + if (! __logback_jvm) return; + if (JNI_EDETACHED == (err = __logback_jvm->GetEnv((void**) &env, JNI_VERSION_1_4))) { + if (__logback_jvm->AttachCurrentThread(&env, NULL) < 0) { + __android_log_write(LOG_LEVEL_WARN, LOG_TAG, "__logback_write failed to get env neither attach current thread"); + } + } + + static char buffer[1024]; + vsnprintf(buffer, sizeof(buffer), format, ap); + jstring jmsg = env->NewStringUTF(buffer); + env->CallStaticVoidMethod(__logback_jclass_logger, __logback_jmethod_logWrite, level, jmsg); + env->DeleteLocalRef(jmsg); + + if (JNI_EDETACHED == err) __logback_jvm->DetachCurrentThread(); +} + +void +__logback_write(const int level, const char * msg) +{ + JNIEnv * env = NULL; + jint err = JNI_OK; + if (! __logback_jvm) return; + if (JNI_EDETACHED == (err = __logback_jvm->GetEnv((void**) &env, JNI_VERSION_1_4))) { + if (__logback_jvm->AttachCurrentThread(&env, NULL) < 0) { + __android_log_write(LOG_LEVEL_WARN, LOG_TAG, "__logback_write failed to get env neither attach current thread"); + } + } + + jstring jmsg = env->NewStringUTF(msg); + env->CallStaticVoidMethod(__logback_jclass_logger, __logback_jmethod_logWrite, level, jmsg); + env->DeleteLocalRef(jmsg); + + if (JNI_EDETACHED == err) __logback_jvm->DetachCurrentThread(); +} + +} + +// vim:ts=8:sw=4: \ No newline at end of file diff --git a/app/src/main/cpp/logging.h b/app/src/main/cpp/logging.h new file mode 100644 index 0000000..e799932 --- /dev/null +++ b/app/src/main/cpp/logging.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Logging support, provide the level definition and support logging to logcat, stdout and Java logback + * When using logback, need add com/rex/logger/NdkLogger.java into project + */ + +#ifndef _LOGGING_H_ +#define _LOGGING_H_ + +#define LOG_OUTPUT_LOGBACK + +#ifndef LOG_TAG +# define LOG_TAG "Rex" +#endif + +#ifndef LOG_LEVEL +# define LOG_LEVEL LOG_LEVEL_DEBUG +//# define LOG_LEVEL LOG_LEVEL_VERBOSE // note: goes to logcat only +#endif + +// values here must match those in android_LogPriority +#define LOG_LEVEL_ALL 1 +#define LOG_LEVEL_VERBOSE 2 +#define LOG_LEVEL_DEBUG 3 +#define LOG_LEVEL_INFO 4 +#define LOG_LEVEL_WARN 5 +#define LOG_LEVEL_ERROR 6 +#define LOG_LEVEL_FATAL 7 +#define LOG_LEVEL_SILENT 8 + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void __stdout_log_print(const int level, const char * format, ...); +void __stdout_log_vprint(const int level, const char * format, va_list ap); +void __stdout_log_write(const int level, const char * msg); + +void logback_init(JavaVM * jVM, JNIEnv * env); +void logback_uninit(JNIEnv * env); +void __logback_print(const int level, const char * format, ...); +void __logback_vprint(const int level, const char * format, va_list ap); +void __logback_write(const int level, const char * msg); + +#ifdef __cplusplus +} //extern "C" +#endif + + +#ifdef LOG_OUTPUT_STDOUT +# define LOG_PRINT(prio, format, ...) __stdout_log_print(prio, format, ##__VA_ARGS__) +# define LOG_VPRINT(prio, format, ap) __stdout_log_vprint(prio, format, ap) +# define LOG_WRITE(prio, msg) __stdout_log_write(prio, msg) +#elif defined(LOG_OUTPUT_LOGBACK) +# define LOG_PRINT(prio, format, ...) __logback_print(prio, LOG_TAG " - " format, ##__VA_ARGS__) +# define LOG_VPRINT(prio, format, ap) __logback_vprint(prio, LOG_TAG " - " format, ap) +# define LOG_WRITE(prio, msg) __logback_write(prio, LOG_TAG " - " msg) +# include +# define LOG_PRINT_ANDROID(prio, format, ...) __android_log_print(prio, LOG_TAG, format, ##__VA_ARGS__) +#else +# include +# define LOG_PRINT(prio, format, ...) __android_log_print(prio, LOG_TAG, format, ##__VA_ARGS__) +# define LOG_VPRINT(prio, format, ap) __android_log_vprint(prio, LOG_TAG, format, ap) +# define LOG_WRITE(prio, msg) __android_log_write(prio, LOG_TAG, msg) +#endif + +#if LOG_LEVEL > LOG_LEVEL_VERBOSE +# define LOGV(...) +#else +# define LOGV(...) LOG_PRINT_ANDROID(LOG_LEVEL_VERBOSE, __VA_ARGS__) +#endif + +#if LOG_LEVEL > LOG_LEVEL_DEBUG +# define LOGD(...) +#else +# define LOGD(...) LOG_PRINT(LOG_LEVEL_DEBUG, __VA_ARGS__) +#endif + +#if LOG_LEVEL > LOG_LEVEL_INFO +# define LOGI(...) +#else +# define LOGI(...) LOG_PRINT(LOG_LEVEL_INFO, __VA_ARGS__) +#endif + +#if LOG_LEVEL > LOG_LEVEL_WARN +# define LOGW(...) +#else +# define LOGW(...) LOG_PRINT(LOG_LEVEL_WARN, __VA_ARGS__) +#endif + +#if LOG_LEVEL > LOG_LEVEL_ERROR +# define LOGE(...) +#else +# define LOGE(...) LOG_PRINT(LOG_LEVEL_ERROR, __VA_ARGS__) +#endif + + +#endif /* #ifndef _LOGGING_H_ */ + +// vim:ts=8:sw=4: \ No newline at end of file diff --git a/app/src/main/cpp/logging_jni.cpp b/app/src/main/cpp/logging_jni.cpp new file mode 100644 index 0000000..fd8f400 --- /dev/null +++ b/app/src/main/cpp/logging_jni.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include "logging.h" + + +extern "C" jstring +Java_com_rex_logger_NdkLogger_native_1getABI(JNIEnv* env, jclass thiz) +{ +#if defined(__arm__) + #if defined(__ARM_ARCH_7A__) + #if defined(__ARM_NEON__) + #if defined(__ARM_PCS_VFP) + #define ABI "armeabi-v7a/NEON (hard-float)" + #else + #define ABI "armeabi-v7a/NEON" + #endif + #else + #if defined(__ARM_PCS_VFP) + #define ABI "armeabi-v7a (hard-float)" + #else + #define ABI "armeabi-v7a" + #endif + #endif + #else + #define ABI "armeabi" + #endif +#elif defined(__i386__) +#define ABI "x86" +#elif defined(__x86_64__) +#define ABI "x86_64" +#elif defined(__mips64) /* mips64el-* toolchain defines __mips__ too */ +#define ABI "mips64" +#elif defined(__mips__) +#define ABI "mips" +#elif defined(__aarch64__) +#define ABI "arm64-v8a" +#else +#define ABI "unknown" +#endif + + return env->NewStringUTF(ABI); +} + +extern "C" jint +JNI_OnLoad(JavaVM * vm, void * reserved) +{ + JNIEnv * env = NULL; + jclass clazz; + //LOGV("JNI_Onload vm:%p reserved:%p", vm, reserved); + + if (JNI_OK != vm->GetEnv((void**) &env, JNI_VERSION_1_4)) { + //LOGE("JNI_OnUnload GetEnv JNI_VERSION_1_4 failed"); + return JNI_VERSION_1_4; + } + + logback_init(vm, env); + + return JNI_VERSION_1_4; +} + +extern "C" void +JNI_OnUnload(JavaVM * vm, void * reserved) +{ + JNIEnv * env = NULL; + //LOGV("JNI_OnUnload vm:%p reserved:%p", vm, reserved); + + if (JNI_OK != vm->GetEnv((void**) &env, JNI_VERSION_1_4)) { + //LOGE("JNI_OnUnload GetEnv JNI_VERSION_1_4 failed"); + return; + } + + logback_uninit(env); +} + +// vim:ts=8:sw=4: diff --git a/app/src/main/java/at/lockstep/app/MainActivity.java b/app/src/main/java/at/lockstep/app/MainActivity.java new file mode 100644 index 0000000..87dfe21 --- /dev/null +++ b/app/src/main/java/at/lockstep/app/MainActivity.java @@ -0,0 +1,23 @@ +package at.lockstep.app; + +import android.app.Activity; + +import at.lockstep.pb.PlaybackEngine; + +public class MainActivity extends Activity { + /* + * Creating engine in onResume() and destroying in onPause() so the stream retains exclusive + * mode only while in focus. This allows other apps to reclaim exclusive stream mode. + */ + @Override + protected void onResume() { + super.onResume(); + PlaybackEngine.create(this); // note: called twice (is permission request causing Activity to go out of focus?) + } + + @Override + protected void onPause() { + PlaybackEngine.delete(); + super.onPause(); + } +} diff --git a/app/src/main/java/at/lockstep/pb/PlaybackEngine.java b/app/src/main/java/at/lockstep/pb/PlaybackEngine.java new file mode 100644 index 0000000..4484940 --- /dev/null +++ b/app/src/main/java/at/lockstep/pb/PlaybackEngine.java @@ -0,0 +1,31 @@ +package at.lockstep.pb; + +import android.content.Context; +import android.util.Log; + +public class PlaybackEngine { + static long mEngineHandle = 0; + + static { + System.loadLibrary("lockstep-native"); + } + + public static boolean create(Context context) { + if (mEngineHandle == 0) { + //setDefaultStreamValues(context); // TODO + Log.i("PlaybackEngine", "Hello PlaybackEngine"); + mEngineHandle = native_createEngine(); + } + return (mEngineHandle != 0); + } + + public static void delete() { + if (mEngineHandle != 0){ + native_deleteEngine(mEngineHandle); + } + mEngineHandle = 0; + } + + private static native long native_createEngine(); + private static native void native_deleteEngine(long engineHandle); +} diff --git a/app/src/main/java/com/rex/logger/NdkLogger.java b/app/src/main/java/com/rex/logger/NdkLogger.java new file mode 100644 index 0000000..a049ebd --- /dev/null +++ b/app/src/main/java/com/rex/logger/NdkLogger.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.rex.logger; + +import android.util.Log; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +//import proguard.annotation.KeepName; + +//@KeepName + +/** + * Gather native log into logback, for easy control save log into SDCard + */ +public class NdkLogger { + + private static final Logger sLogger = LoggerFactory.getLogger("NDK"); + + //@KeepName + public static void logWrite(int level, String message) { + //sLogger.trace("level:{} message:{}", level, message); + switch (level) { + case Log.VERBOSE: + sLogger.trace(message); + break; + case Log.DEBUG: + sLogger.debug(message); + break; + case Log.INFO: + sLogger.info(message); + break; + case Log.WARN: + sLogger.warn(message); + break; + case Log.ERROR: + sLogger.error(message); + break; + } + } + + static { + try { + System.loadLibrary("ndk-logger"); + } catch (UnsatisfiedLinkError ex) { + sLogger.error("Failed to load library.", ex); + } + } + + public static String getABI() { return native_getABI(); } + private static native String native_getABI(); +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..35a425e --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Lockstep + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..5f919fd --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + +