diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index 2c1425cb..329e3836 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -16,6 +16,7 @@ include_directories( ../cpp/AudioContext ../cpp/AudioDestinationNode ../cpp/OscillatorNode + ../cpp/AudioNode src/main/cpp ../node_modules/react-native/ReactCommon/jsi ../node_modules/react-native/ReactAndroid/src/main/jni/react/jni @@ -27,6 +28,7 @@ add_library(react-native-audio-context SHARED src/main/cpp/AudioContext src/main/cpp/OscillatorNode src/main/cpp/AudioDestinationNode + src/main/cpp/AudioNode ../cpp/AudioContext/AudioContextHostObject ../cpp/AudioContext/AudioContextWrapper.h @@ -39,6 +41,10 @@ add_library(react-native-audio-context SHARED ../cpp/OscillatorNode/OscillatorNodeHostObject ../cpp/OscillatorNode/OscillatorNodeWrapper.h ../cpp/OscillatorNode/android/OscillatorNodeWrapper.cpp + + ../cpp/AudioNode/AudioNodeHostObject.h + ../cpp/AudioNode/AudioNodeWrapper.h + ../cpp/AudioNode/android/AudioNodeWrapper.cpp ) find_package(ReactAndroid REQUIRED CONFIG) diff --git a/android/src/main/cpp/AudioDestinationNode.cpp b/android/src/main/cpp/AudioDestinationNode.cpp index 737d9760..cef18fb4 100644 --- a/android/src/main/cpp/AudioDestinationNode.cpp +++ b/android/src/main/cpp/AudioDestinationNode.cpp @@ -4,6 +4,4 @@ namespace audiocontext { using namespace facebook::jni; - AudioDestinationNode::AudioDestinationNode(jni::alias_ref &jThis): javaObject_(make_global(jThis)) {} - } // namespace audiocontext diff --git a/android/src/main/cpp/AudioDestinationNode.h b/android/src/main/cpp/AudioDestinationNode.h index 76eab180..b7526d53 100644 --- a/android/src/main/cpp/AudioDestinationNode.h +++ b/android/src/main/cpp/AudioDestinationNode.h @@ -13,28 +13,9 @@ namespace audiocontext { class OscillatorNode; - class AudioDestinationNode : public jni::HybridClass { + class AudioDestinationNode : public jni::HybridClass { public: static auto constexpr kJavaDescriptor = "Lcom/audiocontext/nodes/AudioDestinationNode;"; - - static jni::local_ref initHybrid(jni::alias_ref jThis) - { - return makeCxxInstance(jThis); - } - - static void registerNatives() { - registerHybrid({ - makeNativeMethod("initHybrid", AudioDestinationNode::initHybrid), - }); - } - - private: - friend HybridBase; - friend class OscillatorNode; - - global_ref javaObject_; - - explicit AudioDestinationNode(jni::alias_ref& jThis); }; } // namespace audiocontext diff --git a/android/src/main/cpp/AudioNode.cpp b/android/src/main/cpp/AudioNode.cpp new file mode 100644 index 00000000..0a462c62 --- /dev/null +++ b/android/src/main/cpp/AudioNode.cpp @@ -0,0 +1,18 @@ +#include "AudioNode.h" + +namespace audiocontext { + + AudioNode::AudioNode(alias_ref &jThis): javaObject_(make_global(jThis)) {} + + void AudioNode::connect(const std::shared_ptr node) { + static const auto method = javaClassLocal()->getMethod( + "connect"); + method(javaObject_.get(), node->javaObject_.get()); + } + + void AudioNode::disconnect(const std::shared_ptr node) { + static const auto method = javaClassLocal()->getMethod( + "disconnect"); + method(javaObject_.get(), node->javaObject_.get()); + } +} diff --git a/android/src/main/cpp/AudioNode.h b/android/src/main/cpp/AudioNode.h new file mode 100644 index 00000000..0962a5ac --- /dev/null +++ b/android/src/main/cpp/AudioNode.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include + +namespace audiocontext { + + using namespace facebook; + using namespace facebook::jni; + + class AudioNode : public jni::HybridClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/audiocontext/nodes/AudioNode;"; + + static jni::local_ref initHybrid(jni::alias_ref jThis) + { + return makeCxxInstance(jThis); + } + + static void registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", AudioNode::initHybrid), + }); + } + + void connect(const std::shared_ptr node); + void disconnect(const std::shared_ptr node); + + protected: + friend HybridBase; + friend class AudioNode; + + global_ref javaObject_; + + explicit AudioNode(jni::alias_ref& jThis); + }; + +} // namespace audiocontext diff --git a/android/src/main/cpp/OnLoad.cpp b/android/src/main/cpp/OnLoad.cpp index a7e45f89..0057106a 100644 --- a/android/src/main/cpp/OnLoad.cpp +++ b/android/src/main/cpp/OnLoad.cpp @@ -2,6 +2,7 @@ #include "OscillatorNode.h" #include "AudioContext.h" #include "AudioDestinationNode.h" +#include "AudioNode.h" using namespace audiocontext; @@ -11,5 +12,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) OscillatorNode::registerNatives(); AudioContext::registerNatives(); AudioDestinationNode::registerNatives(); + AudioNode::registerNatives(); }); } diff --git a/android/src/main/cpp/OscillatorNode.cpp b/android/src/main/cpp/OscillatorNode.cpp index 35c2e905..13c50e8d 100644 --- a/android/src/main/cpp/OscillatorNode.cpp +++ b/android/src/main/cpp/OscillatorNode.cpp @@ -4,9 +4,6 @@ namespace audiocontext { using namespace facebook::jni; - OscillatorNode::OscillatorNode(jni::alias_ref &jThis) - : javaObject_(make_global(jThis)){} - void OscillatorNode::start(double time) { static const auto method = javaClassLocal()->getMethod("start"); method(javaObject_.get(), time); @@ -47,9 +44,4 @@ namespace audiocontext { method(javaObject_.get(), *make_jstring(waveType)); } - - 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 f0f70107..5776da31 100644 --- a/android/src/main/cpp/OscillatorNode.h +++ b/android/src/main/cpp/OscillatorNode.h @@ -4,46 +4,31 @@ #include #include #include -#include "AudioDestinationNode.h" +#include "AudioNode.h" namespace audiocontext { using namespace facebook; using namespace facebook::jni; - class AudioDestinationNode; - - class OscillatorNode : public jni::HybridClass { + class OscillatorNode : public jni::HybridClass { public: static auto constexpr kJavaDescriptor = "Lcom/audiocontext/nodes/oscillator/OscillatorNode;"; - static jni::local_ref initHybrid(jni::alias_ref jThis) - { - return makeCxxInstance(jThis); - } - - static void registerNatives() { - registerHybrid({ - makeNativeMethod("initHybrid", OscillatorNode::initHybrid), - }); - } - void start(double time); + void stop(double time); + double getFrequency(); + double getDetune(); + std::string getWaveType(); - void setFrequency(double frequency); - void setDetune(double detune); - void setWaveType(const std::string& waveType); - void connect(const AudioDestinationNode &destination); - private: - friend HybridBase; + void setFrequency(double frequency); - global_ref javaObject_; + void setDetune(double detune); - explicit OscillatorNode(jni::alias_ref& jThis); + void setWaveType(const std::string &waveType); }; - -} // namespace audiocontext +}// namespace audiocontext diff --git a/android/src/main/java/com/audiocontext/context/AudioContext.kt b/android/src/main/java/com/audiocontext/context/AudioContext.kt index dd74d1b4..e966e21f 100644 --- a/android/src/main/java/com/audiocontext/context/AudioContext.kt +++ b/android/src/main/java/com/audiocontext/context/AudioContext.kt @@ -3,9 +3,6 @@ package com.audiocontext.context import com.audiocontext.nodes.AudioDestinationNode import com.audiocontext.nodes.oscillator.OscillatorNode import com.facebook.jni.HybridData -import com.facebook.react.bridge.ReactApplicationContext - -// nodes class AudioContext() : BaseAudioContext { override val sampleRate: Int = 44100 diff --git a/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt b/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt index 6b194439..e9da01a6 100644 --- a/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt +++ b/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt @@ -1,6 +1,7 @@ package com.audiocontext.nodes import android.media.AudioTrack +import android.util.Log import com.audiocontext.context.BaseAudioContext import com.facebook.jni.HybridData import com.facebook.react.bridge.ReactApplicationContext @@ -10,7 +11,7 @@ class AudioDestinationNode(context: BaseAudioContext): AudioNode(context) { override val numberOfInputs = 1 override val numberOfOutputs = 0 - private val mHybridData: HybridData?; + private val mHybridData: HybridData? = initHybrid(); companion object { init { @@ -18,12 +19,6 @@ class AudioDestinationNode(context: BaseAudioContext): AudioNode(context) { } } - init { - mHybridData = initHybrid() - } - - external fun initHybrid(): 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 cba69f5f..2665f1d7 100644 --- a/android/src/main/java/com/audiocontext/nodes/AudioNode.kt +++ b/android/src/main/java/com/audiocontext/nodes/AudioNode.kt @@ -1,7 +1,9 @@ package com.audiocontext.nodes import android.media.AudioTrack +import android.util.Log import com.audiocontext.context.BaseAudioContext +import com.facebook.jni.HybridData abstract class AudioNode(val context: BaseAudioContext) { @@ -9,9 +11,23 @@ abstract class AudioNode(val context: BaseAudioContext) { abstract val numberOfOutputs: Int; private val connectedNodes = mutableListOf() - fun connect(destination: AudioDestinationNode) { + private val mHybridData: HybridData?; + + companion object { + init { + System.loadLibrary("react-native-audio-context") + } + } + + init { + mHybridData = initHybrid() + } + + external fun initHybrid(): HybridData? + + fun connect(node: AudioNode) { if(this.numberOfOutputs > 0) { - connectedNodes.add(destination) + connectedNodes.add(node) } } 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 1bbc5703..7785c824 100644 --- a/android/src/main/java/com/audiocontext/nodes/oscillator/OscillatorNode.kt +++ b/android/src/main/java/com/audiocontext/nodes/oscillator/OscillatorNode.kt @@ -1,5 +1,6 @@ package com.audiocontext.nodes.oscillator +import android.media.AudioAttributes import android.media.AudioFormat import android.media.AudioManager import android.media.AudioTrack @@ -33,7 +34,7 @@ class OscillatorNode(context: BaseAudioContext) : AudioScheduledSourceNode(conte private var stopThread: Thread? = null private var buffer: ShortArray = ShortArray(1024) - private val mHybridData: HybridData?; + private val mHybridData: HybridData? = initHybrid(); companion object { init { @@ -42,17 +43,23 @@ class OscillatorNode(context: BaseAudioContext) : AudioScheduledSourceNode(conte } init { - mHybridData = initHybrid() 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(): HybridData? + val audioAttributes = AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_MEDIA) + .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) + .build() + + val audioFormat = AudioFormat.Builder() + .setSampleRate(context.sampleRate) + .setEncoding(AudioFormat.ENCODING_PCM_16BIT) + .setChannelMask(AudioFormat.CHANNEL_OUT_MONO) + .build() + + this.audioTrack = AudioTrack(audioAttributes, audioFormat, bufferSize, AudioTrack.MODE_STREAM, AudioManager.AUDIO_SESSION_ID_GENERATE) + } fun getWaveType(): String { return WaveType.toString(waveType) diff --git a/cpp/AudioContext/AudioContextHostObject.h b/cpp/AudioContext/AudioContextHostObject.h index 86d0d556..bada6f7f 100644 --- a/cpp/AudioContext/AudioContextHostObject.h +++ b/cpp/AudioContext/AudioContextHostObject.h @@ -1,6 +1,8 @@ #pragma once #include + +#include #include "AudioContextWrapper.h" #include "OscillatorNodeHostObject.h" #include "AudioDestinationNodeHostObject.h" @@ -19,7 +21,7 @@ namespace audiocontext public: explicit AudioContextHostObject(std::shared_ptr wrapper) : wrapper_(wrapper) {} - static void createAndInstallFromWrapper(std::shared_ptr wrapper, jlong jsContext) { + static void createAndInstallFromWrapper(const std::shared_ptr& wrapper, jlong jsContext) { auto runtime = reinterpret_cast(jsContext); auto hostObject = std::make_shared(wrapper); auto object = jsi::Object::createFromHostObject(*runtime, hostObject); diff --git a/cpp/AudioContext/AudioContextWrapper.h b/cpp/AudioContext/AudioContextWrapper.h index 1efbad7c..09b9342e 100644 --- a/cpp/AudioContext/AudioContextWrapper.h +++ b/cpp/AudioContext/AudioContextWrapper.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "OscillatorNodeWrapper.h" #include "AudioDestinationNodeWrapper.h" diff --git a/cpp/AudioDestinationNode/AudioDestinationNodeHostObject.h b/cpp/AudioDestinationNode/AudioDestinationNodeHostObject.h index 0b16921e..1f56395a 100644 --- a/cpp/AudioDestinationNode/AudioDestinationNodeHostObject.h +++ b/cpp/AudioDestinationNode/AudioDestinationNodeHostObject.h @@ -2,7 +2,7 @@ #include #include "AudioDestinationNodeWrapper.h" -#include "OscillatorNodeWrapper.h" +#include "AudioNodeHostObject.h" namespace audiocontext { using namespace facebook; @@ -10,15 +10,13 @@ namespace audiocontext { class AudioDestinationNodeWrapper; class OscillatorNodeWrapper; - class AudioDestinationNodeHostObject : public jsi::HostObject { - - private: - friend class OscillatorNodeHostObject; + class AudioDestinationNodeHostObject : public AudioNodeHostObject { + protected: std::shared_ptr wrapper_; public: - explicit AudioDestinationNodeHostObject(std::shared_ptr wrapper): wrapper_(wrapper) {} + explicit AudioDestinationNodeHostObject(std::shared_ptr wrapper): AudioNodeHostObject(wrapper), wrapper_(wrapper) {} 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; diff --git a/cpp/AudioDestinationNode/AudioDestinationNodeWrapper.h b/cpp/AudioDestinationNode/AudioDestinationNodeWrapper.h index d74e032c..e81e4152 100644 --- a/cpp/AudioDestinationNode/AudioDestinationNodeWrapper.h +++ b/cpp/AudioDestinationNode/AudioDestinationNodeWrapper.h @@ -14,7 +14,7 @@ namespace audiocontext { class AudioDestinationNode; #endif - class AudioDestinationNodeWrapper { + class AudioDestinationNodeWrapper: public AudioNodeWrapper { #ifdef ANDROID private: friend class OscillatorNodeWrapper; @@ -24,7 +24,7 @@ namespace audiocontext { public: #ifdef ANDROID - explicit AudioDestinationNodeWrapper(std::shared_ptr destination) : destination_(destination) {} + explicit AudioDestinationNodeWrapper(std::shared_ptr destination) : AudioNodeWrapper(destination), destination_(destination) {} #else explicit AudioDestinationNodeWrapper() {} #endif diff --git a/cpp/AudioNode/AudioNodeHostObject.h b/cpp/AudioNode/AudioNodeHostObject.h new file mode 100644 index 00000000..ef3badef --- /dev/null +++ b/cpp/AudioNode/AudioNodeHostObject.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "AudioNodeWrapper.h" + +namespace audiocontext { + using namespace facebook; + + class AudioNodeWrapper; + + class AudioNodeHostObject : public jsi::HostObject { + + public: + std::shared_ptr wrapper_; + + public: + explicit AudioNodeHostObject(std::shared_ptr wrapper) : wrapper_(wrapper) {} + }; +} // namespace audiocontext diff --git a/cpp/AudioNode/AudioNodeWrapper.h b/cpp/AudioNode/AudioNodeWrapper.h new file mode 100644 index 00000000..1de3b5b2 --- /dev/null +++ b/cpp/AudioNode/AudioNodeWrapper.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#ifdef ANDROID +#include "AudioNode.h" +#endif + +namespace audiocontext { + using namespace facebook; + + class AudioNodeWrapper { + public: +#ifdef ANDROID + std::shared_ptr node_; + + explicit AudioNodeWrapper(std::shared_ptr node) : node_(node) {} +#else + explicit AudioNodeWrapper() {} +#endif + void connect(const std::shared_ptr node) const; + void disconnect(const std::shared_ptr node) const; + }; +} // namespace audiocontext diff --git a/cpp/AudioNode/android/AudioNodeWrapper.cpp b/cpp/AudioNode/android/AudioNodeWrapper.cpp new file mode 100644 index 00000000..59fea09f --- /dev/null +++ b/cpp/AudioNode/android/AudioNodeWrapper.cpp @@ -0,0 +1,12 @@ +#include "AudioNodeWrapper.h" + +namespace audiocontext { + + void AudioNodeWrapper::connect(const std::shared_ptr node) const { + node_->connect(node->node_); + } + + void AudioNodeWrapper::disconnect(const std::shared_ptr node) const { + node_->disconnect(node->node_); + } +} diff --git a/cpp/AudioNode/ios/.keep b/cpp/AudioNode/ios/.keep new file mode 100644 index 00000000..e69de29b diff --git a/cpp/OscillatorNode/OscillatorNodeHostObject.cpp b/cpp/OscillatorNode/OscillatorNodeHostObject.cpp index 250c11e5..2ae3619e 100644 --- a/cpp/OscillatorNode/OscillatorNodeHostObject.cpp +++ b/cpp/OscillatorNode/OscillatorNodeHostObject.cpp @@ -62,8 +62,8 @@ namespace audiocontext { 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].getObject(rt).getHostObject(rt); - wrapper_->connect(std::shared_ptr(destination->wrapper_)); + auto node = args[0].getObject(rt).getHostObject(rt); + wrapper_->connect(std::shared_ptr(node)->wrapper_); return jsi::Value::undefined(); }); } diff --git a/cpp/OscillatorNode/OscillatorNodeHostObject.h b/cpp/OscillatorNode/OscillatorNodeHostObject.h index b2ef49d4..c9fbb284 100644 --- a/cpp/OscillatorNode/OscillatorNodeHostObject.h +++ b/cpp/OscillatorNode/OscillatorNodeHostObject.h @@ -2,20 +2,19 @@ #include #include "OscillatorNodeWrapper.h" -#include "AudioDestinationNodeHostObject.h" +#include "AudioNodeHostObject.h" namespace audiocontext { using namespace facebook; class OscillatorNodeWrapper; - class AudioDestinationNodeHostObject; - class OscillatorNodeHostObject : public jsi::HostObject { - private: + class OscillatorNodeHostObject : public AudioNodeHostObject { + protected: std::shared_ptr wrapper_; public: - explicit OscillatorNodeHostObject(std::shared_ptr wrapper) : wrapper_(wrapper) {} + explicit OscillatorNodeHostObject(std::shared_ptr wrapper) : AudioNodeHostObject(wrapper), wrapper_(wrapper) {} 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; diff --git a/cpp/OscillatorNode/OscillatorNodeWrapper.h b/cpp/OscillatorNode/OscillatorNodeWrapper.h index 39cfdc44..8bf41042 100644 --- a/cpp/OscillatorNode/OscillatorNodeWrapper.h +++ b/cpp/OscillatorNode/OscillatorNodeWrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include "AudioDestinationNodeWrapper.h" +#include "AudioNodeWrapper.h" #ifdef ANDROID #include "OscillatorNode.h" @@ -10,13 +10,11 @@ namespace audiocontext { using namespace facebook; - class AudioDestinationNodeWrapper; - #ifdef ANDROID class OscillatorNode; #endif - class OscillatorNodeWrapper { + class OscillatorNodeWrapper: public AudioNodeWrapper { #ifdef ANDROID private: std::shared_ptr oscillator_; @@ -24,7 +22,8 @@ namespace audiocontext { public: #ifdef ANDROID - explicit OscillatorNodeWrapper(std::shared_ptr oscillator) : oscillator_(oscillator) {} + explicit OscillatorNodeWrapper( std::shared_ptr oscillator) : AudioNodeWrapper( + oscillator), oscillator_(oscillator) {} #else explicit OscillatorNodeWrapper() {} #endif @@ -33,11 +32,9 @@ namespace audiocontext { std::string getType(); void start(double time); void stop(double time); - void connect(std::shared_ptr destination); void setFrequency(double frequency); void setDetune(double detune); - void setType(std::string type); - + void setType(const std::string& type); }; } // namespace audiocontext diff --git a/cpp/OscillatorNode/android/OscillatorNodeWrapper.cpp b/cpp/OscillatorNode/android/OscillatorNodeWrapper.cpp index 67c286f5..2260e986 100644 --- a/cpp/OscillatorNode/android/OscillatorNodeWrapper.cpp +++ b/cpp/OscillatorNode/android/OscillatorNodeWrapper.cpp @@ -30,11 +30,7 @@ namespace audiocontext { oscillator_->setDetune(detune); } - void OscillatorNodeWrapper::setType(std::string type) { + void OscillatorNodeWrapper::setType(const std::string& type) { oscillator_->setWaveType(type); } - - void OscillatorNodeWrapper::connect(std::shared_ptr destination) { - oscillator_->connect(*destination->destination_); - } } diff --git a/example/src/App.tsx b/example/src/App.tsx index f7302bf9..082ff811 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -12,6 +12,8 @@ const App = () => { const setUp = () => { audioContextRef.current = new AudioContext(); oscillatorRef.current = audioContextRef.current.createOscillator(); + oscillatorRef.current.frequency = 800; + secondaryOscillatorRef.current = audioContextRef.current.createOscillator(); secondaryOscillatorRef.current.frequency = 300; secondaryOscillatorRef.current.type = 'square'; @@ -32,7 +34,7 @@ const App = () => { setUp(); } - oscillatorRef.current?.start(0); + oscillatorRef.current?.start(1); secondaryOscillatorRef.current?.start(0); }; const stopOscillator = () => {