-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implemented osicillator kotlin class with hybridclass and host …
…object
- Loading branch information
Maciej Makowski
committed
Jul 11, 2024
1 parent
20420a1
commit e602011
Showing
29 changed files
with
598 additions
and
259 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,20 @@ | ||
#include <jni.h> | ||
#include <jsi/jsi.h> | ||
#include "JSIExampleHostObject.h" | ||
// #include <jni.h> | ||
// #include <jsi/jsi.h> | ||
// #include "JSIExampleHostObject.h" | ||
|
||
using namespace facebook; | ||
// using namespace facebook; | ||
|
||
void install(jsi::Runtime& runtime) { | ||
auto hostObject = std::make_shared<example::JSIExampleHostObject>(); | ||
auto object = jsi::Object::createFromHostObject(runtime, hostObject); | ||
runtime.global().setProperty(runtime, "__JSIExampleProxy", std::move(object)); | ||
} | ||
// void install(jsi::Runtime& runtime) { | ||
// auto hostObject = std::make_shared<example::JSIExampleHostObject>(); | ||
// auto object = jsi::Object::createFromHostObject(runtime, hostObject); | ||
// runtime.global().setProperty(runtime, "__JSIExampleProxy", std::move(object)); | ||
// } | ||
|
||
extern "C" | ||
JNIEXPORT void JNICALL | ||
Java_com_audiocontext_jsi_JSIExampleModule_00024Companion_nativeInstall(JNIEnv *env, jobject clazz, jlong jsiPtr) { | ||
auto runtime = reinterpret_cast<jsi::Runtime*>(jsiPtr); | ||
if (runtime) { | ||
install(*runtime); | ||
} | ||
} | ||
// extern "C" | ||
// JNIEXPORT void JNICALL | ||
// Java_com_audiocontext_jsi_JSIExampleModule_00024Companion_nativeInstall(JNIEnv *env, jobject clazz, jlong jsiPtr) { | ||
// auto runtime = reinterpret_cast<jsi::Runtime*>(jsiPtr); | ||
// if (runtime) { | ||
// install(*runtime); | ||
// } | ||
// } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(const 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis, jlong jsContext) | ||
{ | ||
return makeCxxInstance(jThis, jsContext); | ||
} | ||
|
||
static void registerNatives() { | ||
javaClassStatic()->registerNatives({ | ||
makeNativeMethod("initHybrid", Oscillator::initHybrid), | ||
}); | ||
} | ||
|
||
void start(); | ||
void stop(); | ||
|
||
private: | ||
friend HybridBase; | ||
|
||
global_ref<Oscillator::javaobject> javaObject_; | ||
|
||
explicit Oscillator(const jni::alias_ref<Oscillator::jhybridobject>& jThis, jlong jsContext); | ||
}; | ||
|
||
} // namespace audiocontext |
6 changes: 3 additions & 3 deletions
6
...ava/com/audiocontext/JSIExamplePackage.kt → ...a/com/audiocontext/AudioContextPackage.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
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?; | ||
|
||
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, | ||
AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM | ||
) | ||
} | ||
|
||
external fun initHybrid(l: Long): HybridData? | ||
|
||
fun start() { | ||
if(isPlaying) return | ||
isPlaying = true | ||
audioTrack.play() | ||
playbackThread = Thread { generateSound() }.apply{ start()} | ||
} | ||
|
||
fun stop() { | ||
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 | ||
|
||
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() | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
android/src/main/java/com/audiocontext/context/AudioContext.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package com.audiocontext.context | ||
|
||
import android.media.AudioTrack | ||
import com.audiocontext.nodes.AudioDestinationNode | ||
import com.audiocontext.nodes.AudioNode | ||
import com.audiocontext.nodes.gain.GainNode | ||
import com.audiocontext.nodes.oscillator.OscillatorNode | ||
import java.util.concurrent.CopyOnWriteArrayList | ||
|
||
class AudioContext : BaseAudioContext { | ||
override var sampleRate: Int = 44100 | ||
override val destination: AudioDestinationNode = AudioDestinationNode(this) | ||
override val sources = CopyOnWriteArrayList<AudioNode>() | ||
|
||
override fun addNode(node: AudioNode) { | ||
sources.add(node) | ||
} | ||
|
||
override fun removeNode(node: AudioNode) { | ||
sources.remove(node) | ||
} | ||
|
||
override fun createOscillatorNode(): OscillatorNode { | ||
val oscillatorNode = OscillatorNode(this) | ||
addNode(oscillatorNode) | ||
return oscillatorNode | ||
} | ||
|
||
override fun createGainNode(): GainNode { | ||
val gainNode = GainNode(this) | ||
return gainNode | ||
} | ||
|
||
override fun dispatchAudio(buffer: ShortArray, audioTrack: AudioTrack) { | ||
val currentBuffer = buffer.clone() | ||
|
||
synchronized(sources) { | ||
sources.forEach { source -> | ||
source.process(currentBuffer, audioTrack) | ||
} | ||
} | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
android/src/main/java/com/audiocontext/context/AudioContextState.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.audiocontext.context | ||
|
||
enum class AudioContextState { | ||
SUSPENDED, | ||
RUNNING, | ||
CLOSED | ||
} |
21 changes: 21 additions & 0 deletions
21
android/src/main/java/com/audiocontext/context/BaseAudioContext.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.audiocontext.context | ||
|
||
import android.media.AudioTrack | ||
import android.provider.MediaStore.Audio | ||
import com.audiocontext.nodes.AudioDestinationNode | ||
import com.audiocontext.nodes.AudioNode | ||
import com.audiocontext.nodes.gain.GainNode | ||
import com.audiocontext.nodes.oscillator.OscillatorNode | ||
import java.util.concurrent.CopyOnWriteArrayList | ||
|
||
interface BaseAudioContext { | ||
val sampleRate: Int | ||
val destination: AudioDestinationNode | ||
val sources: List<AudioNode> | ||
|
||
fun createOscillatorNode(): OscillatorNode | ||
fun createGainNode(): GainNode | ||
fun dispatchAudio(buffer: ShortArray, audioTrack: AudioTrack) | ||
fun addNode(node: AudioNode) | ||
fun removeNode(node: AudioNode) | ||
} |
36 changes: 0 additions & 36 deletions
36
android/src/main/java/com/audiocontext/jsi/JSIExampleModule.kt
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.