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
1 change: 1 addition & 0 deletions packages/audiodocs/docs/core/audio-context.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ constructor(options?: AudioContextOptions)
| Parameter | Type | Default | |
| :---: | :---: | :----: | :---- |
| `sampleRate` <Optional /> | `number` | - | The preferred sample rate for the context. |
| `androidOutputProfile` <Optional /> | `'media'` \| `'voiceCommunication'` | `'media'` | Android output classification. Use `'voiceCommunication'` for interactive voice calls. |

#### Errors

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ AudioPlayer::AudioPlayer(
int channelCount,
std::mutex *driverMutex,
const std::shared_ptr<AudioContext> &context,
std::atomic<uint32_t> &currentRenders)
std::atomic<uint32_t> &currentRenders,
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_);
Expand All @@ -43,6 +45,10 @@ bool AudioPlayer::openAudioStream() {
->setErrorCallback(shared_from_this())
->setSampleRate(static_cast<int>(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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <mutex>

#include <audioapi/core/CommonPlayer.h>
#include <audioapi/core/types/AudioContextOptions.h>
#include <audioapi/utils/AudioBuffer.hpp>

namespace audioapi {
Expand All @@ -29,7 +30,8 @@ class AudioPlayer : public CommonPlayer,
int channelCount,
std::mutex *driverMutex,
const std::shared_ptr<AudioContext> &context,
std::atomic<uint32_t> &currentRenders);
std::atomic<uint32_t> &currentRenders,
AndroidOutputProfile outputProfile);

~AudioPlayer() override {
cleanup();
Expand Down Expand Up @@ -67,6 +69,7 @@ class AudioPlayer : public CommonPlayer,
std::atomic<int32_t> lastCallbackFrameCount_{0};
std::mutex *driverMutex_;
std::weak_ptr<AudioContext> context_;
AndroidOutputProfile outputProfile_;

bool openAudioStream();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,15 @@ class AudioAPIModuleInstaller {
const jsi::Value *args,
size_t count) -> jsi::Value {
auto sampleRate = static_cast<float>(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<AudioContextHostObject>(
sampleRate, audioEventHandlerRegistry, &runtime, jsCallInvoker);
sampleRate, androidOutputProfile, audioEventHandlerRegistry, &runtime, jsCallInvoker);

return jsi::Object::createFromHostObject(runtime, audioContextHostObject);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ namespace audioapi {

AudioContextHostObject::AudioContextHostObject(
float sampleRate,
AndroidOutputProfile androidOutputProfile,
const std::shared_ptr<IAudioEventHandlerRegistry> &audioEventHandlerRegistry,
jsi::Runtime *runtime,
const std::shared_ptr<react::CallInvoker> &callInvoker)
: BaseAudioContextHostObject(
std::make_shared<AudioContext>(sampleRate, audioEventHandlerRegistry),
std::make_shared<AudioContext>(
sampleRate,
androidOutputProfile,
audioEventHandlerRegistry),
runtime,
callInvoker) {
addGetters(JSI_EXPORT_PROPERTY_GETTER(AudioContextHostObject, outputLatency));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <audioapi/HostObjects/BaseAudioContextHostObject.h>
#include <audioapi/core/types/AudioContextOptions.h>
#include <audioapi/events/IAudioEventHandlerRegistry.h>

#include <jsi/jsi.h>
Expand All @@ -15,6 +16,7 @@ class AudioContextHostObject : public BaseAudioContextHostObject {
public:
explicit AudioContextHostObject(
float sampleRate,
AndroidOutputProfile androidOutputProfile,
const std::shared_ptr<IAudioEventHandlerRegistry> &audioEventHandlerRegistry,
jsi::Runtime *runtime,
const std::shared_ptr<react::CallInvoker> &callInvoker);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
namespace audioapi {
AudioContext::AudioContext(
float sampleRate,
AndroidOutputProfile androidOutputProfile,
const std::shared_ptr<IAudioEventHandlerRegistry> &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).
Expand Down Expand Up @@ -45,7 +48,8 @@ void AudioContext::initialize(const AudioDestinationNode *destination) {
destination_->getChannelCount(),
&driverMutex_,
std::static_pointer_cast<AudioContext>(shared_from_this()),
currentRenders_);
currentRenders_,
androidOutputProfile_);
#else
audioPlayer_ = std::make_shared<IOSAudioPlayer>(
[this](DSPAudioBuffer *buf, int n) { processGraph(buf, n); },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <audioapi/core/BaseAudioContext.h>
#include <audioapi/core/CommonPlayer.h>
#include <audioapi/core/types/AudioContextOptions.h>
#include <audioapi/jsi/ContextPromiseResolver.hpp>
#include <audioapi/utils/AudioBuffer.hpp>
#include <audioapi/utils/Macros.h>
Expand All @@ -15,6 +16,7 @@ class AudioContext : public BaseAudioContext {
public:
explicit AudioContext(
float sampleRate,
AndroidOutputProfile androidOutputProfile,
const std::shared_ptr<IAudioEventHandlerRegistry> &audioEventHandlerRegistry);
~AudioContext() override;
DELETE_COPY_AND_MOVE(AudioContext);
Expand All @@ -39,6 +41,7 @@ class AudioContext : public BaseAudioContext {

private:
std::shared_ptr<CommonPlayer> audioPlayer_;
AndroidOutputProfile androidOutputProfile_;
std::atomic<bool> isInitialized_{false};
/// Audio I/O callback thread increments around each platform render callback;
/// control thread waits on suspend/close.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#pragma once
#include <cstdint>

namespace audioapi {

enum class AndroidOutputProfile : std::uint8_t {
Media,
VoiceCommunication,
};

} // namespace audioapi
11 changes: 9 additions & 2 deletions packages/react-native-audio-api/src/AudioAPIModule/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion packages/react-native-audio-api/src/core/AudioContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export default class AudioContext extends BaseAudioContext {

super(
globalThis.createAudioContext(
options?.sampleRate || AudioManager.getDevicePreferredSampleRate()
options?.sampleRate || AudioManager.getDevicePreferredSampleRate(),
options?.androidOutputProfile
)
);
}
Expand Down
3 changes: 3 additions & 0 deletions packages/react-native-audio-api/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions packages/react-native-audio-api/wpt_tests/src/jsi_install.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

namespace {

using audioapi::AndroidOutputProfile;
using audioapi::AudioBuffer;
using audioapi::AudioBufferHostObject;
using audioapi::AudioContextHostObject;
Expand Down Expand Up @@ -203,6 +204,7 @@ void installAudioContextBinding(
const auto sampleRate = static_cast<float>(args[0].getNumber());
auto hostObject = std::make_shared<AudioContextHostObject>(
sampleRate,
AndroidOutputProfile::Media,
eventRegistry,
&rt,
callInvoker);
Expand Down
Loading