diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index fcefba66..baa80886 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -4,22 +4,31 @@ project(react-native-audio-context) set (CMAKE_VERBOSE_MAKEFILE ON) set (CMAKE_CXX_STANDARD 14) -add_library(react-native-audio-context - SHARED - ../cpp/JSIExampleHostObject.cpp - cpp-adapter.cpp -) +include(../node_modules/react-native/ReactAndroid/cmake-utils/folly-flags.cmake) +add_compile_options(${folly_FLAGS}) -# Specifies a path to native header files. include_directories( - ../cpp - ../node_modules/react-native/ReactCommon/jsi + ../cpp + src/main/cpp + ../node_modules/react-native/ReactCommon/jsi + ../node_modules/react-native/ReactAndroid/src/main/jni/react/jni + ../node_modules/react-native/ReactAndroid/src/main/jni/third-party/folly +) + +add_library(react-native-audio-context SHARED + src/main/cpp/OnLoad.cpp + src/main/cpp + ../cpp/AudioContextHostObject ) find_package(ReactAndroid REQUIRED CONFIG) +find_package(fbjni REQUIRED CONFIG) -target_link_libraries( - react-native-audio-context - ReactAndroid::jsi - android +target_link_libraries(react-native-audio-context + ReactAndroid::jsi + ReactAndroid::reactnativejni + fbjni::fbjni + ReactAndroid::folly_runtime + android + log ) diff --git a/android/build.gradle b/android/build.gradle index 431dd98a..65874cc2 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -72,6 +72,10 @@ android { } } + packagingOptions { + excludes = ["**/libc++_shared.so", "**/libfbjni.so", "**/libjsi.so", "**/libreactnativejni.so", "**/libfolly_json.so", "**/libreanimated.so", "**/libjscexecutor.so", "**/libhermes.so"] + } + externalNativeBuild { cmake { path "CMakeLists.txt" @@ -125,6 +129,7 @@ dependencies { //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" implementation 'androidx.core:core-ktx:1.13.1' + implementation 'com.facebook.fbjni:fbjni:0.6.0' } if (isNewArchitectureEnabled()) { diff --git a/android/cpp-adapter.cpp b/android/cpp-adapter.cpp deleted file mode 100644 index a7b0247d..00000000 --- a/android/cpp-adapter.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include "JSIExampleHostObject.h" - -using namespace facebook; - -void install(jsi::Runtime& runtime) { - auto hostObject = std::make_shared(); - auto object = jsi::Object::createFromHostObject(runtime, hostObject); - runtime.global().setProperty(runtime, "__JSIExampleProxy", std::move(object)); -} - -extern "C" -JNIEXPORT void JNICALL -Java_com_audiocontext_jsi_JSIExampleModule_00024Companion_nativeInstall(JNIEnv *env, jobject clazz, jlong jsiPtr) { - auto runtime = reinterpret_cast(jsiPtr); - if (runtime) { - install(*runtime); - } -} diff --git a/android/src/main/cpp/AudioContext.cpp b/android/src/main/cpp/AudioContext.cpp new file mode 100644 index 00000000..4ea2defe --- /dev/null +++ b/android/src/main/cpp/AudioContext.cpp @@ -0,0 +1,67 @@ +#include "AudioContext.h" +#include "AudioContextHostObject.h" +#include "OscillatorNode.h" +#include +#include + +namespace audiocontext { + + using namespace facebook::jni; + + AudioContext::AudioContext(jni::alias_ref &jThis, + jlong jsContext): javaObject_(make_global(jThis)) { + auto runtime = reinterpret_cast(jsContext); + auto hostObject = std::make_shared(this); + auto object = jsi::Object::createFromHostObject(*runtime, hostObject); + auto createOscillatorFunction = jsi::Function::createFromHostFunction( + *runtime, + jsi::PropNameID::forAscii(*runtime, "createOscillator"), + 0, + [this](jsi::Runtime &runtime, const jsi::Value &, const jsi::Value *, size_t) { + return jsiCreateOscillator(runtime); + }); + object.setProperty(*runtime, "createOscillator", std::move(createOscillatorFunction)); + runtime->global().setProperty(*runtime, "__AudioContextProxy", std::move(object)); + } + + // this method is called from Java to create a new instance of oscillator + jni::local_ref AudioContext::createOscillator() { + auto method = javaClassStatic()->getMethod()>("createOscillator"); + return method(javaObject_.get()); + } + + // this method is called in order to get a reference to c++ hybrid object of oscillator + //and create a host object for it + jsi::Value AudioContext::jsiCreateOscillator(jsi::Runtime &runtime) { + auto oscillatorHybridData = createOscillator(); + auto oscillatorNode = oscillatorHybridData->cthis();//error detail::HybridData does not have a member named 'cthis' + //cthis() is a method of HybridClass class that returns a pointer to the c++ object + + //HybridClass from Hybrid.h + //template + //class HybridClass : public detail::HybridTraits::CxxBase { + // public: + // struct JavaPart + // : JavaClass::JavaBase> { + // // At this point, T is incomplete, and so we cannot access + // // T::kJavaDescriptor directly. jtype_traits support this escape hatch for + // // such a case. + // static constexpr const char* kJavaDescriptor = nullptr; + // static constexpr auto /* detail::SimpleFixedString<_> */ + // get_instantiated_java_descriptor(); + // static constexpr auto /* detail::SimpleFixedString<_> */ + // get_instantiated_base_name(); + // + // using HybridType = T; + // + // // This will reach into the java object and extract the C++ instance from + // // the mHybridData and return it. + // T* cthis() const; + // + // friend class HybridClass; + // friend T; + // }; + return oscillatorNode->createOscillatorNodeHostObject(runtime); + } + +} // namespace audiocontext diff --git a/android/src/main/cpp/AudioContext.h b/android/src/main/cpp/AudioContext.h new file mode 100644 index 00000000..8365b3be --- /dev/null +++ b/android/src/main/cpp/AudioContext.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include +#include +#include "AudioContextHostObject.h" +#include "OscillatorNode.h" + +namespace audiocontext { + + using namespace facebook; + using namespace facebook::jni; + + class AudioContext : public jni::HybridClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/audiocontext/nodes/context/AudioContext;"; + + static jni::local_ref initHybrid(jni::alias_ref jThis, jlong jsContext) + { + return makeCxxInstance(jThis, jsContext); + } + + static void registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", AudioContext::initHybrid), + }); + } + + jni::local_ref createOscillator(); + jsi::Value jsiCreateOscillator(jsi::Runtime &runtime); + + private: + friend HybridBase; + + global_ref javaObject_; + std::shared_ptr runtime_; + + explicit AudioContext(jni::alias_ref& jThis, jlong jsContext); + }; + +} // namespace audiocontext diff --git a/android/src/main/cpp/OnLoad.cpp b/android/src/main/cpp/OnLoad.cpp new file mode 100644 index 00000000..93daacda --- /dev/null +++ b/android/src/main/cpp/OnLoad.cpp @@ -0,0 +1,13 @@ +#include +#include "OscillatorNode.h" +#include "AudioContext.h" + +using namespace audiocontext; + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) +{ + return facebook::jni::initialize(vm, [] { + OscillatorNode::registerNatives(); + AudioContext::registerNatives(); + }); +} diff --git a/android/src/main/cpp/OscillatorNode.cpp b/android/src/main/cpp/OscillatorNode.cpp new file mode 100644 index 00000000..d4e0156e --- /dev/null +++ b/android/src/main/cpp/OscillatorNode.cpp @@ -0,0 +1,27 @@ +#include "OscillatorNode.h" +#include +#include + +namespace audiocontext { + + using namespace facebook::jni; + + OscillatorNode::OscillatorNode(jni::alias_ref &jThis, jlong jsContext) + : javaObject_(make_global(jThis)), jsContext(jsContext){} + + jsi::Object OscillatorNode::createOscillatorNodeHostObject() { + auto runtime = reinterpret_cast(jsContext); + auto hostObject = std::make_shared(this); + return jsi::Object::createFromHostObject(*runtime, hostObject); + } + + void OscillatorNode::start() { + static const auto method = javaClassLocal()->getMethod("start"); + method(javaObject_.get()); + } + + void OscillatorNode::stop() { + static const auto method = javaClassLocal()->getMethod("stop"); + method(javaObject_.get()); + } +} // namespace audiocontext diff --git a/android/src/main/cpp/OscillatorNode.h b/android/src/main/cpp/OscillatorNode.h new file mode 100644 index 00000000..1751421e --- /dev/null +++ b/android/src/main/cpp/OscillatorNode.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include +#include "OscillatorNodeHostObject.h" + +namespace audiocontext { + + using namespace facebook; + using namespace facebook::jni; + + class OscillatorNode : public jni::HybridClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/audiocontext/nodes/oscillator/OscillatorNode;"; + + static jni::local_ref initHybrid(jni::alias_ref jThis, jlong jsContext) + { + return makeCxxInstance(jThis, jsContext); + } + + jsi::Object createOscillatorNodeHostObject(); + + static void registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", OscillatorNode::initHybrid), + }); + } + + void start(); + void stop(); + + private: + friend HybridBase; + + global_ref javaObject_; + jlong jsContext; + + explicit OscillatorNode(jni::alias_ref& jThis, jlong jsContext); + }; + +} // namespace audiocontext diff --git a/android/src/main/java/com/audiocontext/JSIExamplePackage.kt b/android/src/main/java/com/audiocontext/AudioContextPackage.kt similarity index 72% rename from android/src/main/java/com/audiocontext/JSIExamplePackage.kt rename to android/src/main/java/com/audiocontext/AudioContextPackage.kt index 81750b80..e4cb0d94 100644 --- a/android/src/main/java/com/audiocontext/JSIExamplePackage.kt +++ b/android/src/main/java/com/audiocontext/AudioContextPackage.kt @@ -1,14 +1,14 @@ package com.audiocontext -import com.audiocontext.jsi.JSIExampleModule +import com.audiocontext.nativemodules.AudioContextModule import com.facebook.react.ReactPackage import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.uimanager.ViewManager -class JSIExamplePackage : ReactPackage { +class AudioContextPackage : ReactPackage { override fun createNativeModules(reactContext: ReactApplicationContext): List { - return listOf(JSIExampleModule(reactContext)) + return listOf(AudioContextModule(reactContext)) } override fun createViewManagers(reactContext: ReactApplicationContext): List> { diff --git a/android/src/main/java/com/audiocontext/context/AudioContext.kt b/android/src/main/java/com/audiocontext/context/AudioContext.kt new file mode 100644 index 00000000..fd85f7a0 --- /dev/null +++ b/android/src/main/java/com/audiocontext/context/AudioContext.kt @@ -0,0 +1,49 @@ +package com.audiocontext.context + +import android.media.AudioTrack +import com.audiocontext.nodes.AudioDestinationNode +import com.audiocontext.nodes.AudioNode +import com.audiocontext.nodes.oscillator.OscillatorNode +import com.facebook.jni.HybridData +import com.facebook.react.bridge.ReactApplicationContext +import java.util.concurrent.CopyOnWriteArrayList + +class AudioContext(private val reactContext: ReactApplicationContext) : BaseAudioContext { + override var sampleRate: Int = 44100 + override val destination: AudioDestinationNode = AudioDestinationNode(this) + override val sources = CopyOnWriteArrayList() + + private val mHybridData: HybridData?; + + companion object { + init { + System.loadLibrary("react-native-audio-context") + } + } + + init { + mHybridData = initHybrid(reactContext.javaScriptContextHolder!!.get()) + } + + external fun initHybrid(l: Long): HybridData? + + private fun addNode(node: AudioNode) { + sources.add(node) + } + + fun createOscillator(): HybridData? { + val oscillator = OscillatorNode(this, reactContext) + addNode(oscillator) + return oscillator.getHybridData() + } + + override fun dispatchAudio(buffer: ShortArray, audioTrack: AudioTrack) { + val currentBuffer = buffer.clone() + + synchronized(sources) { + sources.forEach { source -> + source.process(currentBuffer, audioTrack) + } + } + } +} diff --git a/android/src/main/java/com/audiocontext/context/BaseAudioContext.kt b/android/src/main/java/com/audiocontext/context/BaseAudioContext.kt new file mode 100644 index 00000000..7685323a --- /dev/null +++ b/android/src/main/java/com/audiocontext/context/BaseAudioContext.kt @@ -0,0 +1,15 @@ +package com.audiocontext.context + +import android.media.AudioTrack +import com.audiocontext.nodes.AudioDestinationNode +import com.audiocontext.nodes.AudioNode +import com.audiocontext.nodes.oscillator.OscillatorNode +import com.facebook.jni.HybridData + +interface BaseAudioContext { + val sampleRate: Int + val destination: AudioDestinationNode + val sources: List + + abstract fun dispatchAudio(buffer: ShortArray, audioTrack: AudioTrack) +} diff --git a/android/src/main/java/com/audiocontext/jsi/JSIExampleModule.kt b/android/src/main/java/com/audiocontext/jsi/JSIExampleModule.kt deleted file mode 100644 index 371c2886..00000000 --- a/android/src/main/java/com/audiocontext/jsi/JSIExampleModule.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.audiocontext.jsi - -import android.util.Log -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactContextBaseJavaModule -import com.facebook.react.bridge.ReactMethod -import com.facebook.react.module.annotations.ReactModule - -@ReactModule(name = JSIExampleModule.NAME) -class JSIExampleModule(reactContext: ReactApplicationContext?) : - ReactContextBaseJavaModule(reactContext) { - override fun getName(): String { - return NAME - } - - @ReactMethod(isBlockingSynchronousMethod = true) - fun install(): Boolean { - try { - System.loadLibrary("react-native-audio-context") - - val jsContext = reactApplicationContext.javaScriptContextHolder - - nativeInstall(jsContext!!.get()) - return true - } catch (exception: Exception) { - Log.e(NAME, "Failed to install JSI Bindings for react-native-audio-context", exception) - return false - } - } - - companion object { - const val NAME: String = "JSIExample" - - private external fun nativeInstall(jsiPtr: Long) - } -} diff --git a/android/src/main/java/com/audiocontext/nativemodules/AudioContextModule.kt b/android/src/main/java/com/audiocontext/nativemodules/AudioContextModule.kt new file mode 100644 index 00000000..5d3cf64f --- /dev/null +++ b/android/src/main/java/com/audiocontext/nativemodules/AudioContextModule.kt @@ -0,0 +1,21 @@ +package com.audiocontext.nativemodules + +import com.audiocontext.context.AudioContext +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactMethod + +class AudioContextModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { + private val audioContext = AudioContext(reactContext) + private val destination = audioContext.destination + + override fun getName(): String { + return "AudioContextModule" + } + + @ReactMethod(isBlockingSynchronousMethod = true) + fun createOscillator() { + val oscillator = audioContext.createOscillator() + //oscillator.connect(destination) + } +} diff --git a/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt b/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt new file mode 100644 index 00000000..96c121a0 --- /dev/null +++ b/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt @@ -0,0 +1,14 @@ +package com.audiocontext.nodes + +import android.media.AudioTrack +import com.audiocontext.context.BaseAudioContext + + +class AudioDestinationNode(context: BaseAudioContext): AudioNode(context) { + override val numberOfInputs = 1 + override val numberOfOutputs = 0 + + override fun process(buffer: ShortArray, audioTrack: AudioTrack) { + audioTrack.write(buffer, 0, buffer.size) + } +} diff --git a/android/src/main/java/com/audiocontext/nodes/AudioNode.kt b/android/src/main/java/com/audiocontext/nodes/AudioNode.kt new file mode 100644 index 00000000..9c68beaa --- /dev/null +++ b/android/src/main/java/com/audiocontext/nodes/AudioNode.kt @@ -0,0 +1,25 @@ +package com.audiocontext.nodes + +import android.media.AudioTrack +import com.audiocontext.context.BaseAudioContext + + +abstract class AudioNode(val context: BaseAudioContext) { + abstract val numberOfInputs: Int; + abstract val numberOfOutputs: Int; + private val connectedNodes = mutableListOf() + + fun connect(destination: AudioNode) { + if(this.numberOfOutputs > 0) { + connectedNodes.add(destination) + } + } + + fun disconnect() { + connectedNodes.clear() + } + + open fun process(buffer: ShortArray, audioTrack: AudioTrack) { + connectedNodes.forEach { it.process(buffer, audioTrack) } + } +} diff --git a/android/src/main/java/com/audiocontext/nodes/AudioScheduledSourceNode.kt b/android/src/main/java/com/audiocontext/nodes/AudioScheduledSourceNode.kt new file mode 100644 index 00000000..b2fcc5d6 --- /dev/null +++ b/android/src/main/java/com/audiocontext/nodes/AudioScheduledSourceNode.kt @@ -0,0 +1,8 @@ +package com.audiocontext.nodes + +import com.audiocontext.context.BaseAudioContext + +abstract class AudioScheduledSourceNode(context: BaseAudioContext) : AudioNode(context) { + abstract fun start() + abstract fun stop() +} diff --git a/android/src/main/java/com/audiocontext/nodes/oscillator/OscillatorNode.kt b/android/src/main/java/com/audiocontext/nodes/oscillator/OscillatorNode.kt new file mode 100644 index 00000000..a7ec86d2 --- /dev/null +++ b/android/src/main/java/com/audiocontext/nodes/oscillator/OscillatorNode.kt @@ -0,0 +1,87 @@ +package com.audiocontext.nodes.oscillator + +import android.media.AudioFormat +import android.media.AudioManager +import android.media.AudioTrack +import com.audiocontext.context.BaseAudioContext +import com.audiocontext.nodes.AudioScheduledSourceNode +import com.facebook.jni.HybridData +import com.facebook.react.bridge.ReactApplicationContext +import kotlin.math.abs +import kotlin.math.floor +import kotlin.math.sin + +class OscillatorNode(context: BaseAudioContext, reactContext: ReactApplicationContext) : AudioScheduledSourceNode(context) { + override val numberOfInputs: Int = 0 + override val numberOfOutputs: Int = 1 + private var frequency: Double = 440.0 + private var detune: Double = 0.0 + private var waveType: WaveType = WaveType.SINE + + private val audioTrack: AudioTrack + @Volatile private var isPlaying: Boolean = false + private var playbackThread: Thread? = null + private var buffer: ShortArray = ShortArray(1024) + + private val mHybridData: HybridData?; + + companion object { + init { + System.loadLibrary("react-native-audio-context") + } + } + + init { + mHybridData = initHybrid(reactContext.javaScriptContextHolder!!.get()) + + val bufferSize = AudioTrack.getMinBufferSize( + context.sampleRate, + AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT) + this.audioTrack = AudioTrack( + AudioManager.STREAM_MUSIC, context.sampleRate, AudioFormat.CHANNEL_OUT_MONO, + AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM + ) + } + + external fun initHybrid(l: Long): HybridData? + + fun getHybridData(): HybridData? { + return mHybridData + } + + override fun start() { + if(isPlaying) return + isPlaying = true + audioTrack.play() + playbackThread = Thread { generateSound() }.apply{ start()} + } + + override fun stop() { + if(!isPlaying) return + isPlaying = false + audioTrack.stop() + playbackThread?.join() + } + + private fun generateSound() { + var wavePhase = 0.0 + var phaseChange: Double + + while(isPlaying) { + phaseChange = 2 * Math.PI * (frequency + detune) / context.sampleRate + + for(i in buffer.indices) { + buffer[i] = when(waveType) { + WaveType.SINE -> (sin(wavePhase) * Short.MAX_VALUE).toInt().toShort() + WaveType.SQUARE -> ((if (sin(wavePhase) >= 0) 1 else -1) * Short.MAX_VALUE).toShort() + WaveType.SAWTOOTH -> ((2 * (wavePhase / (2 * Math.PI) - floor(wavePhase / (2 * Math.PI) + 0.5))) * Short.MAX_VALUE).toInt().toShort() + WaveType.TRIANGLE -> ((2 * abs(2 * (wavePhase / (2 * Math.PI) - floor(wavePhase / (2 * Math.PI) + 0.5))) - 1) * Short.MAX_VALUE).toInt().toShort() + } + wavePhase += phaseChange + } + + context.dispatchAudio(buffer, audioTrack) + } + audioTrack.flush() + } +} diff --git a/android/src/main/java/com/audiocontext/nodes/oscillator/WaveType.kt b/android/src/main/java/com/audiocontext/nodes/oscillator/WaveType.kt new file mode 100644 index 00000000..ffd63ff2 --- /dev/null +++ b/android/src/main/java/com/audiocontext/nodes/oscillator/WaveType.kt @@ -0,0 +1,8 @@ +package com.audiocontext.nodes.oscillator + +enum class WaveType { + SINE, + SQUARE, + SAWTOOTH, + TRIANGLE +} diff --git a/cpp/AudioContextHostObject.cpp b/cpp/AudioContextHostObject.cpp new file mode 100644 index 00000000..ca9d6de7 --- /dev/null +++ b/cpp/AudioContextHostObject.cpp @@ -0,0 +1,23 @@ +#include "AudioContextHostObject.h" + +namespace audiocontext { + using namespace facebook; + + std::vector AudioContextHostObject::getPropertyNames(jsi::Runtime& runtime) { + std::vector propertyNames; + propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "createOscillator")); + return propertyNames; + } + + jsi::Value AudioContextHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propNameId) { + auto propName = propNameId.utf8(runtime); + + throw std::runtime_error("Prop not yet implemented!"); + } + + void AudioContextHostObject::set(jsi::Runtime& runtime, const jsi::PropNameID& propNameId, const jsi::Value& value) { + auto propName = propNameId.utf8(runtime); + + throw std::runtime_error("Not yet implemented!"); + } +} diff --git a/cpp/AudioContextHostObject.h b/cpp/AudioContextHostObject.h new file mode 100644 index 00000000..c18aa910 --- /dev/null +++ b/cpp/AudioContextHostObject.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include +#include "AudioContext.h" + +namespace audiocontext { + using namespace facebook; + + class AudioContext; + + class AudioContextHostObject : public jsi::HostObject { + private: + AudioContext* audiocontext_; + + public: + explicit AudioContextHostObject(AudioContext* audiocontext) : audiocontext_(audiocontext) {} + + jsi::Value get(jsi::Runtime& runtime, const jsi::PropNameID& name) override; + void set(jsi::Runtime& runtime, const jsi::PropNameID& name, const jsi::Value& value) override; + std::vector getPropertyNames(jsi::Runtime& rt) override; + }; +} // namespace audiocontext diff --git a/cpp/JSIExampleHostObject.cpp b/cpp/JSIExampleHostObject.cpp deleted file mode 100644 index 1f28c9e6..00000000 --- a/cpp/JSIExampleHostObject.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "JSIExampleHostObject.h" -#include - -namespace example { - using namespace facebook; - - std::vector JSIExampleHostObject::getPropertyNames(jsi::Runtime &runtime) - { - std::vector propertyNames; - propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "helloWorld")); - return propertyNames; - } - - jsi::Value JSIExampleHostObject::get(jsi::Runtime &runtime, const jsi::PropNameID &propNameId) - { - auto propName = propNameId.utf8(runtime); - - if (propName == "helloWorld") - { - return jsi::Function::createFromHostFunction(runtime, propNameId, 0, - [this](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *, size_t) - { - return this->helloWorld(rt); - }); - } - - throw std::runtime_error("Not yet implemented!"); - } - - void JSIExampleHostObject::set(jsi::Runtime &runtime, const jsi::PropNameID &propNameId, const jsi::Value &value) - { - auto propName = propNameId.utf8(runtime); - if (propName == "helloWorld") - { - // Do nothing - return; - } - throw std::runtime_error("Not yet implemented!"); - } - - jsi::Value JSIExampleHostObject::helloWorld(jsi::Runtime &runtime) - { - return jsi::String::createFromUtf8(runtime, "Hello World using jsi::HostObject!"); - } -} diff --git a/cpp/JSIExampleHostObject.h b/cpp/JSIExampleHostObject.h deleted file mode 100644 index 3ec12423..00000000 --- a/cpp/JSIExampleHostObject.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef JSIEXAMPLEHOSTOBJECT_H -#define JSIEXAMPLEHOSTOBJECT_H - -#include - -namespace example -{ - - using namespace facebook; - - class JSI_EXPORT JSIExampleHostObject : public jsi::HostObject - { - public: - explicit JSIExampleHostObject() = default; - - public: - jsi::Value get(jsi::Runtime &, const jsi::PropNameID &name) override; - void set(jsi::Runtime &, const jsi::PropNameID &name, const jsi::Value &value) override; - std::vector getPropertyNames(jsi::Runtime &rt) override; - static jsi::Value helloWorld(jsi::Runtime &); - }; - -} // namespace margelo - -#endif /* JSIEXAMPLEHOSTOBJECT_H */ diff --git a/cpp/OscillatorNodeHostObject.cpp b/cpp/OscillatorNodeHostObject.cpp new file mode 100644 index 00000000..c3ed82b6 --- /dev/null +++ b/cpp/OscillatorNodeHostObject.cpp @@ -0,0 +1,38 @@ +#include "OscillatorNodeHostObject.h" + +namespace audiocontext { + using namespace facebook; + + std::vector OscillatorNodeHostObject::getPropertyNames(jsi::Runtime& runtime) { + std::vector propertyNames; + propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "start")); + propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "stop")); + return propertyNames; + } + + jsi::Value OscillatorNodeHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propNameId) { + auto propName = propNameId.utf8(runtime); + + if (propName == "start") { + return jsi::Function::createFromHostFunction(runtime, propNameId, 0, [this](jsi::Runtime& rt, const jsi::Value& thisValue, const jsi::Value* args, size_t count) -> jsi::Value { + oscillator_->start(); + return jsi::Value::undefined(); + }); + } + + if (propName == "stop") { + return jsi::Function::createFromHostFunction(runtime, propNameId, 0, [this](jsi::Runtime& rt, const jsi::Value& thisValue, const jsi::Value* args, size_t count) -> jsi::Value { + oscillator_->stop(); + return jsi::Value::undefined(); + }); + } + + throw std::runtime_error("Prop not yet implemented!"); + } + + void OscillatorNodeHostObject::set(jsi::Runtime& runtime, const jsi::PropNameID& propNameId, const jsi::Value& value) { + auto propName = propNameId.utf8(runtime); + + throw std::runtime_error("Not yet implemented!"); + } +} diff --git a/cpp/OscillatorNodeHostObject.h b/cpp/OscillatorNodeHostObject.h new file mode 100644 index 00000000..40680352 --- /dev/null +++ b/cpp/OscillatorNodeHostObject.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include +#include "OscillatorNode.h" + +namespace audiocontext { + using namespace facebook; + + class OscillatorNode; + + class OscillatorNodeHostObject : public jsi::HostObject { + private: + OscillatorNode* oscillator_; + + public: + explicit OscillatorNodeHostObject(OscillatorNode* oscillator) : oscillator_(oscillator) {} + + jsi::Value get(jsi::Runtime& runtime, const jsi::PropNameID& name) override; + void set(jsi::Runtime& runtime, const jsi::PropNameID& name, const jsi::Value& value) override; + std::vector getPropertyNames(jsi::Runtime& rt) override; + }; +} // namespace audiocontext diff --git a/example/src/App.tsx b/example/src/App.tsx index 479543b5..14d5ba4f 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,45 +1,41 @@ -import { StyleSheet, View, Text } from 'react-native'; -import JSIExample from '../../src/JSIExample/JSIExample'; +/* eslint-disable react/react-in-jsx-scope */ +import { Button, StyleSheet, Text, View } from 'react-native'; +import { AudioContext } from 'react-native-audio-context'; +import type { OscillatorWrapper } from 'react-native-audio-context'; -export default function App() { - const sayHello = () => { - //JSIExample.helloWorld = 'Hello World'; - return JSIExample.helloWorld(); +const App = () => { + AudioContext.createOscillator(); + const oscillator = global.__OscillatorNodeProxy as OscillatorWrapper; + + const startOscillator = () => { + oscillator.start(); + }; + + const stopOscillator = () => { + oscillator.stop(); }; return ( - {sayHello()} + React Native Oscillator +