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/oscillator #24

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 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
33 changes: 21 additions & 12 deletions android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,31 @@ project(react-native-audio-context)
set (CMAKE_VERBOSE_MAKEFILE ON)
set (CMAKE_CXX_STANDARD 14)

add_library(react-native-audio-context
SHARED
../cpp/JSIExampleHostObject.cpp
cpp-adapter.cpp
)
include(../node_modules/react-native/ReactAndroid/cmake-utils/folly-flags.cmake)
add_compile_options(${folly_FLAGS})

# Specifies a path to native header files.
include_directories(
../cpp
../node_modules/react-native/ReactCommon/jsi
../cpp
src/main/cpp
../node_modules/react-native/ReactCommon/jsi
../node_modules/react-native/ReactAndroid/src/main/jni/react/jni
../node_modules/react-native/ReactAndroid/src/main/jni/third-party/folly
)

add_library(react-native-audio-context SHARED
src/main/cpp/OnLoad.cpp
src/main/cpp/Oscillator
../cpp/OscillatorHostObject
)

find_package(ReactAndroid REQUIRED CONFIG)
find_package(fbjni REQUIRED CONFIG)

target_link_libraries(
react-native-audio-context
ReactAndroid::jsi
android
target_link_libraries(react-native-audio-context
ReactAndroid::jsi
ReactAndroid::reactnativejni
fbjni::fbjni
ReactAndroid::folly_runtime
android
log
)
5 changes: 5 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ android {
}
}

packagingOptions {
excludes = ["**/libc++_shared.so", "**/libfbjni.so", "**/libjsi.so", "**/libreactnativejni.so", "**/libfolly_json.so", "**/libreanimated.so", "**/libjscexecutor.so", "**/libhermes.so"]
}

externalNativeBuild {
cmake {
path "CMakeLists.txt"
Expand Down Expand Up @@ -125,6 +129,7 @@ dependencies {
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+"
implementation 'androidx.core:core-ktx:1.13.1'
implementation 'com.facebook.fbjni:fbjni:0.6.0'
}

if (isNewArchitectureEnabled()) {
Expand Down
20 changes: 0 additions & 20 deletions android/cpp-adapter.cpp

This file was deleted.

12 changes: 12 additions & 0 deletions android/src/main/cpp/OnLoad.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <fbjni/fbjni.h>
#include "Oscillator.h"
#include "OscillatorHostObject.h"

using namespace audiocontext;

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
return facebook::jni::initialize(vm, [] {
Oscillator::registerNatives();
});
}
27 changes: 27 additions & 0 deletions android/src/main/cpp/Oscillator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include "Oscillator.h"
#include <fbjni/fbjni.h>
#include <jsi/jsi.h>

namespace audiocontext {

using namespace facebook::jni;

Oscillator::Oscillator(jni::alias_ref<Oscillator::jhybridobject> &jThis,
jlong jsContext): javaObject_(make_global(jThis)) {
auto runtime = reinterpret_cast<jsi::Runtime *>(jsContext);
auto hostObject = std::make_shared<OscillatorHostObject>(this);
auto object = jsi::Object::createFromHostObject(*runtime, hostObject);
runtime->global().setProperty(*runtime, "__OscillatorProxy", std::move(object));
}

void Oscillator::start() {
static const auto method = javaClassStatic()->getMethod<void()>("start");
method(javaObject_.get());
}

void Oscillator::stop() {
static const auto method = javaClassStatic()->getMethod<void()>("stop");
method(javaObject_.get());
}

} // namespace audiocontext
40 changes: 40 additions & 0 deletions android/src/main/cpp/Oscillator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#pragma once

#include <fbjni/fbjni.h>
#include <jsi/jsi.h>
#include <react/jni/CxxModuleWrapper.h>
#include <react/jni/JMessageQueueThread.h>
#include "OscillatorHostObject.h"

namespace audiocontext {

using namespace facebook;
using namespace facebook::jni;

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

static jni::local_ref<Oscillator::jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis, jlong jsContext)
{
return makeCxxInstance(jThis, jsContext);
}

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

void start();
void stop();

private:
friend HybridBase;

global_ref<Oscillator::javaobject> javaObject_;

explicit Oscillator(jni::alias_ref<Oscillator::jhybridobject>& jThis, jlong jsContext);
};

} // namespace audiocontext
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.audiocontext

import com.audiocontext.jsi.JSIExampleModule
import com.audiocontext.nativemodules.AudioContextModule
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager

class JSIExamplePackage : ReactPackage {
class AudioContextPackage : ReactPackage {
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
return listOf<NativeModule>(JSIExampleModule(reactContext))
return listOf<NativeModule>(AudioContextModule(reactContext))
}

override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
Expand Down
82 changes: 82 additions & 0 deletions android/src/main/java/com/audiocontext/Oscillator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.audiocontext

import android.media.AudioFormat
import android.media.AudioManager
import android.media.AudioTrack
import com.audiocontext.nodes.oscillator.WaveType
import com.facebook.jni.HybridData
import com.facebook.react.bridge.ReactApplicationContext
import kotlin.math.abs
import kotlin.math.floor
import kotlin.math.sin

class Oscillator(reactContext: ReactApplicationContext) {
val numberOfInputs: Int = 0
val numberOfOutputs: Int = 1
private var frequency: Double = 440.0
private var detune: Double = 0.0
private var waveType: WaveType = WaveType.SINE

private val audioTrack: AudioTrack
@Volatile private var isPlaying: Boolean = false
private var playbackThread: Thread? = null
private var buffer: ShortArray = ShortArray(1024)

private val mHybridData: HybridData?;

companion object {
init {
System.loadLibrary("react-native-audio-context")
}
}

init {
mHybridData = initHybrid(reactContext.javaScriptContextHolder!!.get())

val bufferSize = AudioTrack.getMinBufferSize(
44100,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)
this.audioTrack = AudioTrack(
AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would to be good to move 44100 to be a constant

AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM
)
}

external fun initHybrid(l: Long): HybridData?

fun start() {
if(isPlaying) return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if(isPlaying) return
if (isPlaying) {
return
}

:)

isPlaying = true
audioTrack.play()
playbackThread = Thread { generateSound() }.apply{ start()}
}

fun stop() {
if(!isPlaying) return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if(!isPlaying) return
if (!isPlaying) {
return
}

:)

isPlaying = false
audioTrack.stop()
playbackThread?.join()
}

private fun generateSound() {
var wavePhase = 0.0
var phaseChange: Double

while(isPlaying) {
phaseChange = 2 * Math.PI * (frequency + detune) / 44100
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and use it also here :)


for(i in buffer.indices) {
buffer[i] = when(waveType) {
WaveType.SINE -> (sin(wavePhase) * Short.MAX_VALUE).toInt().toShort()
WaveType.SQUARE -> ((if (sin(wavePhase) >= 0) 1 else -1) * Short.MAX_VALUE).toShort()
WaveType.SAWTOOTH -> ((2 * (wavePhase / (2 * Math.PI) - floor(wavePhase / (2 * Math.PI) + 0.5))) * Short.MAX_VALUE).toInt().toShort()
WaveType.TRIANGLE -> ((2 * abs(2 * (wavePhase / (2 * Math.PI) - floor(wavePhase / (2 * Math.PI) + 0.5))) - 1) * Short.MAX_VALUE).toInt().toShort()
}
wavePhase += phaseChange
}

audioTrack.write(buffer, 0, buffer.size)
}
audioTrack.flush()
}
}
8 changes: 8 additions & 0 deletions android/src/main/java/com/audiocontext/WaveType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.audiocontext.nodes.oscillator

enum class WaveType {
SINE,
SQUARE,
SAWTOOTH,
TRIANGLE
}
36 changes: 0 additions & 36 deletions android/src/main/java/com/audiocontext/jsi/JSIExampleModule.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.audiocontext.nativemodules

import com.audiocontext.Oscillator
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod

class AudioContextModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
override fun getName(): String {
return "AudioContextModule"
}

@ReactMethod(isBlockingSynchronousMethod = true)
fun createOscillator() {
Oscillator(reactContext)
}
}
45 changes: 0 additions & 45 deletions cpp/JSIExampleHostObject.cpp

This file was deleted.

Loading