diff --git a/packages/audiodocs/docs/core/audio-context.mdx b/packages/audiodocs/docs/core/audio-context.mdx index 45eaebeca..cb96ccb3c 100644 --- a/packages/audiodocs/docs/core/audio-context.mdx +++ b/packages/audiodocs/docs/core/audio-context.mdx @@ -21,6 +21,7 @@ constructor(options?: AudioContextOptions) | Parameter | Type | Default | | | :---: | :---: | :----: | :---- | | `sampleRate` | `number` | - | The preferred sample rate for the context. | +| `androidOutputProfile` | `'media'` \| `'voiceCommunication'` | `'media'` | Android output classification. Use `'voiceCommunication'` for interactive voice calls. | #### Errors diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp index 38371018f..4bbdf0217 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp @@ -19,14 +19,16 @@ AudioPlayer::AudioPlayer( int channelCount, std::mutex *driverMutex, const std::shared_ptr &context, - std::atomic ¤tRenders) + std::atomic ¤tRenders, + AndroidOutputProfile outputProfile) : renderAudio_(renderAudio), currentRenders_(currentRenders), sampleRate_(sampleRate), channelCount_(channelCount), isRunning_(false), driverMutex_(driverMutex), - context_(context) {} + context_(context), + outputProfile_(outputProfile) {} bool AudioPlayer::openAudioStream() { std::scoped_lock lock(streamMutex_); @@ -43,6 +45,10 @@ bool AudioPlayer::openAudioStream() { ->setErrorCallback(shared_from_this()) ->setSampleRate(static_cast(sampleRate_)); + if (outputProfile_ == AndroidOutputProfile::VoiceCommunication) { + builder.setUsage(Usage::VoiceCommunication)->setContentType(ContentType::Speech); + } + auto result = builder.openStream(mStream_); if (result != oboe::Result::OK || mStream_ == nullptr) { __android_log_print( diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h index 2f9a4ac01..b0c651fdb 100644 --- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h +++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioPlayer.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace audioapi { @@ -29,7 +30,8 @@ class AudioPlayer : public CommonPlayer, int channelCount, std::mutex *driverMutex, const std::shared_ptr &context, - std::atomic ¤tRenders); + std::atomic ¤tRenders, + AndroidOutputProfile outputProfile); ~AudioPlayer() override { cleanup(); @@ -67,6 +69,7 @@ class AudioPlayer : public CommonPlayer, std::atomic lastCallbackFrameCount_{0}; std::mutex *driverMutex_; std::weak_ptr context_; + AndroidOutputProfile outputProfile_; bool openAudioStream(); }; diff --git a/packages/react-native-audio-api/common/cpp/audioapi/AudioAPIModuleInstaller.h b/packages/react-native-audio-api/common/cpp/audioapi/AudioAPIModuleInstaller.h index ba15c738b..89c91d261 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/AudioAPIModuleInstaller.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/AudioAPIModuleInstaller.h @@ -76,9 +76,15 @@ class AudioAPIModuleInstaller { const jsi::Value *args, size_t count) -> jsi::Value { auto sampleRate = static_cast(args[0].getNumber()); + auto androidOutputProfile = AndroidOutputProfile::Media; + + if (count > 1 && args[1].isString() && + args[1].getString(runtime).utf8(runtime) == "voiceCommunication") { + androidOutputProfile = AndroidOutputProfile::VoiceCommunication; + } auto audioContextHostObject = std::make_shared( - sampleRate, audioEventHandlerRegistry, &runtime, jsCallInvoker); + sampleRate, androidOutputProfile, audioEventHandlerRegistry, &runtime, jsCallInvoker); return jsi::Object::createFromHostObject(runtime, audioContextHostObject); }); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.cpp b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.cpp index 4bc1c6843..3f2d2ab9f 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.cpp @@ -11,11 +11,15 @@ namespace audioapi { AudioContextHostObject::AudioContextHostObject( float sampleRate, + AndroidOutputProfile androidOutputProfile, const std::shared_ptr &audioEventHandlerRegistry, jsi::Runtime *runtime, const std::shared_ptr &callInvoker) : BaseAudioContextHostObject( - std::make_shared(sampleRate, audioEventHandlerRegistry), + std::make_shared( + sampleRate, + androidOutputProfile, + audioEventHandlerRegistry), runtime, callInvoker) { addGetters(JSI_EXPORT_PROPERTY_GETTER(AudioContextHostObject, outputLatency)); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.h index 4952c6c85..b5467bb76 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioContextHostObject.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -15,6 +16,7 @@ class AudioContextHostObject : public BaseAudioContextHostObject { public: explicit AudioContextHostObject( float sampleRate, + AndroidOutputProfile androidOutputProfile, const std::shared_ptr &audioEventHandlerRegistry, jsi::Runtime *runtime, const std::shared_ptr &callInvoker); diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp index 6721bb9bf..0e175db41 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.cpp @@ -15,8 +15,11 @@ namespace audioapi { AudioContext::AudioContext( float sampleRate, + AndroidOutputProfile androidOutputProfile, const std::shared_ptr &audioEventHandlerRegistry) - : BaseAudioContext(sampleRate, audioEventHandlerRegistry), isInitialized_(false) { + : BaseAudioContext(sampleRate, audioEventHandlerRegistry), + androidOutputProfile_(androidOutputProfile), + isInitialized_(false) { // Context starts SUSPENDED with no audio-thread consumer. Let the producer // drain the channels itself until start()/resume() hands draining to the // audio callback (same pattern as OfflineAudioContext before rendering). @@ -45,7 +48,8 @@ void AudioContext::initialize(const AudioDestinationNode *destination) { destination_->getChannelCount(), &driverMutex_, std::static_pointer_cast(shared_from_this()), - currentRenders_); + currentRenders_, + androidOutputProfile_); #else audioPlayer_ = std::make_shared( [this](DSPAudioBuffer *buf, int n) { processGraph(buf, n); }, diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h index 0cad163a2..15ce73ac8 100644 --- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioContext.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -15,6 +16,7 @@ class AudioContext : public BaseAudioContext { public: explicit AudioContext( float sampleRate, + AndroidOutputProfile androidOutputProfile, const std::shared_ptr &audioEventHandlerRegistry); ~AudioContext() override; DELETE_COPY_AND_MOVE(AudioContext); @@ -39,6 +41,7 @@ class AudioContext : public BaseAudioContext { private: std::shared_ptr audioPlayer_; + AndroidOutputProfile androidOutputProfile_; std::atomic isInitialized_{false}; /// Audio I/O callback thread increments around each platform render callback; /// control thread waits on suspend/close. diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/types/AudioContextOptions.h b/packages/react-native-audio-api/common/cpp/audioapi/core/types/AudioContextOptions.h new file mode 100644 index 000000000..8fba15bfc --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/audioapi/core/types/AudioContextOptions.h @@ -0,0 +1,11 @@ +#pragma once +#include + +namespace audioapi { + +enum class AndroidOutputProfile : std::uint8_t { + Media, + VoiceCommunication, +}; + +} // namespace audioapi diff --git a/packages/react-native-audio-api/src/AudioAPIModule/globals.d.ts b/packages/react-native-audio-api/src/AudioAPIModule/globals.d.ts index a3f23c4d4..b8482baa2 100644 --- a/packages/react-native-audio-api/src/AudioAPIModule/globals.d.ts +++ b/packages/react-native-audio-api/src/AudioAPIModule/globals.d.ts @@ -7,11 +7,18 @@ import type { IAudioBuffer, IOfflineAudioContext, } from '../jsi-interfaces'; -import type { AudioRecorderOptions, FileInfo } from '../types'; +import type { + AndroidOutputProfile, + AudioRecorderOptions, + FileInfo, +} from '../types'; /* eslint-disable no-var */ declare global { - var createAudioContext: (sampleRate: number) => IAudioContext; + var createAudioContext: ( + sampleRate: number, + androidOutputProfile?: AndroidOutputProfile + ) => IAudioContext; var createOfflineAudioContext: ( numberOfChannels: number, length: number, diff --git a/packages/react-native-audio-api/src/core/AudioContext.ts b/packages/react-native-audio-api/src/core/AudioContext.ts index f0b075bda..d22a5ec9a 100644 --- a/packages/react-native-audio-api/src/core/AudioContext.ts +++ b/packages/react-native-audio-api/src/core/AudioContext.ts @@ -15,7 +15,8 @@ export default class AudioContext extends BaseAudioContext { super( globalThis.createAudioContext( - options?.sampleRate || AudioManager.getDevicePreferredSampleRate() + options?.sampleRate || AudioManager.getDevicePreferredSampleRate(), + options?.androidOutputProfile ) ); } diff --git a/packages/react-native-audio-api/src/types.ts b/packages/react-native-audio-api/src/types.ts index 56d4c8aab..df4823cb1 100644 --- a/packages/react-native-audio-api/src/types.ts +++ b/packages/react-native-audio-api/src/types.ts @@ -47,8 +47,11 @@ export type OscillatorType = export interface AudioContextOptions { sampleRate?: number; + androidOutputProfile?: AndroidOutputProfile; } +export type AndroidOutputProfile = 'media' | 'voiceCommunication'; + export interface OfflineAudioContextOptions { numberOfChannels: number; length: number; diff --git a/packages/react-native-audio-api/wpt_tests/src/jsi_install.cpp b/packages/react-native-audio-api/wpt_tests/src/jsi_install.cpp index dccc27672..ba30e417d 100644 --- a/packages/react-native-audio-api/wpt_tests/src/jsi_install.cpp +++ b/packages/react-native-audio-api/wpt_tests/src/jsi_install.cpp @@ -20,6 +20,7 @@ namespace { +using audioapi::AndroidOutputProfile; using audioapi::AudioBuffer; using audioapi::AudioBufferHostObject; using audioapi::AudioContextHostObject; @@ -203,6 +204,7 @@ void installAudioContextBinding( const auto sampleRate = static_cast(args[0].getNumber()); auto hostObject = std::make_shared( sampleRate, + AndroidOutputProfile::Media, eventRegistry, &rt, callInvoker);