diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index f1c7b96d..6b4d9612 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -19,8 +19,10 @@ add_library(react-native-audio-context SHARED src/main/cpp/OnLoad.cpp src/main/cpp/AudioContext src/main/cpp/OscillatorNode + src/main/cpp/AudioDestinationNode ../cpp/AudioContextHostObject ../cpp/OscillatorNodeHostObject + ../cpp/AudioDestinationNodeHostObject ) find_package(ReactAndroid REQUIRED CONFIG) diff --git a/android/src/main/cpp/AudioContext.cpp b/android/src/main/cpp/AudioContext.cpp index 731d04a2..157ea32a 100644 --- a/android/src/main/cpp/AudioContext.cpp +++ b/android/src/main/cpp/AudioContext.cpp @@ -1,28 +1,35 @@ #include "AudioContext.h" -#include "AudioContextHostObject.h" -#include "OscillatorNode.h" -#include -#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); - runtime->global().setProperty(*runtime, "__AudioContextProxy", std::move(object)); - } - - jsi::Object AudioContext::createOscillator() { - static const auto method = javaClassLocal()->getMethod("createOscillator"); - auto oscillator = method(javaObject_.get()); - auto oscillatorCppInstance = oscillator->cthis(); - - return oscillatorCppInstance->createOscillatorNodeHostObject(); - } + +namespace audiocontext +{ + + using namespace facebook::jni; + + AudioContext::AudioContext(jni::alias_ref &jThis, + jlong jsContext) : javaObject_(make_global(jThis)), jsContext_(jsContext) + { + auto runtime = reinterpret_cast(jsContext); + auto hostObject = std::make_shared(this); + auto object = jsi::Object::createFromHostObject(*runtime, hostObject); + runtime->global().setProperty(*runtime, "__AudioContextProxy", std::move(object)); + } + + jsi::Object AudioContext::createOscillator() + { + static const auto method = javaClassLocal()->getMethod("createOscillator"); + auto oscillator = method(javaObject_.get()); + auto oscillatorCppInstance = oscillator->cthis(); + + return oscillatorCppInstance->createOscillatorNodeHostObject(); + } + + jsi::Object AudioContext::getDestination() + { + static const auto method = javaClassLocal()->getMethod("getDestination"); + auto destination = method(javaObject_.get()); + auto destinationCppInstance = destination->cthis(); + + return destinationCppInstance->createAudioDestinationHostObject(); + } } // namespace audiocontext diff --git a/android/src/main/cpp/AudioContext.h b/android/src/main/cpp/AudioContext.h index d50a1775..dad880fe 100644 --- a/android/src/main/cpp/AudioContext.h +++ b/android/src/main/cpp/AudioContext.h @@ -6,36 +6,41 @@ #include #include "AudioContextHostObject.h" #include "OscillatorNode.h" +#include "AudioDestinationNode.h" -namespace audiocontext { +namespace audiocontext +{ - using namespace facebook; - using namespace facebook::jni; + using namespace facebook; + using namespace facebook::jni; - class AudioContext : public jni::HybridClass { - public: - static auto constexpr kJavaDescriptor = "Lcom/audiocontext/context/AudioContext;"; + class AudioContext : public jni::HybridClass + { + public: + static auto constexpr kJavaDescriptor = "Lcom/audiocontext/context/AudioContext;"; - static jni::local_ref initHybrid(jni::alias_ref jThis, jlong jsContext) - { - return makeCxxInstance(jThis, jsContext); - } + static jni::local_ref initHybrid(jni::alias_ref jThis, jlong jsContext) + { + return makeCxxInstance(jThis, jsContext); + } - static void registerNatives() { - registerHybrid({ - makeNativeMethod("initHybrid", AudioContext::initHybrid), - }); - } + static void registerNatives() + { + registerHybrid({ + makeNativeMethod("initHybrid", AudioContext::initHybrid), + }); + } - jsi::Object createOscillator(); + jsi::Object createOscillator(); + jsi::Object getDestination(); - private: - friend HybridBase; + private: + friend HybridBase; - global_ref javaObject_; - std::shared_ptr runtime_; + global_ref javaObject_; + jlong jsContext_; - explicit AudioContext(jni::alias_ref& jThis, jlong jsContext); - }; + explicit AudioContext(jni::alias_ref &jThis, jlong jsContext); + }; } // namespace audiocontext diff --git a/android/src/main/cpp/AudioDestinationNode.cpp b/android/src/main/cpp/AudioDestinationNode.cpp new file mode 100644 index 00000000..8fb200e7 --- /dev/null +++ b/android/src/main/cpp/AudioDestinationNode.cpp @@ -0,0 +1,15 @@ +#include "AudioDestinationNode.h" + +namespace audiocontext { + + using namespace facebook::jni; + + AudioDestinationNode::AudioDestinationNode(jni::alias_ref &jThis, + jlong jsContext): javaObject_(make_global(jThis)), jsContext_(jsContext) {} + + jsi::Object AudioDestinationNode::createAudioDestinationHostObject() { + auto runtime = reinterpret_cast(jsContext_); + auto hostObject = std::make_shared(this); + return jsi::Object::createFromHostObject(*runtime, hostObject); + } +} // namespace audiocontext diff --git a/android/src/main/cpp/AudioDestinationNode.h b/android/src/main/cpp/AudioDestinationNode.h new file mode 100644 index 00000000..033b1e37 --- /dev/null +++ b/android/src/main/cpp/AudioDestinationNode.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include +#include +#include "AudioDestinationNodeHostObject.h" +#include "OscillatorNode.h" + +namespace audiocontext { + + using namespace facebook; + using namespace facebook::jni; + + class OscillatorNode; + + class AudioDestinationNode : public jni::HybridClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/audiocontext/nodes/AudioDestinationNode;"; + + static jni::local_ref initHybrid(jni::alias_ref jThis, jlong jsContext) + { + return makeCxxInstance(jThis, jsContext); + } + + static void registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", AudioDestinationNode::initHybrid), + }); + } + + jsi::Object createAudioDestinationHostObject(); + + private: + friend HybridBase; + friend class OscillatorNode; + + global_ref javaObject_; + jlong jsContext_{}; + + explicit AudioDestinationNode(jni::alias_ref& jThis, jlong jsContext); + }; + +} // namespace audiocontext diff --git a/android/src/main/cpp/OnLoad.cpp b/android/src/main/cpp/OnLoad.cpp index 93daacda..a7e45f89 100644 --- a/android/src/main/cpp/OnLoad.cpp +++ b/android/src/main/cpp/OnLoad.cpp @@ -1,6 +1,7 @@ #include #include "OscillatorNode.h" #include "AudioContext.h" +#include "AudioDestinationNode.h" using namespace audiocontext; @@ -9,5 +10,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) return facebook::jni::initialize(vm, [] { OscillatorNode::registerNatives(); AudioContext::registerNatives(); + AudioDestinationNode::registerNatives(); }); } diff --git a/android/src/main/cpp/OscillatorNode.cpp b/android/src/main/cpp/OscillatorNode.cpp index 5e3899b3..5d7ca83a 100644 --- a/android/src/main/cpp/OscillatorNode.cpp +++ b/android/src/main/cpp/OscillatorNode.cpp @@ -1,17 +1,14 @@ #include "OscillatorNode.h" -#include -#include -#include namespace audiocontext { using namespace facebook::jni; OscillatorNode::OscillatorNode(jni::alias_ref &jThis, jlong jsContext) - : javaObject_(make_global(jThis)), jsContext(jsContext){} + : javaObject_(make_global(jThis)), jsContext_(jsContext){} jsi::Object OscillatorNode::createOscillatorNodeHostObject() { - auto runtime = reinterpret_cast(jsContext); + auto runtime = reinterpret_cast(jsContext_); auto hostObject = std::make_shared(this); return jsi::Object::createFromHostObject(*runtime, hostObject); } @@ -45,4 +42,9 @@ namespace audiocontext { static const auto method = javaClassStatic()->getMethod("getDetune"); return method(javaObject_.get()); } + + void OscillatorNode::connect(const AudioDestinationNode &destination) { + const auto method = javaClassLocal()->getMethod("connect"); + method(javaObject_.get(), destination.javaObject_.get()); + } } // namespace audiocontext diff --git a/android/src/main/cpp/OscillatorNode.h b/android/src/main/cpp/OscillatorNode.h index 8471ab17..8a86dbd3 100644 --- a/android/src/main/cpp/OscillatorNode.h +++ b/android/src/main/cpp/OscillatorNode.h @@ -5,12 +5,15 @@ #include #include #include "OscillatorNodeHostObject.h" +#include "AudioDestinationNode.h" namespace audiocontext { using namespace facebook; using namespace facebook::jni; + class AudioDestinationNode; + class OscillatorNode : public jni::HybridClass { public: static auto constexpr kJavaDescriptor = "Lcom/audiocontext/nodes/oscillator/OscillatorNode;"; @@ -32,6 +35,7 @@ namespace audiocontext { void setDetune(jdouble detune); jdouble getFrequency(); jdouble getDetune(); + void connect(const AudioDestinationNode &destination); jsi::Object createOscillatorNodeHostObject(); @@ -39,7 +43,7 @@ namespace audiocontext { friend HybridBase; global_ref javaObject_; - jlong jsContext; + jlong jsContext_; explicit OscillatorNode(jni::alias_ref& jThis, jlong jsContext); }; diff --git a/android/src/main/java/com/audiocontext/context/AudioContext.kt b/android/src/main/java/com/audiocontext/context/AudioContext.kt index 86f02201..1af5631b 100644 --- a/android/src/main/java/com/audiocontext/context/AudioContext.kt +++ b/android/src/main/java/com/audiocontext/context/AudioContext.kt @@ -9,9 +9,9 @@ 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() + override val sampleRate: Int = 44100 + override val destination: AudioDestinationNode = AudioDestinationNode(this, reactContext) + get() = field private val mHybridData: HybridData?; @@ -27,14 +27,7 @@ class AudioContext(private val reactContext: ReactApplicationContext) : BaseAudi external fun initHybrid(l: Long): HybridData? - private fun addNode(node: AudioNode) { - sources.add(node) - } - override fun createOscillator(): OscillatorNode { - val oscillator = OscillatorNode(this, reactContext) - oscillator.connect(destination) - addNode(oscillator) - return oscillator + return OscillatorNode(this, reactContext) } } diff --git a/android/src/main/java/com/audiocontext/context/BaseAudioContext.kt b/android/src/main/java/com/audiocontext/context/BaseAudioContext.kt index 80175c5d..4c73c62c 100644 --- a/android/src/main/java/com/audiocontext/context/BaseAudioContext.kt +++ b/android/src/main/java/com/audiocontext/context/BaseAudioContext.kt @@ -9,7 +9,6 @@ import com.facebook.jni.HybridData interface BaseAudioContext { val sampleRate: Int val destination: AudioDestinationNode - val sources: List abstract fun createOscillator(): OscillatorNode } diff --git a/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt b/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt index 96c121a0..1542baa7 100644 --- a/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt +++ b/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt @@ -2,12 +2,28 @@ package com.audiocontext.nodes import android.media.AudioTrack import com.audiocontext.context.BaseAudioContext +import com.facebook.jni.HybridData +import com.facebook.react.bridge.ReactApplicationContext -class AudioDestinationNode(context: BaseAudioContext): AudioNode(context) { +class AudioDestinationNode(context: BaseAudioContext, reactContext: ReactApplicationContext): AudioNode(context) { override val numberOfInputs = 1 override val numberOfOutputs = 0 + 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? + 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 index 9c68beaa..cba69f5f 100644 --- a/android/src/main/java/com/audiocontext/nodes/AudioNode.kt +++ b/android/src/main/java/com/audiocontext/nodes/AudioNode.kt @@ -9,7 +9,7 @@ abstract class AudioNode(val context: BaseAudioContext) { abstract val numberOfOutputs: Int; private val connectedNodes = mutableListOf() - fun connect(destination: AudioNode) { + fun connect(destination: AudioDestinationNode) { if(this.numberOfOutputs > 0) { connectedNodes.add(destination) } diff --git a/android/src/main/java/com/audiocontext/nodes/oscillator/OscillatorNode.kt b/android/src/main/java/com/audiocontext/nodes/oscillator/OscillatorNode.kt index f05373a4..2600872d 100644 --- a/android/src/main/java/com/audiocontext/nodes/oscillator/OscillatorNode.kt +++ b/android/src/main/java/com/audiocontext/nodes/oscillator/OscillatorNode.kt @@ -16,7 +16,15 @@ class OscillatorNode(context: BaseAudioContext, reactContext: ReactApplicationCo override val numberOfInputs: Int = 0 override val numberOfOutputs: Int = 1 private var frequency: Double = 440.0 + get() = field + set(value) { + field = value + } private var detune: Double = 0.0 + get() = field + set(value) { + field = value + } private var waveType: WaveType = WaveType.SINE private val audioTrack: AudioTrack @@ -45,22 +53,6 @@ class OscillatorNode(context: BaseAudioContext, reactContext: ReactApplicationCo external fun initHybrid(l: Long): HybridData? - fun getFrequency(): Double { - return frequency - } - - fun getDetune(): Double { - return detune - } - - fun setFrequency(frequency: Double) { - this.frequency = frequency - } - - fun setDetune(detune: Double) { - this.detune = detune - } - override fun start() { if(isPlaying) { return @@ -89,32 +81,11 @@ class OscillatorNode(context: BaseAudioContext, reactContext: ReactApplicationCo phaseChange = 2 * Math.PI * (frequency + detune) / context.sampleRate for(i in buffer.indices) { - buffer[i] = when(waveType) { - WaveType.SINE -> sineWaveBuffer(wavePhase) - WaveType.SQUARE -> squareWaveBuffer(wavePhase) - WaveType.SAWTOOTH -> sawtoothWaveBuffer(wavePhase) - WaveType.TRIANGLE -> triangleWaveBuffer(wavePhase) - } + buffer[i] = WaveType.getWaveBufferElement(wavePhase, waveType) wavePhase += phaseChange } process(buffer, audioTrack) } audioTrack.flush() } - - private fun sineWaveBuffer(wavePhase: Double): Short { - return (sin(wavePhase) * Short.MAX_VALUE).toInt().toShort() - } - - private fun squareWaveBuffer(wavePhase: Double): Short { - return ((if (sin(wavePhase) >= 0) 1 else -1) * Short.MAX_VALUE).toShort() - } - - private fun sawtoothWaveBuffer(wavePhase: Double): Short { - return ((2 * (wavePhase / (2 * Math.PI) - floor(wavePhase / (2 * Math.PI) + 0.5))) * Short.MAX_VALUE).toInt().toShort() - } - - private fun triangleWaveBuffer(wavePhase: Double): Short { - return ((2 * abs(2 * (wavePhase / (2 * Math.PI) - floor(wavePhase / (2 * Math.PI) + 0.5))) - 1) * Short.MAX_VALUE).toInt().toShort() - } } diff --git a/android/src/main/java/com/audiocontext/nodes/oscillator/WaveType.kt b/android/src/main/java/com/audiocontext/nodes/oscillator/WaveType.kt index 5d506dca..aa666460 100644 --- a/android/src/main/java/com/audiocontext/nodes/oscillator/WaveType.kt +++ b/android/src/main/java/com/audiocontext/nodes/oscillator/WaveType.kt @@ -1,11 +1,40 @@ package com.audiocontext.nodes.oscillator +import kotlin.math.abs +import kotlin.math.floor +import kotlin.math.sin + enum class WaveType { SINE, SQUARE, SAWTOOTH, TRIANGLE; + private fun sineWave(wavePhase: Double): Short { + return (sin(wavePhase) * Short.MAX_VALUE).toInt().toShort() + } + + private fun squareWave(wavePhase: Double): Short { + return ((if (sin(wavePhase) >= 0) 1 else -1) * Short.MAX_VALUE).toShort() + } + + private fun sawtoothWave(wavePhase: Double): Short { + return ((2 * (wavePhase / (2 * Math.PI) - floor(wavePhase / (2 * Math.PI) + 0.5))) * Short.MAX_VALUE).toInt().toShort() + } + + private fun triangleWave(wavePhase: Double): Short { + return ((2 * abs(2 * (wavePhase / (2 * Math.PI) - floor(wavePhase / (2 * Math.PI) + 0.5))) - 1) * Short.MAX_VALUE).toInt().toShort() + } + + private fun getWaveValue(wavePhase: Double): Short { + return when(this) { + SINE -> sineWave(wavePhase) + SQUARE -> squareWave(wavePhase) + SAWTOOTH -> sawtoothWave(wavePhase) + TRIANGLE -> triangleWave(wavePhase) + } + } + companion object { fun fromString(type: String): WaveType { return when (type.uppercase()) { @@ -16,5 +45,9 @@ enum class WaveType { else -> throw IllegalArgumentException("Unknown wave type: $type") } } + + fun getWaveBufferElement(wavePhase: Double, waveType: WaveType): Short { + return waveType.getWaveValue(wavePhase) + } } } diff --git a/cpp/AudioContextHostObject.cpp b/cpp/AudioContextHostObject.cpp index 6a16841b..371df9e1 100644 --- a/cpp/AudioContextHostObject.cpp +++ b/cpp/AudioContextHostObject.cpp @@ -6,6 +6,7 @@ namespace audiocontext { std::vector AudioContextHostObject::getPropertyNames(jsi::Runtime& runtime) { std::vector propertyNames; propertyNames.push_back(jsi::PropNameID::forUtf8(runtime, "createOscillator")); + propertyNames.push_back(jsi::PropNameID::forUtf8(runtime, "destination")); return propertyNames; } @@ -16,6 +17,10 @@ namespace audiocontext { return createOscillator(runtime, propNameId); } + if(propName == "destination") { + return getDestination(runtime, propNameId); + } + throw std::runtime_error("Not yet implemented!"); } @@ -32,4 +37,9 @@ namespace audiocontext { }); } + jsi::Value AudioContextHostObject::getDestination(jsi::Runtime &runtime, const jsi::PropNameID &propNameId) { + return jsi::Function::createFromHostFunction(runtime, propNameId, 0, [this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value { + return audiocontext_->getDestination(); + }); + } } diff --git a/cpp/AudioContextHostObject.h b/cpp/AudioContextHostObject.h index af9e07fd..3485b364 100644 --- a/cpp/AudioContextHostObject.h +++ b/cpp/AudioContextHostObject.h @@ -22,5 +22,6 @@ namespace audiocontext { std::vector getPropertyNames(jsi::Runtime& rt) override; jsi::Value createOscillator(jsi::Runtime& runtime, const jsi::PropNameID& propNameId); + jsi::Value getDestination(jsi::Runtime& runtime, const jsi::PropNameID& propNameId); }; } // namespace audiocontext diff --git a/cpp/AudioDestinationNodeHostObject.cpp b/cpp/AudioDestinationNodeHostObject.cpp new file mode 100644 index 00000000..a2d967b3 --- /dev/null +++ b/cpp/AudioDestinationNodeHostObject.cpp @@ -0,0 +1,23 @@ +#include "AudioDestinationNodeHostObject.h" + +namespace audiocontext { + using namespace facebook; + + std::vector AudioDestinationNodeHostObject::getPropertyNames(jsi::Runtime& runtime) { + std::vector propertyNames; + return propertyNames; + } + + jsi::Value AudioDestinationNodeHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propNameId) { + auto propName = propNameId.utf8(runtime); + + throw std::runtime_error("Not yet implemented!"); + } + + void AudioDestinationNodeHostObject::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/AudioDestinationNodeHostObject.h b/cpp/AudioDestinationNodeHostObject.h new file mode 100644 index 00000000..31ff3d27 --- /dev/null +++ b/cpp/AudioDestinationNodeHostObject.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include "AudioDestinationNode.h" +#include "OscillatorNodeHostObject.h" + +namespace audiocontext { + using namespace facebook; + + class AudioDestinationNode; + + class AudioDestinationNodeHostObject : public jsi::HostObject { + + private: + friend class OscillatorNodeHostObject; + + AudioDestinationNode* destination_; + + public: + explicit AudioDestinationNodeHostObject(AudioDestinationNode* destination) : destination_(destination) {} + + 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/OscillatorNodeHostObject.cpp b/cpp/OscillatorNodeHostObject.cpp index 9d387fcc..b4c82059 100644 --- a/cpp/OscillatorNodeHostObject.cpp +++ b/cpp/OscillatorNodeHostObject.cpp @@ -1,84 +1,115 @@ #include "OscillatorNodeHostObject.h" #include -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; +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")); + propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "frequency")); + propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "detune")); + return propertyNames; + } + + jsi::Value OscillatorNodeHostObject::get(jsi::Runtime &runtime, const jsi::PropNameID &propNameId) + { + auto propName = propNameId.utf8(runtime); + + if (propName == "start") + { + return start(runtime, propNameId); } - jsi::Value OscillatorNodeHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propNameId) { - auto propName = propNameId.utf8(runtime); - - if (propName == "start") { - return start(runtime, propNameId); - } - - if (propName == "stop") { - return stop(runtime, propNameId); - } - - if (propName == "frequency") { - return frequency(runtime, propNameId); - } + if (propName == "stop") + { + return stop(runtime, propNameId); + } - if (propName == "detune") { - return detune(runtime, propNameId); - } + if (propName == "frequency") + { + return frequency(runtime, propNameId); + } - throw std::runtime_error("Prop not yet implemented!"); + if (propName == "detune") + { + return detune(runtime, propNameId); } - void OscillatorNodeHostObject::set(jsi::Runtime& runtime, const jsi::PropNameID& propNameId, const jsi::Value& value) { - auto propName = propNameId.utf8(runtime); + if (propName == "connect") + { + return connect(runtime, propNameId); + } - if (propName == "frequency") { - auto frequency = value.asNumber(); - oscillator_->setFrequency(frequency); - return; - } + throw std::runtime_error("Prop not yet implemented!"); + } - if (propName == "detune") { - auto detune = value.asNumber(); - oscillator_->setDetune(detune); - return; - } + 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!"); + if (propName == "frequency") + { + auto frequency = value.asNumber(); + oscillator_->setFrequency(frequency); + return; } - jsi::Value OscillatorNodeHostObject::start(jsi::Runtime& runtime, const jsi::PropNameID& propNameId) { - 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 == "detune") + { + auto detune = value.asNumber(); + oscillator_->setDetune(detune); + return; } - jsi::Value OscillatorNodeHostObject::stop(jsi::Runtime &runtime, - const jsi::PropNameID &propNameId) { - 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("Not yet implemented!"); + } - jsi::Value OscillatorNodeHostObject::frequency(jsi::Runtime &runtime, - const jsi::PropNameID &propNameId) { - return jsi::Function::createFromHostFunction(runtime, propNameId, 0, [this](jsi::Runtime& rt, const jsi::Value& thisValue, const jsi::Value* args, size_t count) -> jsi::Value { + jsi::Value OscillatorNodeHostObject::start(jsi::Runtime &runtime, const jsi::PropNameID &propNameId) + { + 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(); }); + } + + jsi::Value OscillatorNodeHostObject::stop(jsi::Runtime &runtime, + const jsi::PropNameID &propNameId) + { + 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(); }); + } + + jsi::Value OscillatorNodeHostObject::frequency(jsi::Runtime &runtime, + const jsi::PropNameID &propNameId) + { + return jsi::Function::createFromHostFunction(runtime, propNameId, 0, [this](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value + { auto frequency = oscillator_->getFrequency(); - return jsi::Value(frequency); - }); - } - - jsi::Value OscillatorNodeHostObject::detune(jsi::Runtime &runtime, - const jsi::PropNameID &propNameId) { - return jsi::Function::createFromHostFunction(runtime, propNameId, 0, [this](jsi::Runtime& rt, const jsi::Value& thisValue, const jsi::Value* args, size_t count) -> jsi::Value { + return jsi::Value(frequency); }); + } + + jsi::Value OscillatorNodeHostObject::detune(jsi::Runtime &runtime, + const jsi::PropNameID &propNameId) + { + return jsi::Function::createFromHostFunction(runtime, propNameId, 0, [this](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value + { auto detune = oscillator_->getDetune(); - return jsi::Value(detune); - }); - } + return jsi::Value(detune); }); + } + + jsi::Value OscillatorNodeHostObject::connect(jsi::Runtime &runtime, + const jsi::PropNameID &propNameId) + { + return jsi::Function::createFromHostFunction(runtime, propNameId, 1, [this](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value + { + auto destination = args[0].asObject(rt).asHostObject(rt); + oscillator_->connect(*destination->destination_); + return jsi::Value::undefined(); }); + } } diff --git a/cpp/OscillatorNodeHostObject.h b/cpp/OscillatorNodeHostObject.h index 9a0822f9..c91d1044 100644 --- a/cpp/OscillatorNodeHostObject.h +++ b/cpp/OscillatorNodeHostObject.h @@ -25,5 +25,6 @@ namespace audiocontext { jsi::Value stop(jsi::Runtime& runtime, const jsi::PropNameID& propNameId); jsi::Value frequency(jsi::Runtime& runtime, const jsi::PropNameID& propNameId); jsi::Value detune(jsi::Runtime& runtime, const jsi::PropNameID& propNameId); + jsi::Value connect(jsi::Runtime& runtime, const jsi::PropNameID& propNameId); }; } // namespace audiocontext diff --git a/example/src/App.tsx b/example/src/App.tsx index e3c9ce43..1802fc94 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -15,6 +15,10 @@ const App = () => { secondaryOscillatorRef.current = audioContextRef.current.createOscillator(); secondaryOscillatorRef.current.frequency = 300; + const destination = audioContextRef.current.destination(); + oscillatorRef.current.connect(destination); + secondaryOscillatorRef.current.connect(destination); + return () => { //TODO }; diff --git a/src/index.ts b/src/index.ts index 255b48fb..fba02ce9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,10 @@ import { NativeModules } from 'react-native'; const { AudioContextModule } = NativeModules; -import type { Oscillator, BaseAudioContext } from './types'; +import type { + Oscillator, + BaseAudioContext, + AudioDestinationNode, +} from './types'; declare global { function nativeCallSyncHook(): unknown; @@ -15,6 +19,10 @@ export class AudioContext implements BaseAudioContext { createOscillator(): Oscillator { return global.__AudioContextProxy.createOscillator(); } + + destination(): AudioDestinationNode { + return global.__AudioContextProxy.destination(); + } } export type { Oscillator }; diff --git a/src/types.ts b/src/types.ts index ee3747c1..f1cbf218 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,13 +1,16 @@ export interface BaseAudioContext { createOscillator(): Oscillator; + destination(): AudioDestinationNode; } export interface AudioNode { context: BaseAudioContext; - connect: (destination: AudioNode) => void; + connect: (destination: AudioDestinationNode) => void; disconnect: () => void; } +export interface AudioDestinationNode extends AudioNode {} + export interface AudioScheduledSourceNode extends AudioNode { start: () => void; stop: () => void;