Skip to content

Commit

Permalink
Merge pull request #38 from software-mansion-labs/feat/android/audio-…
Browse files Browse the repository at this point in the history
…node

Feat/android/audio node
  • Loading branch information
maciejmakowski2003 authored Jul 23, 2024
2 parents 86db614 + 679efb7 commit 2b8ba55
Show file tree
Hide file tree
Showing 25 changed files with 192 additions and 105 deletions.
6 changes: 6 additions & 0 deletions android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand Down
2 changes: 0 additions & 2 deletions android/src/main/cpp/AudioDestinationNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,4 @@ namespace audiocontext {

using namespace facebook::jni;

AudioDestinationNode::AudioDestinationNode(jni::alias_ref<AudioDestinationNode::jhybridobject> &jThis): javaObject_(make_global(jThis)) {}

} // namespace audiocontext
21 changes: 1 addition & 20 deletions android/src/main/cpp/AudioDestinationNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,9 @@ namespace audiocontext {

class OscillatorNode;

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

static jni::local_ref<AudioDestinationNode::jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis)
{
return makeCxxInstance(jThis);
}

static void registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", AudioDestinationNode::initHybrid),
});
}

private:
friend HybridBase;
friend class OscillatorNode;

global_ref<AudioDestinationNode::javaobject> javaObject_;

explicit AudioDestinationNode(jni::alias_ref<AudioDestinationNode::jhybridobject>& jThis);
};

} // namespace audiocontext
18 changes: 18 additions & 0 deletions android/src/main/cpp/AudioNode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include "AudioNode.h"

namespace audiocontext {

AudioNode::AudioNode(alias_ref<AudioNode::jhybridobject> &jThis): javaObject_(make_global(jThis)) {}

void AudioNode::connect(const std::shared_ptr<AudioNode> node) {
static const auto method = javaClassLocal()->getMethod<void(AudioNode::javaobject)>(
"connect");
method(javaObject_.get(), node->javaObject_.get());
}

void AudioNode::disconnect(const std::shared_ptr<AudioNode> node) {
static const auto method = javaClassLocal()->getMethod<void(AudioNode::javaobject)>(
"disconnect");
method(javaObject_.get(), node->javaObject_.get());
}
}
40 changes: 40 additions & 0 deletions android/src/main/cpp/AudioNode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#pragma once

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

namespace audiocontext {

using namespace facebook;
using namespace facebook::jni;

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

static jni::local_ref<AudioNode::jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis)
{
return makeCxxInstance(jThis);
}

static void registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", AudioNode::initHybrid),
});
}

void connect(const std::shared_ptr<AudioNode> node);
void disconnect(const std::shared_ptr<AudioNode> node);

protected:
friend HybridBase;
friend class AudioNode;

global_ref<AudioNode::javaobject> javaObject_;

explicit AudioNode(jni::alias_ref<AudioNode::jhybridobject>& jThis);
};

} // namespace audiocontext
2 changes: 2 additions & 0 deletions android/src/main/cpp/OnLoad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "OscillatorNode.h"
#include "AudioContext.h"
#include "AudioDestinationNode.h"
#include "AudioNode.h"

using namespace audiocontext;

Expand All @@ -11,5 +12,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
OscillatorNode::registerNatives();
AudioContext::registerNatives();
AudioDestinationNode::registerNatives();
AudioNode::registerNatives();
});
}
8 changes: 0 additions & 8 deletions android/src/main/cpp/OscillatorNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ namespace audiocontext {

using namespace facebook::jni;

OscillatorNode::OscillatorNode(jni::alias_ref<OscillatorNode::jhybridobject> &jThis)
: javaObject_(make_global(jThis)){}

void OscillatorNode::start(double time) {
static const auto method = javaClassLocal()->getMethod<void(jdouble)>("start");
method(javaObject_.get(), time);
Expand Down Expand Up @@ -47,9 +44,4 @@ namespace audiocontext {
method(javaObject_.get(), *make_jstring(waveType));

}

void OscillatorNode::connect(const AudioDestinationNode &destination) {
const auto method = javaClassLocal()->getMethod<void(AudioDestinationNode::javaobject)>("connect");
method(javaObject_.get(), destination.javaObject_.get());
}
} // namespace audiocontext
35 changes: 10 additions & 25 deletions android/src/main/cpp/OscillatorNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,31 @@
#include <react/jni/CxxModuleWrapper.h>
#include <react/jni/JMessageQueueThread.h>
#include <memory>
#include "AudioDestinationNode.h"
#include "AudioNode.h"

namespace audiocontext {

using namespace facebook;
using namespace facebook::jni;

class AudioDestinationNode;

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

static jni::local_ref<OscillatorNode::jhybriddata> initHybrid(jni::alias_ref<jhybridobject> 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<OscillatorNode::javaobject> javaObject_;
void setDetune(double detune);

explicit OscillatorNode(jni::alias_ref<OscillatorNode::jhybridobject>& jThis);
void setWaveType(const std::string &waveType);
};

} // namespace audiocontext
}// namespace audiocontext
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -10,20 +11,14 @@ 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 {
System.loadLibrary("react-native-audio-context")
}
}

init {
mHybridData = initHybrid()
}

external fun initHybrid(): HybridData?

override fun process(buffer: ShortArray, audioTrack: AudioTrack) {
audioTrack.write(buffer, 0, buffer.size)
}
Expand Down
20 changes: 18 additions & 2 deletions android/src/main/java/com/audiocontext/nodes/AudioNode.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
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) {
abstract val numberOfInputs: Int;
abstract val numberOfOutputs: Int;
private val connectedNodes = mutableListOf<AudioNode>()

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)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion cpp/AudioContext/AudioContextHostObject.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include <jsi/jsi.h>

#include <utility>
#include "AudioContextWrapper.h"
#include "OscillatorNodeHostObject.h"
#include "AudioDestinationNodeHostObject.h"
Expand All @@ -19,7 +21,7 @@ namespace audiocontext
public:
explicit AudioContextHostObject(std::shared_ptr<AudioContextWrapper> wrapper) : wrapper_(wrapper) {}

static void createAndInstallFromWrapper(std::shared_ptr<AudioContextWrapper> wrapper, jlong jsContext) {
static void createAndInstallFromWrapper(const std::shared_ptr<AudioContextWrapper>& wrapper, jlong jsContext) {
auto runtime = reinterpret_cast<jsi::Runtime *>(jsContext);
auto hostObject = std::make_shared<AudioContextHostObject>(wrapper);
auto object = jsi::Object::createFromHostObject(*runtime, hostObject);
Expand Down
1 change: 1 addition & 0 deletions cpp/AudioContext/AudioContextWrapper.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <memory>
#include <utility>
#include "OscillatorNodeWrapper.h"
#include "AudioDestinationNodeWrapper.h"

Expand Down
10 changes: 4 additions & 6 deletions cpp/AudioDestinationNode/AudioDestinationNodeHostObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,21 @@

#include <jsi/jsi.h>
#include "AudioDestinationNodeWrapper.h"
#include "OscillatorNodeWrapper.h"
#include "AudioNodeHostObject.h"

namespace audiocontext {
using namespace facebook;

class AudioDestinationNodeWrapper;
class OscillatorNodeWrapper;

class AudioDestinationNodeHostObject : public jsi::HostObject {

private:
friend class OscillatorNodeHostObject;
class AudioDestinationNodeHostObject : public AudioNodeHostObject {

protected:
std::shared_ptr<AudioDestinationNodeWrapper> wrapper_;

public:
explicit AudioDestinationNodeHostObject(std::shared_ptr<AudioDestinationNodeWrapper> wrapper): wrapper_(wrapper) {}
explicit AudioDestinationNodeHostObject(std::shared_ptr<AudioDestinationNodeWrapper> 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;
Expand Down
Loading

0 comments on commit 2b8ba55

Please sign in to comment.