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);