Skip to content
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
29 changes: 22 additions & 7 deletions apps/common-app/src/examples/Worklets/Worklets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,19 @@ function Worklets() {
const bar4 = useSharedValue(0);

useEffect(() => {
if (!aCtxRef.current) {
aCtxRef.current = new AudioContext({ sampleRate: SAMPLE_RATE });
}

AudioManager.setAudioSessionOptions({
iosCategory: 'playAndRecord',
iosMode: 'spokenAudio',
iosOptions: ['defaultToSpeaker', 'allowBluetoothA2DP'],
});

return () => {
aCtxRef.current?.close();
};
}, []);

const start = () => {
Expand All @@ -45,7 +53,7 @@ function Worklets() {
inputAudioData: Array<Float32Array>,
outputAudioData: Array<Float32Array>,
framesToProcess: number,
currentTime: number
_currentTime: number
) => {
'worklet';
const gain = 0.5;
Expand All @@ -62,28 +70,32 @@ function Worklets() {
audioData: Array<Float32Array>,
framesToProcess: number,
currentTime: number,
startOffset: number
_startOffset: number
) => {
'worklet';
const frequency = 440; // A4 note
const baseAmplitude = 0.2;

const modulationFreq = 2; // 2 Hz modulation
const modulationDepth = 0.8;
const amplitudeModulation = Math.sin(2 * Math.PI * modulationFreq * currentTime);
const dynamicAmplitude = baseAmplitude * (1 + modulationDepth * amplitudeModulation);
const amplitudeModulation = Math.sin(
2 * Math.PI * modulationFreq * currentTime
);
const dynamicAmplitude =
baseAmplitude * (1 + modulationDepth * amplitudeModulation);

for (let channel = 0; channel < audioData.length; channel++) {
for (let sample = 0; sample < framesToProcess; sample++) {
// Calculate phase based on sample position and time
const phase = 2 * Math.PI * frequency * (currentTime + sample / SAMPLE_RATE);
const phase =
2 * Math.PI * frequency * (currentTime + sample / SAMPLE_RATE);
audioData[channel][sample] = dynamicAmplitude * Math.sin(phase);
}
}
};
const worklet = (
audioData: Array<Float32Array>,
inputChannelCount: number
_inputChannelCount: number
) => {
'worklet';

Expand All @@ -106,7 +118,10 @@ function Worklets() {
bar2.value = withSpring(scaledAmplitude, { damping: 15, stiffness: 200 });
};

aCtxRef.current = new AudioContext({ sampleRate: SAMPLE_RATE });
if (!aCtxRef.current) {
aCtxRef.current = new AudioContext({ sampleRate: SAMPLE_RATE });
}

workletSourceNodeRef.current = aCtxRef.current.createWorkletSourceNode(
sourceWorklet,
'AudioRuntime'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ void AudioAPIModule::registerNatives() {
makeNativeMethod(
"invokeHandlerWithEventNameAndEventBody",
AudioAPIModule::invokeHandlerWithEventNameAndEventBody),
makeNativeMethod("closeAllContexts", AudioAPIModule::closeAllContexts),
});
}

Expand Down Expand Up @@ -101,4 +102,8 @@ void AudioAPIModule::invokeHandlerWithEventNameAndEventBody(
eventName->toStdString(), body);
}
}

void AudioAPIModule::closeAllContexts() {
AudioAPIModuleInstaller::closeAllContexts();
}
} // namespace audioapi
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class AudioAPIModule : public jni::HybridClass<AudioAPIModule> {

void injectJSIBindings();
void invokeHandlerWithEventNameAndEventBody(jni::alias_ref<jni::JString> eventName, jni::alias_ref<jni::JMap<jstring, jobject>> eventBody);
void closeAllContexts();

private:
friend HybridBase;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <audioapi/core/utils/Constants.h>
#include <audioapi/utils/AudioArray.h>
#include <audioapi/utils/AudioBus.h>
#include <jni.h>

namespace audioapi {

Expand Down Expand Up @@ -49,7 +50,8 @@ bool AudioPlayer::openAudioStream() {

bool AudioPlayer::start() {
if (mStream_) {
nativeAudioPlayer_->start();
jni::ThreadScope::WithClassLoader(
[this]() { nativeAudioPlayer_->start(); });
auto result = mStream_->requestStart();
return result == oboe::Result::OK;
}
Expand All @@ -59,7 +61,7 @@ bool AudioPlayer::start() {

void AudioPlayer::stop() {
if (mStream_) {
nativeAudioPlayer_->stop();
jni::ThreadScope::WithClassLoader([this]() { nativeAudioPlayer_->stop(); });
mStream_->requestStop();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.swmansion.audioapi

import com.facebook.jni.HybridData
import com.facebook.react.bridge.LifecycleEventListener
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReadableArray
Expand All @@ -16,14 +18,16 @@ import java.lang.ref.WeakReference
@ReactModule(name = AudioAPIModule.NAME)
class AudioAPIModule(
reactContext: ReactApplicationContext,
) : NativeAudioAPIModuleSpec(reactContext) {
) : NativeAudioAPIModuleSpec(reactContext),
LifecycleEventListener {
companion object {
const val NAME = NativeAudioAPIModuleSpec.NAME
}

val reactContext: WeakReference<ReactApplicationContext> = WeakReference(reactContext)

private val mHybridData: HybridData
private var reanimatedModule: NativeModule? = null

external fun initHybrid(
workletsModule: Any?,
Expand All @@ -38,6 +42,8 @@ class AudioAPIModule(
eventBody: Map<String, Any>,
)

private external fun closeAllContexts()

init {
try {
System.loadLibrary("react-native-audio-api")
Expand All @@ -47,6 +53,8 @@ class AudioAPIModule(
if (BuildConfig.RN_AUDIO_API_ENABLE_WORKLETS) {
try {
workletsModule = reactContext.getNativeModule("WorkletsModule")
reanimatedModule = reactContext.getNativeModule("ReanimatedModule")
reanimatedModule
} catch (ex: Exception) {
throw RuntimeException("WorkletsModule not found - make sure react-native-worklets is properly installed")
}
Expand All @@ -64,6 +72,26 @@ class AudioAPIModule(
return true
}

override fun onHostResume() {
// do nothing
}

override fun onHostPause() {
closeAllContexts()
}

override fun onHostDestroy() {
// do nothing
}

override fun initialize() {
reactContext.get()?.addLifecycleEventListener(this)
}

override fun invalidate() {
// think about cleaning up resources, singletons etc.
}

override fun getDevicePreferredSampleRate(): Double = MediaSessionManager.getDevicePreferredSampleRate()

override fun setAudioSessionActivity(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@
#include <audioapi/core/utils/worklets/SafeIncludes.h>

#include <memory>
#include <vector>

namespace audioapi {

using namespace facebook;

class AudioAPIModuleInstaller {
private:
inline static std::vector<std::weak_ptr<AudioContext>> contexts_ = {};

public:
static void injectJSIBindings(
jsi::Runtime *jsiRuntime,
Expand Down Expand Up @@ -61,6 +65,19 @@ class AudioAPIModuleInstaller {
*jsiRuntime, audioEventHandlerRegistryHostObject));
}

static void closeAllContexts() {
for (auto it = contexts_.begin(); it != contexts_.end(); ++it) {
auto weakContext = *it;

if (auto context = weakContext.lock()) {
context->close();
}

it = contexts_.erase(it);
--it;
}
}

private:
static jsi::Function getCreateAudioContextFunction(
jsi::Runtime *jsiRuntime,
Expand Down Expand Up @@ -95,6 +112,8 @@ class AudioAPIModuleInstaller {
initSuspended,
audioEventHandlerRegistry,
runtimeRegistry);
AudioAPIModuleInstaller::contexts_.push_back(audioContext);

auto audioContextHostObject =
std::make_shared<AudioContextHostObject>(
audioContext, &runtime, jsCallInvoker);
Expand Down Expand Up @@ -138,6 +157,7 @@ class AudioAPIModuleInstaller {
sampleRate,
audioEventHandlerRegistry,
runtimeRegistry);

auto audioContextHostObject =
std::make_shared<OfflineAudioContextHostObject>(
offlineAudioContext, &runtime, jsCallInvoker);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTCallInvokerModule.h>
#import <React/RCTInvalidating.h>
#import <rnaudioapi/rnaudioapi.h>
#else // RCT_NEW_ARCH_ENABLED
#import <React/RCTBridgeModule.h>
Expand All @@ -14,7 +15,7 @@

@interface AudioAPIModule : RCTEventEmitter
#ifdef RCT_NEW_ARCH_ENABLED
<NativeAudioAPIModuleSpec, RCTCallInvokerModule>
<NativeAudioAPIModuleSpec, RCTCallInvokerModule, RCTInvalidating>
#else
<RCTBridgeModule>
#endif // RCT_NEW_ARCH_ENABLED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ - (void)invalidate

_eventHandler = nullptr;

audioapi::AudioAPIModuleInstaller::closeAllContexts();

[super invalidate];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ class IOSAudioRecorder : public AudioRecorder {
IOSAudioRecorder(
float sampleRate,
int bufferLength,
const std::shared_ptr<AudioEventHandlerRegistry>
&audioEventHandlerRegistry);
const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry);

~IOSAudioRecorder() override;

Expand Down