Skip to content
Open
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
2 changes: 2 additions & 0 deletions packages/react-native-audio-api/RNAudioAPI.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ worklets_preprocessor_flag = worklets_enabled ? '-DRN_AUDIO_API_ENABLE_WORKLETS=

ffmpeg_flag = $RN_AUDIO_API_FFMPEG_DISABLED ? '-DRN_AUDIO_API_FFMPEG_DISABLED=1' : ''
skip_ffmpeg_argument = $RN_AUDIO_API_FFMPEG_DISABLED ? 'skipffmpeg' : ''
mac_catalyst_cflags = '-DMA_NO_LIBOPUS -DMA_NO_LIBVORBIS'

Pod::Spec.new do |s|
s.name = "RNAudioAPI"
Expand Down Expand Up @@ -103,6 +104,7 @@ Pod::Spec.new do |s|
"CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
"GCC_PREPROCESSOR_DEFINITIONS" => '$(inherited) HAVE_ACCELERATE=1',
'OTHER_CFLAGS' => "$(inherited) #{fabric_flags} #{version_flag} #{worklets_preprocessor_flag} #{ffmpeg_flag}",
'OTHER_CFLAGS[sdk=macosx*]' => "$(inherited) #{fabric_flags} #{version_flag} #{worklets_preprocessor_flag} #{ffmpeg_flag} #{mac_catalyst_cflags}",
}

s.xcconfig = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,11 @@ class AudioAPIModuleInstaller {
auto sampleRate = static_cast<float>(args[0].getNumber());

#if RN_AUDIO_API_ENABLE_WORKLETS
auto runtimeRegistry = RuntimeRegistry{
.uiRuntime = uiRuntime,
.audioRuntime = worklets::extractWorkletRuntime(runtime, args[1])};
auto runtimeRegistry = RuntimeRegistry{.uiRuntime = uiRuntime};
if (count > 1 && args[1].isObject()) {
runtimeRegistry.audioRuntime =
worklets::extractWorkletRuntime(runtime, args[1]);
}
#else
auto runtimeRegistry = RuntimeRegistry{};
#endif
Expand Down Expand Up @@ -109,9 +111,11 @@ class AudioAPIModuleInstaller {
auto sampleRate = static_cast<float>(args[2].getNumber());

#if RN_AUDIO_API_ENABLE_WORKLETS
auto runtimeRegistry = RuntimeRegistry{
.uiRuntime = uiRuntime,
.audioRuntime = worklets::extractWorkletRuntime(runtime, args[3])};
auto runtimeRegistry = RuntimeRegistry{.uiRuntime = uiRuntime};
if (count > 3 && args[3].isObject()) {
runtimeRegistry.audioRuntime =
worklets::extractWorkletRuntime(runtime, args[3]);
}
#else
auto runtimeRegistry = RuntimeRegistry{};
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,14 @@ WorkletsRunner::WorkletsRunner(
std::weak_ptr<worklets::WorkletRuntime> weakRuntime,
const std::shared_ptr<worklets::SerializableWorklet> &shareableWorklet,
bool shouldLockRuntime)
: weakRuntime_(std::move(weakRuntime)), shouldLockRuntime(shouldLockRuntime) {
auto strongRuntime = weakRuntime_.lock();
if (strongRuntime == nullptr) {
return;
}
#if RN_AUDIO_API_ENABLE_WORKLETS
unsafeRuntimePtr = &strongRuntime->getJSIRuntime();
strongRuntime->executeSync([this, shareableWorklet](jsi::Runtime &rt) -> jsi::Value {
/// Placement new to avoid dynamic memory allocation
new (reinterpret_cast<jsi::Function *>(&unsafeWorklet))
jsi::Function(shareableWorklet->toJSValue(*unsafeRuntimePtr)
.asObject(*unsafeRuntimePtr)
.asFunction(*unsafeRuntimePtr));
return jsi::Value::undefined();
});
workletInitialized = true;
#else
unsafeRuntimePtr = nullptr;
workletInitialized = false;
#endif
: weakRuntime_(std::move(weakRuntime)),
shareableWorklet_(shareableWorklet),
shouldLockRuntime(shouldLockRuntime) {
}

WorkletsRunner::WorkletsRunner(WorkletsRunner &&other)
: weakRuntime_(std::move(other.weakRuntime_)),
shareableWorklet_(std::move(other.shareableWorklet_)),
unsafeRuntimePtr(other.unsafeRuntimePtr),
workletInitialized(other.workletInitialized),
shouldLockRuntime(other.shouldLockRuntime) {
Expand All @@ -55,14 +39,42 @@ WorkletsRunner::~WorkletsRunner() {
workletInitialized = false;
}

bool WorkletsRunner::ensureWorkletInitialized(jsi::Runtime &rt) {
#if RN_AUDIO_API_ENABLE_WORKLETS
if (workletInitialized) {
return true;
}
if (!shareableWorklet_) {
return false;
}
auto valueUnpacker = rt.global().getProperty(rt, "__valueUnpacker");
if (!valueUnpacker.isObject()) {
return false;
}
unsafeRuntimePtr = &rt;
/// Placement new to avoid dynamic memory allocation
new (reinterpret_cast<jsi::Function *>(&unsafeWorklet))
jsi::Function(shareableWorklet_->toJSValue(rt).asObject(rt).asFunction(rt));
workletInitialized = true;
return true;
#else
(void)rt;
return false;
#endif
}

std::optional<jsi::Value> WorkletsRunner::executeOnRuntimeGuarded(
const std::function<jsi::Value(jsi::Runtime &)> &&job) const noexcept(noexcept(job)) {
auto strongRuntime = weakRuntime_.lock();
if (strongRuntime == nullptr) {
return std::nullopt;
}
#if RN_AUDIO_API_ENABLE_WORKLETS
return strongRuntime->executeSync(std::move(job));
auto &rt = strongRuntime->getJSIRuntime();
if (!const_cast<WorkletsRunner *>(this)->ensureWorkletInitialized(rt)) {
return std::nullopt;
}
return job(rt);
#else
return std::nullopt;
#endif
Expand All @@ -71,7 +83,18 @@ std::optional<jsi::Value> WorkletsRunner::executeOnRuntimeGuarded(
std::optional<jsi::Value> WorkletsRunner::executeOnRuntimeUnsafe(
const std::function<jsi::Value(jsi::Runtime &)> &&job) const noexcept(noexcept(job)) {
#if RN_AUDIO_API_ENABLE_WORKLETS
return job(*unsafeRuntimePtr);
jsi::Runtime *rt = unsafeRuntimePtr;
if (rt == nullptr) {
auto strongRuntime = weakRuntime_.lock();
if (strongRuntime == nullptr) {
return std::nullopt;
}
rt = &strongRuntime->getJSIRuntime();
}
if (!const_cast<WorkletsRunner *>(this)->ensureWorkletInitialized(*rt)) {
return std::nullopt;
}
return job(*rt);
#else
return std::nullopt;
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ class WorkletsRunner {
}

private:
std::weak_ptr<worklets::WorkletRuntime> weakRuntime_;
std::weak_ptr<worklets::WorkletRuntime> weakRuntime_;
std::shared_ptr<worklets::SerializableWorklet> shareableWorklet_;
jsi::Runtime *unsafeRuntimePtr = nullptr;

/// @note We want to avoid automatic destruction as
Expand All @@ -81,6 +82,8 @@ class WorkletsRunner {
return *reinterpret_cast<jsi::Function *>(&unsafeWorklet);
}

bool ensureWorkletInitialized(jsi::Runtime &rt);

std::optional<jsi::Value> executeOnRuntimeGuarded(
const std::function<jsi::Value(jsi::Runtime &)> &&job) const noexcept(noexcept(job));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <type_traits>
#include <utility>

namespace audioapi {

struct NoneType {};
inline constexpr NoneType None{};

Expand Down Expand Up @@ -321,3 +323,5 @@ class Result {
};
bool is_ok_;
};

} // namespace audioapi
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#import <TargetConditionals.h>
#import <audioapi/ios/core/NativeAudioRecorder.h>
#import <audioapi/ios/system/AudioEngine.h>
#import <audioapi/ios/system/AudioSessionManager.h>
Expand Down Expand Up @@ -61,6 +62,12 @@ - (AVAudioFormat *)getInputFormat

- (int)getBufferSize
{
#if TARGET_OS_MACCATALYST
AVAudioFormat *format = [self getInputFormat];
double sampleRate = format.sampleRate > 0 ? format.sampleRate : 48000.0;
float bufferDuration = 0.2;
return nextPowerOfTwo(ceil(bufferDuration * sampleRate));
#else
// NOTE: this method should be called only after the session is activated
AVAudioSession *audioSession = [AVAudioSession sharedInstance];

Expand All @@ -72,6 +79,7 @@ - (int)getBufferSize

// IOS returns buffer duration rounded, but expects the buffer size to be power of two in runtime
return nextPowerOfTwo(ceil(bufferDuration * audioSession.sampleRate));
#endif
}

- (void)start
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#import <TargetConditionals.h>
#import <audioapi/ios/system/AudioEngine.h>
#import <audioapi/ios/system/AudioSessionManager.h>

Expand Down Expand Up @@ -278,6 +279,10 @@ - (void)restartAudioEngine

- (void)logAudioEngineState
{
#if TARGET_OS_MACCATALYST
NSLog(@"[AudioEngine] logAudioEngineState is not available on Mac Catalyst.");
return;
#else
AVAudioSession *session = [AVAudioSession sharedInstance];

NSLog(@"================ 🎧 AVAudioEngine STATE ================");
Expand Down Expand Up @@ -312,6 +317,7 @@ - (void)logAudioEngineState
NSLog(@"📐 Engine output format: %.0f Hz, %u channels", format.sampleRate, format.channelCount);

NSLog(@"=======================================================");
#endif
}

@end
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
#pragma once

#import <AVFoundation/AVFoundation.h>
#import <TargetConditionals.h>
#import <Foundation/Foundation.h>
#if !TARGET_OS_MACCATALYST
#import <AVFoundation/AVFoundation.h>
#else
@class AVAudioSession;
@class AVAudioSessionPortDescription;
#endif
#import <React/RCTBridgeModule.h>

@interface AudioSessionManager : NSObject
Expand All @@ -13,9 +19,15 @@
@property (nonatomic, assign) bool shouldManageSession;

// Session configuration options (desired by user)
#if TARGET_OS_MACCATALYST
@property (nonatomic, copy) NSString *desiredMode;
@property (nonatomic, copy) NSString *desiredCategory;
@property (nonatomic, assign) NSUInteger desiredOptions;
#else
@property (nonatomic, assign) AVAudioSessionMode desiredMode;
@property (nonatomic, assign) AVAudioSessionCategory desiredCategory;
@property (nonatomic, assign) AVAudioSessionCategoryOptions desiredOptions;
#endif
@property (nonatomic, assign) bool allowHapticsAndSounds;
@property (nonatomic, assign) bool notifyOthersOnDeactivation;

Expand Down
Loading