Skip to content

Commit 0c810bb

Browse files
authored
refactor: updated AVAudioSessionCategoryOptions (#558)
1 parent b1e1410 commit 0c810bb

5 files changed

Lines changed: 102 additions & 66 deletions

File tree

apps/common-app/src/demos/Record/Record.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ const Record: FC = () => {
9090
setupNotification(false);
9191

9292
if (result.status === 'success') {
93-
console.log('Recording started, file path:', result.path);
9493
setState(RecordingState.Recording);
9594
return;
9695
}
@@ -124,7 +123,7 @@ const Record: FC = () => {
124123
return;
125124
}
126125

127-
const audioBuffer = await audioContext.decodeAudioData(info.path);
126+
const audioBuffer = await audioContext.decodeAudioData(info.paths[0]);
128127
setRecordedBuffer(audioBuffer);
129128
setState(RecordingState.ReadyToPlay);
130129
currentPositionSV.value = 0;

apps/fabric-example/ios/Podfile.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2476,7 +2476,7 @@ EXTERNAL SOURCES:
24762476

24772477
SPEC CHECKSUMS:
24782478
FBLazyVector: c00c20551d40126351a6783c47ce75f5b374851b
2479-
hermes-engine: c399a2e224a0b13c589d76b4fc05e14bdd76fa88
2479+
hermes-engine: 91023181d4bc5948b457de5314623fbfe4f8604e
24802480
RCTDeprecation: 3bb167081b134461cfeb875ff7ae1945f8635257
24812481
RCTRequired: 74839f55d5058a133a0bc4569b0afec750957f64
24822482
RCTSwiftUI: 87a316382f3eab4dd13d2a0d0fd2adcce917361a
@@ -2485,7 +2485,7 @@ SPEC CHECKSUMS:
24852485
React: 1b1536b9099195944034e65b1830f463caaa8390
24862486
React-callinvoker: 6dff6d17d1d6cc8fdf85468a649bafed473c65f5
24872487
React-Core: 00faa4d038298089a1d5a5b21dde8660c4f0820d
2488-
React-Core-prebuilt: ab26be1216323aea7c76f96ca450bffa7bcd4a72
2488+
React-Core-prebuilt: a6d614de037caff7898424dfc22915ec792de921
24892489
React-CoreModules: a17807f849bfd86045b0b9a75ec8c19373b482f6
24902490
React-cxxreact: c7b53ace5827be54048288bce5c55f337c41e95f
24912491
React-debug: e1f00fcd2cef58a2897471a6d76a4ef5f5f90c74
@@ -2549,7 +2549,7 @@ SPEC CHECKSUMS:
25492549
ReactAppDependencyProvider: 5787b37b8e2e51dfeab697ec031cc7c4080dcea2
25502550
ReactCodegen: d07ee3c8db75b43d1cbe479ae6affebf9925c733
25512551
ReactCommon: fe2a3af8975e63efa60f95fca8c34dc85deee360
2552-
ReactNativeDependencies: 212738cc51e6c4cc34ee487890497d6f41979ec0
2552+
ReactNativeDependencies: 4d5ce2683b6d74f7c686bf90a88c7d381295cf3c
25532553
RNAudioAPI: 6f6c369c1df31fc8205cbc6f0c3bcb7322e9cfa9
25542554
RNGestureHandler: 187c5c7936abf427bc4d22d6c3b1ac80ad1f63c0
25552555
RNReanimated: 64f4b3b33b48b19e0ba76a352571b52b1e931981

packages/audiodocs/docs/system/audio-manager.mdx

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ function App() {
2020
AudioManager.setAudioSessionOptions({
2121
iosCategory: 'playback',
2222
iosMode: 'default',
23-
iosOptions: ['defaultToSpeaker', 'allowBluetoothA2DP'],
23+
iosOptions: ['allowBluetoothHFP', 'allowAirPlay'],
2424
})
2525
// enabling emission of events
2626
AudioManager.observeAudioInterruptions(true);
@@ -181,33 +181,37 @@ type AudioFocusType =
181181
<summary>Type definitions</summary>
182182
```typescript
183183
type IOSCategory =
184-
| 'record'
185184
| 'ambient'
186-
| 'playback'
187185
| 'multiRoute'
188-
| 'soloAmbient'
189-
| 'playAndRecord';
186+
| 'playAndRecord'
187+
| 'playback'
188+
| 'record'
189+
| 'soloAmbient';
190190

191191
type IOSMode =
192192
| 'default'
193+
| 'dualRoute'
193194
| 'gameChat'
194-
| 'videoChat'
195-
| 'voiceChat'
196195
| 'measurement'
197-
| 'voicePrompt'
198-
| 'spokenAudio'
199196
| 'moviePlayback'
200-
| 'videoRecording';
197+
| 'shortFormVideo'
198+
| 'spokenAudio'
199+
| 'videoChat'
200+
| 'videoRecording'
201+
| 'voiceChat'
202+
| 'voicePrompt';
201203

202204
type IOSOption =
203-
| 'duckOthers'
204205
| 'allowAirPlay'
205-
| 'mixWithOthers'
206+
| 'allowBluetoothA2DP'
206207
| 'allowBluetoothHFP'
208+
| 'bluetoothHighQualityRecording'
207209
| 'defaultToSpeaker'
208-
| 'allowBluetoothA2DP'
209-
| 'overrideMutedMicrophoneInterruption'
210-
| 'interruptSpokenAudioAndMixWithOthers';
210+
| 'duckOthers'
211+
| 'farFieldInput'
212+
| 'interruptSpokenAudioAndMixWithOthers'
213+
| 'mixWithOthers'
214+
| 'overrideMutedMicrophoneInterruption';
211215

212216
interface SessionOptions {
213217
iosMode?: IOSMode;

packages/react-native-audio-api/ios/audioapi/ios/system/AudioSessionManager.mm

Lines changed: 60 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -344,18 +344,18 @@ - (AVAudioSessionCategory)categoryFromString:(NSString *)categorySTR
344344
{
345345
AVAudioSessionCategory category = 0;
346346

347-
if ([categorySTR isEqualToString:@"record"]) {
348-
category = AVAudioSessionCategoryRecord;
349-
} else if ([categorySTR isEqualToString:@"ambient"]) {
347+
if ([categorySTR isEqualToString:@"ambient"]) {
350348
category = AVAudioSessionCategoryAmbient;
351-
} else if ([categorySTR isEqualToString:@"playback"]) {
352-
category = AVAudioSessionCategoryPlayback;
353349
} else if ([categorySTR isEqualToString:@"multiRoute"]) {
354350
category = AVAudioSessionCategoryMultiRoute;
355-
} else if ([categorySTR isEqualToString:@"soloAmbient"]) {
356-
category = AVAudioSessionCategorySoloAmbient;
357351
} else if ([categorySTR isEqualToString:@"playAndRecord"]) {
358352
category = AVAudioSessionCategoryPlayAndRecord;
353+
} else if ([categorySTR isEqualToString:@"playback"]) {
354+
category = AVAudioSessionCategoryPlayback;
355+
} else if ([categorySTR isEqualToString:@"record"]) {
356+
category = AVAudioSessionCategoryRecord;
357+
} else if ([categorySTR isEqualToString:@"soloAmbient"]) {
358+
category = AVAudioSessionCategorySoloAmbient;
359359
}
360360

361361
return category;
@@ -367,22 +367,34 @@ - (AVAudioSessionMode)modeFromString:(NSString *)modeSTR
367367

368368
if ([modeSTR isEqualToString:@"default"]) {
369369
mode = AVAudioSessionModeDefault;
370+
} else if ([modeSTR isEqualToString:@"dualRoute"]) {
371+
if (@available(iOS 26.2, *)) {
372+
mode = AVAudioSessionModeDualRoute;
373+
} else {
374+
mode = AVAudioSessionModeDefault;
375+
}
370376
} else if ([modeSTR isEqualToString:@"gameChat"]) {
371377
mode = AVAudioSessionModeGameChat;
372-
} else if ([modeSTR isEqualToString:@"videoChat"]) {
373-
mode = AVAudioSessionModeVideoChat;
374-
} else if ([modeSTR isEqualToString:@"voiceChat"]) {
375-
mode = AVAudioSessionModeVoiceChat;
376378
} else if ([modeSTR isEqualToString:@"measurement"]) {
377379
mode = AVAudioSessionModeMeasurement;
378-
} else if ([modeSTR isEqualToString:@"voicePrompt"]) {
379-
mode = AVAudioSessionModeVoicePrompt;
380-
} else if ([modeSTR isEqualToString:@"spokenAudio"]) {
381-
mode = AVAudioSessionModeSpokenAudio;
382380
} else if ([modeSTR isEqualToString:@"moviePlayback"]) {
383381
mode = AVAudioSessionModeMoviePlayback;
382+
} else if ([modeSTR isEqualToString:@"shortFormVideo"]) {
383+
if (@available(iOS 26, *)) {
384+
mode = AVAudioSessionModeShortFormVideo;
385+
} else {
386+
mode = AVAudioSessionModeDefault;
387+
}
388+
} else if ([modeSTR isEqualToString:@"spokenAudio"]) {
389+
mode = AVAudioSessionModeSpokenAudio;
390+
} else if ([modeSTR isEqualToString:@"videoChat"]) {
391+
mode = AVAudioSessionModeVideoChat;
384392
} else if ([modeSTR isEqualToString:@"videoRecording"]) {
385393
mode = AVAudioSessionModeVideoRecording;
394+
} else if ([modeSTR isEqualToString:@"voiceChat"]) {
395+
mode = AVAudioSessionModeVoiceChat;
396+
} else if ([modeSTR isEqualToString:@"voicePrompt"]) {
397+
mode = AVAudioSessionModeVoicePrompt;
386398
}
387399

388400
return mode;
@@ -393,41 +405,58 @@ - (AVAudioSessionCategoryOptions)optionsFromArray:(NSArray *)optionsArray
393405
AVAudioSessionCategoryOptions options = 0;
394406

395407
for (NSString *option in optionsArray) {
396-
if ([option isEqualToString:@"duckOthers"]) {
397-
options |= AVAudioSessionCategoryOptionDuckOthers;
398-
}
399-
400408
if ([option isEqualToString:@"allowAirPlay"]) {
401409
options |= AVAudioSessionCategoryOptionAllowAirPlay;
410+
continue;
402411
}
403412

404-
if ([option isEqualToString:@"mixWithOthers"]) {
405-
options |= AVAudioSessionCategoryOptionMixWithOthers;
413+
if ([option isEqualToString:@"allowBluetoothA2DP"]) {
414+
options |= AVAudioSessionCategoryOptionAllowBluetoothA2DP;
415+
continue;
406416
}
407417

408418
if ([option isEqualToString:@"allowBluetoothHFP"]) {
409-
// XCode 26.x (default support SDK >= 26.x) uses AVAudioSessionCategoryOptionAllowBluetoothHFP as new standard for every platfrom (down to iOS 1.0)
410-
// Older Xcode (default support SDKs) versions doesn't define it at all.
411-
// Both (AVAudioSessionCategoryOptionAllowBluetooth in SDK < 26.x) and (AVAudioSessionCategoryOptionAllowBluetoothHFP in SDK >= 26.x) resolve to this value
412-
// We use it here directly as there is no reliable way to switch between them (no @available for this).
413-
// TODO: replace with AVAudioSessionCategoryOptionAllowBluetoothHFP once XCode 16.x will dig its grave
414-
options |= 0x4;
419+
options |= AVAudioSessionCategoryOptionAllowBluetoothHFP;
420+
continue;
421+
}
422+
423+
if ([option isEqualToString:@"bluetoothHighQualityRecording"]) {
424+
if (@available(iOS 26, *)) {
425+
options |= AVAudioSessionCategoryOptionBluetoothHighQualityRecording;
426+
}
427+
continue;
415428
}
416429

417430
if ([option isEqualToString:@"defaultToSpeaker"]) {
418431
options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
432+
continue;
419433
}
420434

421-
if ([option isEqualToString:@"allowBluetoothA2DP"]) {
422-
options |= AVAudioSessionCategoryOptionAllowBluetoothA2DP;
435+
if ([option isEqualToString:@"duckOthers"]) {
436+
options |= AVAudioSessionCategoryOptionDuckOthers;
437+
continue;
423438
}
424439

425-
if ([option isEqualToString:@"overrideMutedMicrophoneInterruption"]) {
426-
options |= AVAudioSessionCategoryOptionOverrideMutedMicrophoneInterruption;
440+
if ([option isEqualToString:@"farFieldInput"]) {
441+
if (@available(iOS 26.2, *)) {
442+
options |= AVAudioSessionCategoryOptionFarFieldInput;
443+
continue;
444+
}
427445
}
428446

429447
if ([option isEqualToString:@"interruptSpokenAudioAndMixWithOthers"]) {
430448
options |= AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers;
449+
continue;
450+
}
451+
452+
if ([option isEqualToString:@"mixWithOthers"]) {
453+
options |= AVAudioSessionCategoryOptionMixWithOthers;
454+
continue;
455+
}
456+
457+
if ([option isEqualToString:@"overrideMutedMicrophoneInterruption"]) {
458+
options |= AVAudioSessionCategoryOptionOverrideMutedMicrophoneInterruption;
459+
continue;
431460
}
432461
}
433462

packages/react-native-audio-api/src/system/types.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,37 @@ import type { AudioEventSubscription } from '../events';
22
import type { SystemEventCallback, SystemEventName } from '../events/types';
33

44
export type IOSCategory =
5-
| 'record'
65
| 'ambient'
7-
| 'playback'
86
| 'multiRoute'
9-
| 'soloAmbient'
10-
| 'playAndRecord';
7+
| 'playAndRecord'
8+
| 'playback'
9+
| 'record'
10+
| 'soloAmbient';
1111

1212
export type IOSMode =
1313
| 'default'
14+
| 'dualRoute'
1415
| 'gameChat'
15-
| 'videoChat'
16-
| 'voiceChat'
1716
| 'measurement'
18-
| 'voicePrompt'
19-
| 'spokenAudio'
2017
| 'moviePlayback'
21-
| 'videoRecording';
18+
| 'shortFormVideo'
19+
| 'spokenAudio'
20+
| 'videoChat'
21+
| 'videoRecording'
22+
| 'voiceChat'
23+
| 'voicePrompt';
2224

2325
export type IOSOption =
24-
| 'duckOthers'
2526
| 'allowAirPlay'
26-
| 'mixWithOthers'
27-
| 'defaultToSpeaker'
28-
| 'allowBluetoothHFP'
2927
| 'allowBluetoothA2DP'
30-
| 'overrideMutedMicrophoneInterruption'
31-
| 'interruptSpokenAudioAndMixWithOthers';
28+
| 'allowBluetoothHFP'
29+
| 'bluetoothHighQualityRecording'
30+
| 'defaultToSpeaker'
31+
| 'duckOthers'
32+
| 'farFieldInput'
33+
| 'interruptSpokenAudioAndMixWithOthers'
34+
| 'mixWithOthers'
35+
| 'overrideMutedMicrophoneInterruption';
3236

3337
export type AudioFocusType =
3438
| 'gain'

0 commit comments

Comments
 (0)