Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/android/panner node #40

Merged
merged 3 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -31,6 +32,7 @@ add_library(react-native-audio-context SHARED
src/main/cpp/AudioDestinationNode.h
src/main/cpp/AudioNode
src/main/cpp/GainNode
src/main/cpp/StereoPannerNode

../cpp/AudioContext/AudioContextHostObject
../cpp/AudioContext/AudioContextWrapper.h
Expand All @@ -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)
Expand Down
36 changes: 22 additions & 14 deletions android/src/main/cpp/AudioContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,23 @@ namespace audiocontext
AudioContextHostObject::createAndInstallFromWrapper(audioContextWrapper, jsContext);
}

std::shared_ptr<OscillatorNode> AudioContext::createOscillator()
{
static const auto method = javaClassLocal()->getMethod<OscillatorNode()>("createOscillator");
auto oscillator = method(javaObject_.get());
auto oscillatorCppInstance = oscillator->cthis();
std::shared_ptr<OscillatorNode> AudioContext::createOscillator()
{
static const auto method = javaClassLocal()->getMethod<OscillatorNode()>("createOscillator");
auto oscillator = method(javaObject_.get());
auto oscillatorCppInstance = oscillator->cthis();

return std::shared_ptr<OscillatorNode>(oscillatorCppInstance);
}
return std::shared_ptr<OscillatorNode>(oscillatorCppInstance);
}

std::shared_ptr<AudioDestinationNode> AudioContext::getDestination()
{
static const auto method = javaClassLocal()->getMethod<AudioDestinationNode()>("getDestination");
auto destination = method(javaObject_.get());
auto destinationCppInstance = destination->cthis();
std::shared_ptr<AudioDestinationNode> AudioContext::getDestination()
{
static const auto method = javaClassLocal()->getMethod<AudioDestinationNode()>("getDestination");
auto destination = method(javaObject_.get());
auto destinationCppInstance = destination->cthis();

return std::shared_ptr<AudioDestinationNode>(destinationCppInstance);
}
return std::shared_ptr<AudioDestinationNode>(destinationCppInstance);
}

std::shared_ptr<GainNode> AudioContext::createGain()
{
Expand All @@ -40,4 +40,12 @@ namespace audiocontext
return std::shared_ptr<GainNode>(gainCppInstance);
}

std::shared_ptr<StereoPannerNode> AudioContext::createStereoPanner()
{
static const auto method = javaClassLocal()->getMethod<StereoPannerNode()>("createStereoPanner");
auto stereoPanner = method(javaObject_.get());
auto stereoPannerCppInstance = stereoPanner->cthis();

return std::shared_ptr<StereoPannerNode>(stereoPannerCppInstance);
}
} // namespace audiocontext
5 changes: 4 additions & 1 deletion android/src/main/cpp/AudioContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "OscillatorNode.h"
#include "AudioDestinationNode.h"
#include "GainNode.h"
#include "StereoPannerNode.h"

namespace audiocontext
{
Expand Down Expand Up @@ -38,9 +39,11 @@ namespace audiocontext
});
}

std::shared_ptr<OscillatorNode> createOscillator();
std::shared_ptr<AudioDestinationNode> getDestination();
std::shared_ptr<OscillatorNode> createOscillator();
std::shared_ptr<GainNode> createGain();
std::shared_ptr<StereoPannerNode> createStereoPanner();


void install(jlong jsContext);

Expand Down
16 changes: 16 additions & 0 deletions android/src/main/cpp/StereoPannerNode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include "StereoPannerNode.h"

namespace audiocontext{

using namespace facebook::jni;

double StereoPannerNode::getPan(){
static const auto method = javaClassLocal()->getMethod<jdouble()>("getPan");
return method(javaObject_.get());
}

void StereoPannerNode::setPan(double pan){
static const auto method = javaClassLocal()->getMethod<void(jdouble)>("setPan");
method(javaObject_.get(), pan);
}
}
23 changes: 23 additions & 0 deletions android/src/main/cpp/StereoPannerNode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include <fbjni/fbjni.h>
#include <react/jni/CxxModuleWrapper.h>
#include <react/jni/JMessageQueueThread.h>
#include <memory>
#include "AudioNode.h"

namespace audiocontext {

using namespace facebook;
using namespace facebook::jni;

class StereoPannerNode : public jni::HybridClass<StereoPannerNode, AudioNode> {
public:
static auto constexpr kJavaDescriptor = "Lcom/audiocontext/nodes/StereoPannerNode;";

double getPan();

void setPan(double pan);
};

} // namespace audiocontext
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -32,4 +33,8 @@ class AudioContext() : BaseAudioContext {
override fun createGain(): GainNode {
return GainNode(this)
}

override fun createStereoPanner(): StereoPannerNode {
return StereoPannerNode(this)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -10,4 +11,5 @@ interface BaseAudioContext {

abstract fun createOscillator(): OscillatorNode
abstract fun createGain(): GainNode
abstract fun createStereoPanner(): StereoPannerNode
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.audiocontext.nodes

import android.media.AudioTrack
import com.audiocontext.context.BaseAudioContext
import com.facebook.jni.HybridData

Expand All @@ -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)
}
}
6 changes: 2 additions & 4 deletions android/src/main/java/com/audiocontext/nodes/AudioNode.kt
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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) }
}
}
7 changes: 3 additions & 4 deletions android/src/main/java/com/audiocontext/nodes/GainNode.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.audiocontext.nodes

import android.media.AudioTrack
import com.audiocontext.context.BaseAudioContext
import com.facebook.jni.HybridData

Expand All @@ -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)
}
}
Original file line number Diff line number Diff line change
@@ -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
}
23 changes: 23 additions & 0 deletions android/src/main/java/com/audiocontext/nodes/StereoPannerNode.kt
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();

Expand All @@ -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 {
Expand All @@ -82,7 +85,7 @@ class OscillatorNode(context: BaseAudioContext) : AudioScheduledSourceNode(conte
}

isPlaying = true
audioTrack.play()
playbackParameters.audioTrack.play()
generateSound()
}.apply { start() }
}
Expand All @@ -100,7 +103,7 @@ class OscillatorNode(context: BaseAudioContext) : AudioScheduledSourceNode(conte
}

isPlaying = false
audioTrack.stop()
playbackParameters.audioTrack.stop()
playbackThread?.join()
}.apply { start() }

Expand All @@ -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()
}
}
12 changes: 11 additions & 1 deletion cpp/AudioContext/AudioContextHostObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ namespace audiocontext {

std::vector<jsi::PropNameID> AudioContextHostObject::getPropertyNames(jsi::Runtime& runtime) {
std::vector<jsi::PropNameID> 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;
}

Expand Down Expand Up @@ -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!");
}

Expand Down
1 change: 1 addition & 0 deletions cpp/AudioContext/AudioContextHostObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "OscillatorNodeHostObject.h"
#include "AudioDestinationNodeHostObject.h"
#include "GainNodeHostObject.h"
#include "StereoPannerNodeHostObject.h"

namespace audiocontext
{
Expand Down
2 changes: 2 additions & 0 deletions cpp/AudioContext/AudioContextWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "OscillatorNodeWrapper.h"
#include "AudioDestinationNodeWrapper.h"
#include "GainNodeWrapper.h"
#include "StereoPannerNodeWrapper.h"

#ifdef ANDROID
#include "AudioContext.h"
Expand Down Expand Up @@ -38,5 +39,6 @@ namespace audiocontext {
std::shared_ptr<OscillatorNodeWrapper> createOscillator();
std::shared_ptr<AudioDestinationNodeWrapper> getDestination();
std::shared_ptr<GainNodeWrapper> createGain();
std::shared_ptr<StereoPannerNodeWrapper> createStereoPanner();
};
} // namespace audiocontext
5 changes: 5 additions & 0 deletions cpp/AudioContext/android/AudioContextWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,10 @@ namespace audiocontext {
auto gain = audiocontext_->createGain();
return std::make_shared<GainNodeWrapper>(gain);
}

std::shared_ptr<StereoPannerNodeWrapper> AudioContextWrapper::createStereoPanner() {
auto panner = audiocontext_->createStereoPanner();
return std::make_shared<StereoPannerNodeWrapper>(panner);
}
} // namespace audiocontext
#endif
Loading