Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@ interface Spec extends TurboModule {
): void;
getDevicePreferredSampleRate(): number;
observeAudioInterruptions(enabled: boolean): void;
requestAudioFocus(
setAudioOptions(
observeAudioInterruptions: boolean,
options?: {
[key: string]: string | boolean | number | AudioAttributeType | undefined;
[key: string]:
| string
| string[]
| boolean
| number
| AudioAttributeType
| undefined;
}
): void;
abandonAudioFocus(): void;
Expand Down
29 changes: 10 additions & 19 deletions packages/react-native-audio-api/src/system/AudioManager.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
SessionOptions,
LockScreenInfo,
PermissionStatus,
AudioFocusOptions,
} from './types';
import { AudioOptions, LockScreenInfo, PermissionStatus } from './types';
import { SystemEventName, SystemEventCallback } from '../events/types';
import { NativeAudioAPIModule } from '../specs';
import { AudioEventEmitter, AudioEventSubscription } from '../events';
Expand Down Expand Up @@ -32,14 +27,6 @@ class AudioManager {
NativeAudioAPIModule!.resetLockScreenInfo();
}

setAudioSessionOptions(options: SessionOptions) {
NativeAudioAPIModule!.setAudioSessionOptions(
options.iosCategory ?? '',
options.iosMode ?? '',
options.iosOptions ?? []
);
}

getDevicePreferredSampleRate(): number {
return NativeAudioAPIModule!.getDevicePreferredSampleRate();
}
Expand All @@ -48,11 +35,15 @@ class AudioManager {
NativeAudioAPIModule!.observeAudioInterruptions(enabled);
}

requestAudioFocus(
observeAudioInterruption = true,
options?: AudioFocusOptions
) {
NativeAudioAPIModule!.requestAudioFocus(observeAudioInterruption, options);
/**
* Used to set various system options for android and iOS
*
* @param observeAudioInterruption - If true, the module will observe audio
* interruptions and emit events accordingly (true by default).
* @param options - Additional audio options to configure the audio session.
*/
setAudioOptions(observeAudioInterruption = true, options?: AudioOptions) {
NativeAudioAPIModule!.setAudioOptions(observeAudioInterruption, options);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we have to call observe interruption every play invocation on Android so merging AudioSession settings into one method its not the best option in my opinion

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so am I correct, if the audio interruption event will be fired, it won't be invoked 2nd time and we have to re-observe it?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah

}

abandonAudioFocus() {
Expand Down
114 changes: 58 additions & 56 deletions packages/react-native-audio-api/src/system/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// category that defines a set of audio behaviors, f.e. recording is only allowed in 'record' or 'playAndRecord' category
export type IOSCategory =
| 'record'
| 'ambient'
Expand All @@ -6,6 +7,7 @@ export type IOSCategory =
| 'soloAmbient'
| 'playAndRecord';

// used to define specialized behaviors to an audio session
export type IOSMode =
| 'default'
| 'gameChat'
Expand All @@ -18,68 +20,59 @@ export type IOSMode =
| 'videoRecording';

export type IOSOption =
| 'duckOthers'
| 'allowAirPlay'
| 'mixWithOthers'
| 'allowBluetooth'
| 'defaultToSpeaker'
| 'allowBluetoothA2DP'
| 'overrideMutedMicrophoneInterruption'
| 'interruptSpokenAudioAndMixWithOthers';
| 'duckOthers' // reduce the volume of other audio sessions
| 'allowAirPlay' // whether you can stream audio from this session to AirPlay devices
| 'mixWithOthers' // mixing with other audio sessions
| 'allowBluetooth' // deprecated, option that determines whether Bluetooth hands-free devices appear as available input routes
| 'defaultToSpeaker' // whether audio from the session defaults to the built-in speaker instead of the receiver
| 'allowBluetoothA2DP' // whether you can stream audio from this session to Bluetooth devices that support the Advanced Audio Distribution Profile
| 'overrideMutedMicrophoneInterruption' // whether the system interrupts the audio session when it mutes the built-in microphone
| 'interruptSpokenAudioAndMixWithOthers'; // whether to pause spoken audio from other apps when this session plays

// specifies why the source is playing and controls routing, focus, and volume decisions
export type audioAttributeUsageType =
| 'usage_alarm'
| 'usage_assistance_accessibility'
| 'usage_assistance_navigation_guidance'
| 'usage_assistance_sonification'
| 'usage_assistant'
| 'usage_game'
| 'usage_media'
| 'usage_notification'
| 'usage_notification_event'
| 'usage_notification_ringtone'
| 'usage_notification_communication_request'
| 'usage_notification_communication_instant'
| 'usage_notification_communication_delayed'
| 'usage_unknown'
| 'usage_voice_communication'
| 'usage_voice_communication_signalling';
| 'usageAlarm'
| 'usageAssistanceAccessibility'
| 'usageAssistanceNavigationGuidance'
| 'usageAssistanceSonification'
| 'usageAssistant'
| 'usageGame'
| 'usageMedia'
| 'usageNotification'
| 'usageNotificationEvent'
| 'usageNotificationRingtone'
| 'usageNotificationCommunicationRequest'
| 'usageNotificationCommunicationInstant'
| 'usageNotificationCommunicationDelayed'
| 'usageUnknown'
| 'usageVoiceCommunication'
| 'usageVoiceCommunicationSignalling';

// specifies what the source is playing
export type audioAttributeContentType =
| 'content_type_movie'
| 'content_type_music'
| 'content_type_sonification'
| 'content_type_speech'
| 'content_type_unknown';
| 'contentTypeMovie'
| 'contentTypeMusic'
| 'contentTypeSonification'
| 'contentTypeSpeech'
| 'contentTypeUnknown';

export type focusGainType =
| 'audiofocus_gain'
| 'audiofocus_gain_transient'
| 'audiofocus_gain_transient_exclusive'
| 'audiofocus_gain_transient_may_duck';
| 'audiofocusGain' // indicate a gain of audio focus, or a request of audio focus, of unknown duration
| 'audiofocusGainTransient' // indicate a temporary gain or request of audio focus, anticipated to last a short amount of time
| 'audiofocusGainTransientExclusive' // indicate a temporary request of audio focus, anticipated to last a short amount of time, during which no other applications, or system components, should play anything
| 'audiofocusGainTransientMayDuck'; // indicate a temporary request of audio focus, anticipated to last a short amount of time, and where it is acceptable for other audio applications to keep playing after having lowered their output level

export type AudioAttributeType = {
allowedCapturePolicy?:
| 'allow_capture_by_all'
| 'allow_capture_by_system'
| 'allow_capture_by_none';
| 'allowCaptureByAll' // audio may be captured by any app
| 'allowCaptureBySystem' // audio may be captured by system apps only
| 'allowCaptureByNone'; // audio may not be captured by any app
contentType?: audioAttributeContentType;
flag?: 'flag_hw_av_sync' | 'flag_audibility_enforced';
hapticChannelsMuted?: boolean;
isContentSpatialized?: boolean;
spatializationBehavior?:
| 'spatialization_behavior_auto'
| 'spatialization_behavior_never';
usage?: audioAttributeUsageType;
};

export interface SessionOptions {
iosMode?: IOSMode;
iosOptions?: IOSOption[];
iosCategory?: IOSCategory;
}

export type MediaState = 'state_playing' | 'state_paused';
export type MediaState = 'statePlaying' | 'statePaused';

interface BaseLockScreenInfo {
[key: string]: string | boolean | number | undefined;
Expand All @@ -97,15 +90,24 @@ export interface LockScreenInfo extends BaseLockScreenInfo {
elapsedTime?: number;
}

export type PermissionStatus = 'Undetermined' | 'Denied' | 'Granted';
export type PermissionStatus = 'undetermined' | 'denied' | 'granted';

interface BaseAudioFocusOptions {
[key: string]: string | boolean | number | AudioAttributeType | undefined;
interface BaseAudioOptions {
[key: string]:
| string
| string[]
| boolean
| number
| AudioAttributeType
| undefined;
}

export interface AudioFocusOptions extends BaseAudioFocusOptions {
acceptsDelayedFocusGain?: boolean;
audioAttributes?: AudioAttributeType;
focusGain?: focusGainType;
pauseWhenDucked?: boolean;
export interface AudioOptions extends BaseAudioOptions {
androidAcceptsDelayedFocusGain?: boolean; // marks this focus request as compatible with delayed focus
androidAudioAttributes?: AudioAttributeType;
androidFocusGain?: focusGainType;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't comment on the original type: we should have only one case-type when it comes to setting options, currently:

  • ios settings are camel case
  • android settings are snake case
  • permission status (which is kind of related) is upper-case

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also i'm wondering if we should include spatialization behaviour currently, it seems it is either auto or off. I think we could stick with whatever is default for now.

Especially some-day we will have our on spatialization implementation thus we would like to have this off by defULT

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we talked a bit today about this: could you add short JSDoc-like descriptions to the values (aka what is their meaning). Could be a great start for documenting the api on code-level :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with case-type I wanted to stick with original system values, therefore it is different for ios and android, but it can be narrowed to one easily

androidPauseWhenDucked?: boolean; // whether the app will pause when instructed to duck
iosMode?: IOSMode;
iosOptions?: IOSOption[];
iosCategory?: IOSCategory;
}
Loading