From 34a3bc0a325795542c3fc55f5b95b149700cd575 Mon Sep 17 00:00:00 2001 From: Maciej Makowski Date: Wed, 24 Jul 2024 15:48:53 +0200 Subject: [PATCH 1/2] feat: implemented StereoPannerNode classes and added appropriate logic to AudioContext --- android/CMakeLists.txt | 6 +++ android/src/main/cpp/AudioContext.cpp | 36 ++++++++++------- android/src/main/cpp/AudioContext.h | 5 ++- android/src/main/cpp/StereoPannerNode.cpp | 16 ++++++++ android/src/main/cpp/StereoPannerNode.h | 23 +++++++++++ .../com/audiocontext/context/AudioContext.kt | 5 +++ .../audiocontext/context/BaseAudioContext.kt | 2 + .../audiocontext/nodes/PlaybackParameters.kt | 9 +++++ cpp/AudioContext/AudioContextHostObject.cpp | 12 +++++- cpp/AudioContext/AudioContextHostObject.h | 1 + cpp/AudioContext/AudioContextWrapper.h | 2 + .../android/AudioContextWrapper.cpp | 5 +++ .../StereoPannerNodeHostObject.cpp | 40 +++++++++++++++++++ .../StereoPannerNodeHostObject.h | 27 +++++++++++++ .../StereoPannerNodeWrapper.h | 31 ++++++++++++++ .../android/StereoPannerNodeWrapper.cpp | 12 ++++++ cpp/StereoPannerNode/ios/.keep | 0 example/src/App.tsx | 22 +++++++++- src/index.ts | 7 +++- src/types.ts | 5 +++ 20 files changed, 248 insertions(+), 18 deletions(-) create mode 100644 android/src/main/cpp/StereoPannerNode.cpp create mode 100644 android/src/main/cpp/StereoPannerNode.h create mode 100644 android/src/main/java/com/audiocontext/nodes/PlaybackParameters.kt create mode 100644 cpp/StereoPannerNode/StereoPannerNodeHostObject.cpp create mode 100644 cpp/StereoPannerNode/StereoPannerNodeHostObject.h create mode 100644 cpp/StereoPannerNode/StereoPannerNodeWrapper.h create mode 100644 cpp/StereoPannerNode/android/StereoPannerNodeWrapper.cpp create mode 100644 cpp/StereoPannerNode/ios/.keep diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index b1c84c22..aef43fba 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -18,6 +18,7 @@ include_directories( ../cpp/OscillatorNode ../cpp/AudioNode ../cpp/GainNode + ../cpp/StereoPannerNode src/main/cpp ../node_modules/react-native/ReactCommon/jsi ../node_modules/react-native/ReactAndroid/src/main/jni/react/jni @@ -31,6 +32,7 @@ add_library(react-native-audio-context SHARED src/main/cpp/AudioDestinationNode src/main/cpp/AudioNode src/main/cpp/GainNode + src/main/cpp/StereoPannerNode ../cpp/AudioContext/AudioContextHostObject ../cpp/AudioContext/AudioContextWrapper.h @@ -51,6 +53,10 @@ add_library(react-native-audio-context SHARED ../cpp/GainNode/GainNodeHostObject ../cpp/GainNode/GainNodeWrapper.h ../cpp/GainNode/android/GainNodeWrapper.cpp + + ../cpp/StereoPannerNode/StereoPannerNodeHostObject + ../cpp/StereoPannerNode/StereoPannerNodeWrapper.h + ../cpp/StereoPannerNode/android/StereoPannerNodeWrapper.cpp ) find_package(ReactAndroid REQUIRED CONFIG) diff --git a/android/src/main/cpp/AudioContext.cpp b/android/src/main/cpp/AudioContext.cpp index a925a2d6..78f77afb 100644 --- a/android/src/main/cpp/AudioContext.cpp +++ b/android/src/main/cpp/AudioContext.cpp @@ -13,23 +13,23 @@ namespace audiocontext AudioContextHostObject::createAndInstallFromWrapper(audioContextWrapper, jsContext); } - std::shared_ptr AudioContext::createOscillator() - { - static const auto method = javaClassLocal()->getMethod("createOscillator"); - auto oscillator = method(javaObject_.get()); - auto oscillatorCppInstance = oscillator->cthis(); + std::shared_ptr AudioContext::createOscillator() + { + static const auto method = javaClassLocal()->getMethod("createOscillator"); + auto oscillator = method(javaObject_.get()); + auto oscillatorCppInstance = oscillator->cthis(); - return std::shared_ptr(oscillatorCppInstance); - } + return std::shared_ptr(oscillatorCppInstance); + } - std::shared_ptr AudioContext::getDestination() - { - static const auto method = javaClassLocal()->getMethod("getDestination"); - auto destination = method(javaObject_.get()); - auto destinationCppInstance = destination->cthis(); + std::shared_ptr AudioContext::getDestination() + { + static const auto method = javaClassLocal()->getMethod("getDestination"); + auto destination = method(javaObject_.get()); + auto destinationCppInstance = destination->cthis(); - return std::shared_ptr(destinationCppInstance); - } + return std::shared_ptr(destinationCppInstance); + } std::shared_ptr AudioContext::createGain() { @@ -40,4 +40,12 @@ namespace audiocontext return std::shared_ptr(gainCppInstance); } + std::shared_ptr AudioContext::createStereoPanner() + { + static const auto method = javaClassLocal()->getMethod("createStereoPanner"); + auto stereoPanner = method(javaObject_.get()); + auto stereoPannerCppInstance = stereoPanner->cthis(); + + return std::shared_ptr(stereoPannerCppInstance); + } } // namespace audiocontext diff --git a/android/src/main/cpp/AudioContext.h b/android/src/main/cpp/AudioContext.h index 358fdb69..4cc67896 100644 --- a/android/src/main/cpp/AudioContext.h +++ b/android/src/main/cpp/AudioContext.h @@ -10,6 +10,7 @@ #include "OscillatorNode.h" #include "AudioDestinationNode.h" #include "GainNode.h" +#include "StereoPannerNode.h" namespace audiocontext { @@ -38,9 +39,11 @@ namespace audiocontext }); } - std::shared_ptr createOscillator(); std::shared_ptr getDestination(); + std::shared_ptr createOscillator(); std::shared_ptr createGain(); + std::shared_ptr createStereoPanner(); + void install(jlong jsContext); diff --git a/android/src/main/cpp/StereoPannerNode.cpp b/android/src/main/cpp/StereoPannerNode.cpp new file mode 100644 index 00000000..01e1a45f --- /dev/null +++ b/android/src/main/cpp/StereoPannerNode.cpp @@ -0,0 +1,16 @@ +#include "StereoPannerNode.h" + +namespace audiocontext{ + + using namespace facebook::jni; + + double StereoPannerNode::getPan(){ + static const auto method = javaClassLocal()->getMethod("getPan"); + return method(javaObject_.get()); + } + + void StereoPannerNode::setPan(double pan){ + static const auto method = javaClassLocal()->getMethod("setPan"); + method(javaObject_.get(), pan); + } +} diff --git a/android/src/main/cpp/StereoPannerNode.h b/android/src/main/cpp/StereoPannerNode.h new file mode 100644 index 00000000..e6792c28 --- /dev/null +++ b/android/src/main/cpp/StereoPannerNode.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include +#include "AudioNode.h" + +namespace audiocontext { + + using namespace facebook; + using namespace facebook::jni; + + class StereoPannerNode : public jni::HybridClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/audiocontext/nodes/StereoPannerNode;"; + + double getPan(); + + void setPan(double pan); + }; + +} // 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 8f289e51..97721e96 100644 --- a/android/src/main/java/com/audiocontext/context/AudioContext.kt +++ b/android/src/main/java/com/audiocontext/context/AudioContext.kt @@ -2,6 +2,7 @@ package com.audiocontext.context import com.audiocontext.nodes.AudioDestinationNode import com.audiocontext.nodes.GainNode +import com.audiocontext.nodes.StereoPannerNode import com.audiocontext.nodes.oscillator.OscillatorNode import com.facebook.jni.HybridData @@ -32,4 +33,8 @@ class AudioContext() : BaseAudioContext { override fun createGain(): GainNode { return GainNode(this) } + + override fun createStereoPanner(): StereoPannerNode { + return StereoPannerNode(this) + } } diff --git a/android/src/main/java/com/audiocontext/context/BaseAudioContext.kt b/android/src/main/java/com/audiocontext/context/BaseAudioContext.kt index 88d27e5d..7f23ff07 100644 --- a/android/src/main/java/com/audiocontext/context/BaseAudioContext.kt +++ b/android/src/main/java/com/audiocontext/context/BaseAudioContext.kt @@ -2,6 +2,7 @@ package com.audiocontext.context import com.audiocontext.nodes.AudioDestinationNode import com.audiocontext.nodes.GainNode +import com.audiocontext.nodes.StereoPannerNode import com.audiocontext.nodes.oscillator.OscillatorNode interface BaseAudioContext { @@ -10,4 +11,5 @@ interface BaseAudioContext { abstract fun createOscillator(): OscillatorNode abstract fun createGain(): GainNode + abstract fun createStereoPanner(): StereoPannerNode } diff --git a/android/src/main/java/com/audiocontext/nodes/PlaybackParameters.kt b/android/src/main/java/com/audiocontext/nodes/PlaybackParameters.kt new file mode 100644 index 00000000..d2a98790 --- /dev/null +++ b/android/src/main/java/com/audiocontext/nodes/PlaybackParameters.kt @@ -0,0 +1,9 @@ +package com.audiocontext.nodes + +import android.media.AudioTrack + +class PlaybackParameters(val audioTrack: AudioTrack, var buffer: ShortArray) { + var leftPan = 1.0 + var rightPan = 1.0 + var gain = 1.0 +} diff --git a/cpp/AudioContext/AudioContextHostObject.cpp b/cpp/AudioContext/AudioContextHostObject.cpp index d6f8550a..a1457a16 100644 --- a/cpp/AudioContext/AudioContextHostObject.cpp +++ b/cpp/AudioContext/AudioContextHostObject.cpp @@ -5,8 +5,10 @@ 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")); + propertyNames.push_back(jsi::PropNameID::forUtf8(runtime, "createOscillator")); + propertyNames.push_back(jsi::PropNameID::forUtf8(runtime, "createGain")); + propertyNames.push_back(jsi::PropNameID::forUtf8(runtime, "createStereoPanner")); return propertyNames; } @@ -35,6 +37,14 @@ namespace audiocontext { }); } + if(propName == "createStereoPanner") { + return jsi::Function::createFromHostFunction(runtime, propNameId, 0, [this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value { + auto stereoPanner = wrapper_->createStereoPanner(); + auto stereoPannerHostObject = StereoPannerNodeHostObject::createFromWrapper(stereoPanner); + return jsi::Object::createFromHostObject(runtime, stereoPannerHostObject); + }); + } + throw std::runtime_error("Not yet implemented!"); } diff --git a/cpp/AudioContext/AudioContextHostObject.h b/cpp/AudioContext/AudioContextHostObject.h index 58fcc230..03ccbeca 100644 --- a/cpp/AudioContext/AudioContextHostObject.h +++ b/cpp/AudioContext/AudioContextHostObject.h @@ -7,6 +7,7 @@ #include "OscillatorNodeHostObject.h" #include "AudioDestinationNodeHostObject.h" #include "GainNodeHostObject.h" +#include "StereoPannerNodeHostObject.h" namespace audiocontext { diff --git a/cpp/AudioContext/AudioContextWrapper.h b/cpp/AudioContext/AudioContextWrapper.h index 6fb5210d..106a4494 100644 --- a/cpp/AudioContext/AudioContextWrapper.h +++ b/cpp/AudioContext/AudioContextWrapper.h @@ -5,6 +5,7 @@ #include "OscillatorNodeWrapper.h" #include "AudioDestinationNodeWrapper.h" #include "GainNodeWrapper.h" +#include "StereoPannerNodeWrapper.h" #ifdef ANDROID #include "AudioContext.h" @@ -36,5 +37,6 @@ namespace audiocontext { std::shared_ptr createOscillator(); std::shared_ptr getDestination(); std::shared_ptr createGain(); + std::shared_ptr createStereoPanner(); }; } // namespace audiocontext diff --git a/cpp/AudioContext/android/AudioContextWrapper.cpp b/cpp/AudioContext/android/AudioContextWrapper.cpp index 1cb43962..55b2a013 100644 --- a/cpp/AudioContext/android/AudioContextWrapper.cpp +++ b/cpp/AudioContext/android/AudioContextWrapper.cpp @@ -16,4 +16,9 @@ namespace audiocontext { auto gain = audiocontext_->createGain(); return std::make_shared(gain); } + + std::shared_ptr AudioContextWrapper::createStereoPanner() { + auto panner = audiocontext_->createStereoPanner(); + return std::make_shared(panner); + } } // namespace audiocontext diff --git a/cpp/StereoPannerNode/StereoPannerNodeHostObject.cpp b/cpp/StereoPannerNode/StereoPannerNodeHostObject.cpp new file mode 100644 index 00000000..eed05710 --- /dev/null +++ b/cpp/StereoPannerNode/StereoPannerNodeHostObject.cpp @@ -0,0 +1,40 @@ +#include "StereoPannerNodeHostObject.h" + +namespace audiocontext +{ + using namespace facebook; + + std::vector StereoPannerNodeHostObject::getPropertyNames(jsi::Runtime &runtime) + { + std::vector propertyNames = AudioNodeHostObject::getPropertyNames(runtime); + propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "pan")); + return propertyNames; + } + + jsi::Value StereoPannerNodeHostObject::get(jsi::Runtime &runtime, const jsi::PropNameID &propNameId) + { + auto propName = propNameId.utf8(runtime); + + if (propName == "pan") + { + auto gain = wrapper_->getPan(); + return jsi::Value(gain); + } + + return AudioNodeHostObject::get(runtime, propNameId); + } + + void StereoPannerNodeHostObject::set(jsi::Runtime &runtime, const jsi::PropNameID &propNameId, const jsi::Value &value) + { + auto propName = propNameId.utf8(runtime); + + if (propName == "pan") + { + double pan = value.getNumber(); + wrapper_->setPan(pan); + return; + } + + throw std::runtime_error("Not yet implemented!"); + } +} diff --git a/cpp/StereoPannerNode/StereoPannerNodeHostObject.h b/cpp/StereoPannerNode/StereoPannerNodeHostObject.h new file mode 100644 index 00000000..3935be60 --- /dev/null +++ b/cpp/StereoPannerNode/StereoPannerNodeHostObject.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include "StereoPannerNodeWrapper.h" +#include "AudioNodeHostObject.h" + +namespace audiocontext { + using namespace facebook; + + class StereoPannerNodeWrapper; + + class StereoPannerNodeHostObject : public AudioNodeHostObject { + protected: + std::shared_ptr wrapper_; + + public: + explicit StereoPannerNodeHostObject(const 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; + std::vector getPropertyNames(jsi::Runtime& rt) override; + + static std::shared_ptr createFromWrapper(const std::shared_ptr &wrapper) { + return std::make_shared(wrapper); + } + }; +} // namespace audiocontext diff --git a/cpp/StereoPannerNode/StereoPannerNodeWrapper.h b/cpp/StereoPannerNode/StereoPannerNodeWrapper.h new file mode 100644 index 00000000..59e50457 --- /dev/null +++ b/cpp/StereoPannerNode/StereoPannerNodeWrapper.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include "AudioNodeWrapper.h" + +#ifdef ANDROID +#include "StereoPannerNode.h" +#endif + +namespace audiocontext { + using namespace facebook; + +#ifdef ANDROID + class StereoPannerNode; +#endif + + class StereoPannerNodeWrapper: public AudioNodeWrapper { +#ifdef ANDROID + private: + std::shared_ptr panner_; + public: + explicit StereoPannerNodeWrapper(const std::shared_ptr &panner) : AudioNodeWrapper( + panner), panner_(panner) {} +#else + public: + explicit PannerNodeWrapper() {} +#endif + double getPan(); + void setPan(double pan); + }; +} // namespace audiocontext diff --git a/cpp/StereoPannerNode/android/StereoPannerNodeWrapper.cpp b/cpp/StereoPannerNode/android/StereoPannerNodeWrapper.cpp new file mode 100644 index 00000000..9b31b77c --- /dev/null +++ b/cpp/StereoPannerNode/android/StereoPannerNodeWrapper.cpp @@ -0,0 +1,12 @@ +#include "StereoPannerNodeWrapper.h" + +namespace audiocontext { + + double StereoPannerNodeWrapper::getPan() { + return panner_->getPan(); + } + + void StereoPannerNodeWrapper::setPan(double pan) { + panner_->setPan(pan); + } +} // namespace audiocontext diff --git a/cpp/StereoPannerNode/ios/.keep b/cpp/StereoPannerNode/ios/.keep new file mode 100644 index 00000000..e69de29b diff --git a/example/src/App.tsx b/example/src/App.tsx index 34dbe288..50f75f45 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -7,6 +7,7 @@ import { AudioContext, type Oscillator, type Gain, + type StereoPanner, } from 'react-native-audio-context'; const App = () => { @@ -14,10 +15,12 @@ const App = () => { const [gain, setGain] = useState(1.0); const [frequency, setFrequency] = useState(440); const [detune, setDetune] = useState(0); + const [pan, setPan] = useState(0); const audioContextRef = useRef(null); const oscillatorRef = useRef(null); const gainRef = useRef(null); + const panRef = useRef(null); const setUp = () => { audioContextRef.current = new AudioContext(); @@ -30,9 +33,13 @@ const App = () => { gainRef.current = audioContextRef.current.createGain(); gainRef.current.gain = gain; + panRef.current = audioContextRef.current.createStereoPanner(); + panRef.current.pan = pan; + const destination = audioContextRef.current.destination; oscillatorRef.current.connect(gainRef.current); - gainRef.current.connect(destination); + gainRef.current.connect(panRef.current); + panRef.current.connect(destination); }; const handleSliderChange = ( @@ -91,6 +98,19 @@ const App = () => { step={0.1} /> + + Pan: {pan.toFixed(1)} + + handleSliderChange(value, 0, setPan, panRef, 'pan') + } + minimumValue={-1} + maximumValue={1} + step={0.1} + /> + Frequency: {frequency.toFixed(0)} Date: Wed, 24 Jul 2024 15:54:06 +0200 Subject: [PATCH 2/2] refactor: slightly changed audio processing logic --- .../nodes/AudioDestinationNode.kt | 12 +++++++--- .../java/com/audiocontext/nodes/AudioNode.kt | 6 ++--- .../java/com/audiocontext/nodes/GainNode.kt | 7 +++--- .../audiocontext/nodes/StereoPannerNode.kt | 23 +++++++++++++++++++ .../nodes/oscillator/OscillatorNode.kt | 21 +++++++++-------- 5 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 android/src/main/java/com/audiocontext/nodes/StereoPannerNode.kt diff --git a/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt b/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt index 9562cbdc..722c0d76 100644 --- a/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt +++ b/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt @@ -1,6 +1,5 @@ package com.audiocontext.nodes -import android.media.AudioTrack import com.audiocontext.context.BaseAudioContext import com.facebook.jni.HybridData @@ -17,7 +16,14 @@ class AudioDestinationNode(context: BaseAudioContext): AudioNode(context) { } } - override fun process(buffer: ShortArray, audioTrack: AudioTrack) { - audioTrack.write(buffer, 0, buffer.size) + private fun setVolumeAndPanning(playbackParameters: PlaybackParameters) { + val leftPan = playbackParameters.gain * playbackParameters.leftPan + val rightPan = playbackParameters.gain * playbackParameters.rightPan + playbackParameters.audioTrack.setStereoVolume(leftPan.toFloat(), rightPan.toFloat()) + } + + override fun process(playbackParameters: PlaybackParameters) { + setVolumeAndPanning(playbackParameters) + playbackParameters.audioTrack.write(playbackParameters.buffer, 0, playbackParameters.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 2665f1d7..71b254b6 100644 --- a/android/src/main/java/com/audiocontext/nodes/AudioNode.kt +++ b/android/src/main/java/com/audiocontext/nodes/AudioNode.kt @@ -1,7 +1,5 @@ package com.audiocontext.nodes -import android.media.AudioTrack -import android.util.Log import com.audiocontext.context.BaseAudioContext import com.facebook.jni.HybridData @@ -35,7 +33,7 @@ abstract class AudioNode(val context: BaseAudioContext) { connectedNodes.clear() } - open fun process(buffer: ShortArray, audioTrack: AudioTrack) { - connectedNodes.forEach { it.process(buffer, audioTrack) } + open fun process(playbackParameters: PlaybackParameters) { + connectedNodes.forEach { it.process(playbackParameters) } } } diff --git a/android/src/main/java/com/audiocontext/nodes/GainNode.kt b/android/src/main/java/com/audiocontext/nodes/GainNode.kt index c8312d32..4b860190 100644 --- a/android/src/main/java/com/audiocontext/nodes/GainNode.kt +++ b/android/src/main/java/com/audiocontext/nodes/GainNode.kt @@ -1,6 +1,5 @@ package com.audiocontext.nodes -import android.media.AudioTrack import com.audiocontext.context.BaseAudioContext import com.facebook.jni.HybridData @@ -15,8 +14,8 @@ class GainNode(context: BaseAudioContext): AudioNode(context) { private val mHybridData: HybridData? = initHybrid(); - override fun process(buffer: ShortArray, audioTrack: AudioTrack) { - audioTrack.setVolume(gain.toFloat()) - super.process(buffer, audioTrack) + override fun process(playbackParameters: PlaybackParameters) { + playbackParameters.gain = gain + super.process(playbackParameters) } } diff --git a/android/src/main/java/com/audiocontext/nodes/StereoPannerNode.kt b/android/src/main/java/com/audiocontext/nodes/StereoPannerNode.kt new file mode 100644 index 00000000..785b2078 --- /dev/null +++ b/android/src/main/java/com/audiocontext/nodes/StereoPannerNode.kt @@ -0,0 +1,23 @@ +package com.audiocontext.nodes + +import com.audiocontext.context.BaseAudioContext +import com.facebook.jni.HybridData +import kotlin.math.min + +class StereoPannerNode(context: BaseAudioContext): AudioNode(context) { + override val numberOfInputs: Int = 1 + override val numberOfOutputs: Int = 1 + private var pan: Double = 0.0 + get() = field + set(value) { + field = value + } + + private val mHybridData: HybridData? = initHybrid(); + + override fun process(playbackParameters: PlaybackParameters) { + playbackParameters.leftPan = min(1.0 - pan, 1.0) + playbackParameters.rightPan = min(1.0 + pan, 1.0) + super.process(playbackParameters) + } +} 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 7785c824..432ddb82 100644 --- a/android/src/main/java/com/audiocontext/nodes/oscillator/OscillatorNode.kt +++ b/android/src/main/java/com/audiocontext/nodes/oscillator/OscillatorNode.kt @@ -7,6 +7,7 @@ import android.media.AudioTrack import android.util.Log import com.audiocontext.context.BaseAudioContext import com.audiocontext.nodes.AudioScheduledSourceNode +import com.audiocontext.nodes.PlaybackParameters import com.facebook.jni.HybridData import com.facebook.react.bridge.ReactApplicationContext import kotlin.math.abs @@ -28,11 +29,10 @@ class OscillatorNode(context: BaseAudioContext) : AudioScheduledSourceNode(conte } private var waveType: WaveType = WaveType.SINE - private val audioTrack: AudioTrack + private var playbackParameters: PlaybackParameters @Volatile private var isPlaying: Boolean = false private var playbackThread: Thread? = null private var stopThread: Thread? = null - private var buffer: ShortArray = ShortArray(1024) private val mHybridData: HybridData? = initHybrid(); @@ -58,7 +58,10 @@ class OscillatorNode(context: BaseAudioContext) : AudioScheduledSourceNode(conte .setChannelMask(AudioFormat.CHANNEL_OUT_MONO) .build() - this.audioTrack = AudioTrack(audioAttributes, audioFormat, bufferSize, AudioTrack.MODE_STREAM, AudioManager.AUDIO_SESSION_ID_GENERATE) + val audioTrack = AudioTrack(audioAttributes, audioFormat, bufferSize, AudioTrack.MODE_STREAM, AudioManager.AUDIO_SESSION_ID_GENERATE) + val buffer = ShortArray(bufferSize) + + this.playbackParameters = PlaybackParameters(audioTrack, buffer) } fun getWaveType(): String { @@ -82,7 +85,7 @@ class OscillatorNode(context: BaseAudioContext) : AudioScheduledSourceNode(conte } isPlaying = true - audioTrack.play() + playbackParameters.audioTrack.play() generateSound() }.apply { start() } } @@ -100,7 +103,7 @@ class OscillatorNode(context: BaseAudioContext) : AudioScheduledSourceNode(conte } isPlaying = false - audioTrack.stop() + playbackParameters.audioTrack.stop() playbackThread?.join() }.apply { start() } @@ -114,12 +117,12 @@ class OscillatorNode(context: BaseAudioContext) : AudioScheduledSourceNode(conte while(isPlaying) { phaseChange = 2 * Math.PI * (frequency + detune) / context.sampleRate - for(i in buffer.indices) { - buffer[i] = WaveType.getWaveBufferElement(wavePhase, waveType) + for(i in playbackParameters.buffer.indices) { + playbackParameters.buffer[i] = WaveType.getWaveBufferElement(wavePhase, waveType) wavePhase += phaseChange } - process(buffer, audioTrack) + process(playbackParameters) } - audioTrack.flush() + playbackParameters.audioTrack.flush() } }