diff --git a/examples/common/android/MainActivity.java b/examples/common/android/MainActivity.java index 1bde839..4ea1d1e 100644 --- a/examples/common/android/MainActivity.java +++ b/examples/common/android/MainActivity.java @@ -51,7 +51,7 @@ public class MainActivity extends Activity { @JavascriptInterface public void setParameter(int i, float v) { - nativeSetParameter(i, v); + nativeSetParameter(i, v * 0.001f); } } diff --git a/examples/common/android/android.mk b/examples/common/android/android.mk index c0423e3..5ca4b19 100644 --- a/examples/common/android/android.mk +++ b/examples/common/android/android.mk @@ -1,20 +1,23 @@ COMMON_DIR := ${ROOT_DIR}/../../common/android -BUILD_TOOLS_DIR := ${HOME}/Android/Sdk/build-tools/34.0.0 +ANDROID_SDK_DIR := ${HOME}/Android/Sdk +ANDROID_NDK_DIR := ${ANDROID_SDK_DIR}/ndk/25.1.8937393 ANDROIDX_DIR := ${HOME}/Android/androidx +KOTLIN_DIR := ${HOME}/Android/kotlin MINIAUDIO_DIR := ${ROOT_DIR}/../../../../miniaudio -JAR_FILE := ${HOME}/Android/Sdk/platforms/android-34/android.jar +BUILD_TOOLS_DIR := ${ANDROID_SDK_DIR}/build-tools/34.0.0 + +JAR_FILE := ${ANDROID_SDK_DIR}/platforms/android-34/android.jar ANDROIDX_CORE_FILE := ${ANDROIDX_DIR}/core-1.10.1.jar ANDROIDX_LIFECYCLE_COMMON_FILE := ${ANDROIDX_DIR}/lifecycle-common-2.6.1.jar ANDROIDX_VERSIONEDPARCELABLE_FILE := ${ANDROIDX_DIR}/versionedparcelable-1.1.1.jar -KOTLIN_STDLIB_FILE := ${HOME}/Android/kotlin/kotlin-stdlib-1.9.0.jar -KOTLINX_COROUTINES_CORE_FILE := ${HOME}/Android/kotlin/kotlinx-coroutines-core-1.7.3.jar -KOTLINX_COROUTINES_CORE_JVM_FILE := ${HOME}/Android/kotlin/kotlinx-coroutines-core-jvm-1.7.3.jar +KOTLIN_STDLIB_FILE := ${KOTLIN_DIR}/kotlin-stdlib-1.9.0.jar +KOTLINX_COROUTINES_CORE_FILE := ${KOTLIN_DIR}/kotlinx-coroutines-core-1.7.3.jar +KOTLINX_COROUTINES_CORE_JVM_FILE := ${KOTLIN_DIR}/kotlinx-coroutines-core-jvm-1.7.3.jar JAVAC := javac -KOTLINC := kotlinc KEYTOOL := keytool -CXX := ${HOME}/Android/Sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi23-clang++ +CXX := ${ANDROID_NDK_DIR}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi23-clang++ ADB := ${HOME}/Android/Sdk/platform-tools/adb APKSIGNER := ${BUILD_TOOLS_DIR}/apksigner ZIPALIGN := ${BUILD_TOOLS_DIR}/zipalign diff --git a/examples/common/android/index.html b/examples/common/android/index.html index 454b3e4..cbdc55c 100644 --- a/examples/common/android/index.html +++ b/examples/common/android/index.html @@ -49,11 +49,13 @@ window.onload = function () { range.value = parameters[i].defaultValue; if (parameters[i].output) range.setAttribute("readonly", "readonly"); - else + else { + let index = i; range.addEventListener("input", function (ev) { - Android.setParameter(i, e.target.value); + Android.setParameter(index, ev.target.value * 1000); }); + } div.appendChild(range); } diff --git a/examples/common/android/jni.cpp b/examples/common/android/jni.cpp index 37443ab..3750d77 100644 --- a/examples/common/android/jni.cpp +++ b/examples/common/android/jni.cpp @@ -1,28 +1,86 @@ +#include +#include #include #define MINIAUDIO_IMPLEMENTATION #include #include "config.h" -extern "C" { +#define BLOCK_SIZE 32 +#define NUM_BUFS (NUM_CHANNELS_IN > NUM_CHANNELS_OUT ? NUM_CHANNELS_IN : NUM_CHANNELS_OUT) ma_device device; +P_TYPE instance; +float paramValues[NUM_PARAMETERS]; +float bufs[NUM_BUFS][BLOCK_SIZE]; +#if NUM_CHANNELS_IN != 0 +const float *inBufs[NUM_CHANNELS_IN]; +#endif +float *outBufs[NUM_CHANNELS_OUT]; +std::mutex mutex; +#ifdef P_MEM_REQ +void *mem; +#endif static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { + (void)pDevice; +#if NUM_CHANNELS_IN == 0 + (void)pInput; +#endif + + const float *x = reinterpret_cast(pInput); + float *y = reinterpret_cast(pOutput); + + ma_uint32 i = 0; + while (i < frameCount) { + ma_uint32 n = std::min(frameCount - i, static_cast(BLOCK_SIZE)); + + int l; +#if NUM_CHANNELS_IN != 0 + l = NUM_CHANNELS_IN * i; + for (ma_uint32 j = 0; j < n; j++) + for (int k = 0; k < NUM_CHANNELS_IN; k++, l++) + bufs[k][j] = x[l]; +#endif + + P_PROCESS(&instance, inBufs, outBufs, n); + + l = NUM_CHANNELS_OUT * i; + for (ma_uint32 j = 0; j < n; j++) + for (int k = 0; k < NUM_CHANNELS_OUT; k++, l++) + y[l] = bufs[k][j]; + + i += n; + } + + if (mutex.try_lock()) { + for (int i = 0; i < NUM_PARAMETERS; i++) + if (config_parameters[i].out) + paramValues[i] = P_GET_PARAMETER(&instance, i); + else + P_SET_PARAMETER(&instance, i, paramValues[i]); + mutex.unlock(); + } } +extern "C" JNIEXPORT jboolean JNICALL Java_com_orastron_@JNI_NAME@_MainActivity_nativeAudioStart(JNIEnv* env, jobject thiz) { + (void)env; + (void)thiz; + #if NUM_CHANNELS_IN == 0 ma_device_config deviceConfig = ma_device_config_init(ma_device_type_playback); #else ma_device_config deviceConfig = ma_device_config_init(ma_device_type_duplex); #endif - deviceConfig.sampleRate = 0; - deviceConfig.periodSizeInFrames = 64; + deviceConfig.periodSizeInFrames = BLOCK_SIZE; deviceConfig.periods = 1; - deviceConfig.noPreSilencedOutputBuffer = 1; - deviceConfig.noDisableDenormals = 0; deviceConfig.performanceProfile = ma_performance_profile_low_latency; + deviceConfig.noPreSilencedOutputBuffer = 1; + deviceConfig.noClip = 0; + deviceConfig.noDisableDenormals = 0; + deviceConfig.noFixedSizedCallback = 1; + deviceConfig.dataCallback = data_callback; deviceConfig.capture.pDeviceID = NULL; deviceConfig.capture.format = ma_format_f32; deviceConfig.capture.channels = NUM_CHANNELS_IN; @@ -30,12 +88,45 @@ Java_com_orastron_@JNI_NAME@_MainActivity_nativeAudioStart(JNIEnv* env, jobject deviceConfig.playback.pDeviceID = NULL; deviceConfig.playback.format = ma_format_f32; deviceConfig.playback.channels = NUM_CHANNELS_OUT; - deviceConfig.dataCallback = data_callback; - + deviceConfig.playback.shareMode = ma_share_mode_shared; + if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) return false; + P_INIT(&instance); + P_SET_SAMPLE_RATE(&instance, (float)device.sampleRate); +#ifdef P_MEM_REQ + size_t req = P_MEM_REQ(&instance); + if (req) { + mem = malloc(req); + if (mem == NULL) { + ma_device_uninit(&device); + return false; + } + P_MEM_SET(&instance, mem); + } else + mem = NULL; +#endif + + for (int i = 0; i < NUM_PARAMETERS; i++) { + paramValues[i] = config_parameters[i].defaultValueUnmapped; + if (!config_parameters[i].out) + P_SET_PARAMETER(&instance, i, paramValues[i]); + } + + P_RESET(&instance); + +#if NUM_CHANNELS_IN != 0 + for (int i = 0; i < NUM_CHANNELS_IN; i++) + inBufs[i] = bufs[i]; +#endif + for (int i = 0; i < NUM_CHANNELS_OUT; i++) + outBufs[i] = bufs[i]; + if (ma_device_start(&device) != MA_SUCCESS) { +#ifdef P_MEM_REQ + free(mem); +#endif ma_device_uninit(&device); return false; } @@ -43,19 +134,42 @@ Java_com_orastron_@JNI_NAME@_MainActivity_nativeAudioStart(JNIEnv* env, jobject return true; } +extern "C" JNIEXPORT void JNICALL Java_com_orastron_@JNI_NAME@_MainActivity_nativeAudioStop(JNIEnv* env, jobject thiz) { + (void)env; + (void)thiz; + +#ifdef P_MEM_REQ + free(mem); +#endif +#ifdef P_FINI + P_FINI(&instance); +#endif ma_device_stop(&device); ma_device_uninit(&device); } +extern "C" JNIEXPORT jfloat JNICALL Java_com_orastron_@JNI_NAME@_MainActivity_nativeGetParameter(JNIEnv* env, jobject thiz, jint i) { - return 0.3f; + (void)env; + (void)thiz; + + mutex.lock(); + float v = paramValues[i]; + mutex.unlock(); + return v; } + +extern "C" JNIEXPORT void JNICALL Java_com_orastron_@JNI_NAME@_MainActivity_nativeSetParameter(JNIEnv* env, jobject thiz, jint i, jfloat v) { -} + (void)env; + (void)thiz; + mutex.lock(); + paramValues[i] = v; + mutex.unlock(); } diff --git a/examples/common/vst3/plugin.cpp b/examples/common/vst3/plugin.cpp index da28da7..e7ed6a8 100644 --- a/examples/common/vst3/plugin.cpp +++ b/examples/common/vst3/plugin.cpp @@ -199,7 +199,8 @@ tresult PLUGIN_API Plugin::initialize(FUnknown *context) { #if NUM_PARAMETERS != 0 for (int i = 0; i < NUM_PARAMETERS; i++) { parameters[i] = config_parameters[i].defaultValueUnmapped; - P_SET_PARAMETER(&instance, i, parameters[i]); + if (!config_parameters[i].out) + P_SET_PARAMETER(&instance, i, parameters[i]); } #endif diff --git a/examples/common/web/wrapper.c b/examples/common/web/wrapper.c index a18edbb..fcc2cba 100644 --- a/examples/common/web/wrapper.c +++ b/examples/common/web/wrapper.c @@ -82,7 +82,8 @@ wrapper wrapper_new(float sample_rate) { #if NUM_PARAMETERS != 0 for (int i = 0; i < NUM_PARAMETERS; i++) - wrapper_set_parameter(ret, i, config_parameters[i].defaultValueUnmapped); + if (!config_parameters[i].out) + wrapper_set_parameter(ret, i, config_parameters[i].defaultValueUnmapped); #endif P_SET_SAMPLE_RATE(&ret->instance, sample_rate); diff --git a/examples/fx_delay/android/Makefile b/examples/fx_delay/android/Makefile new file mode 100644 index 0000000..d9a41e0 --- /dev/null +++ b/examples/fx_delay/android/Makefile @@ -0,0 +1,6 @@ +ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) + +NAME := bw_example_fx_delay +SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_delay.c + +include ${ROOT_DIR}/../../common/android/android.mk diff --git a/examples/fx_reverb/android/Makefile b/examples/fx_reverb/android/Makefile new file mode 100644 index 0000000..6dd1fcf --- /dev/null +++ b/examples/fx_reverb/android/Makefile @@ -0,0 +1,6 @@ +ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) + +NAME := bw_example_fx_reverb +SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_reverb.c + +include ${ROOT_DIR}/../../common/android/android.mk diff --git a/examples/fx_reverb/src/config.h b/examples/fx_reverb/src/config.h index 9c9592d..9596998 100644 --- a/examples/fx_reverb/src/config.h +++ b/examples/fx_reverb/src/config.h @@ -51,12 +51,12 @@ struct config_parameter { #define COMPANY_MAILTO "mailto:info@orastron.com" #define PLUGIN_NAME "bw_example_fx_reverb" -#define PLUGIN_VERSION "0.5.0" +#define PLUGIN_VERSION "0.6.0" #define NUM_BUSES_IN 1 #define NUM_BUSES_OUT 1 -#define NUM_CHANNELS_IN 1 -#define NUM_CHANNELS_OUT 1 +#define NUM_CHANNELS_IN 2 +#define NUM_CHANNELS_OUT 2 static struct config_io_bus config_buses_in[NUM_BUSES_IN] = { { "Audio in", 0, 0, 0, IO_STEREO }