react-native-sound
react-native-sound copied to clipboard
`mixWithOthers` does not function correctly on iOS.
:beetle: Description
mixWithOthers
permanently stops audio streams on iOS from other apps such as Spotify, even though mixWithOthers
is declared as true.
:beetle: What is the observed behavior?
Audio streams are stopped.
:beetle: What is the expected behavior?
The sound being played from another app is played along side the sound from your app.
:beetle: Please post your code:
In a brand new template app:
-
npx react-native init TestClean
-
npm i react-native-sounds
-
pod install
- Add
loop.mp3
to the xcode project. - Check off background modes:
Audio, AirPlay, and Picture in Picture
,External accessory communication
- In
App.js
:
import Sound from "react-native-sound";
Sound.setCategory(`Playback`, true);
Sound.setMode(`Default`);
let sound = new Sound(`loop.mp3`, Sound.MAIN_BUNDLE, error => {
sound.play();
if (error) {
console.error(error);
} else {
console.log('success');
}
});
:bulb: Possible solution
According to the developer docs for AVAudioSessionCategoryOptionAllowBluetooth:
You can set this option only if the audio session category is AVAudioSessionCategoryPlayAndRecord or AVAudioSessionCategoryRecord.
However, in https://github.com/zmxv/react-native-sound/blob/1aa45f25c8c03ea5c17fac18564c3928bd023113/RNSound/RNSound.m#L175-L180
It is setting this option regardless of the category.
:bulb: Is there a workaround?
Removing this declaration AVAudioSessionCategoryOptionAllowBluetooth
no longer causes the sound to interrupt other apps. I also checked with bluetooth headphones, and there was no difference in behavior with or without this option being present.
Note: I tested this workaround only on playback
category, iOS 15.4.
Using patch-package
:
diff --git a/node_modules/react-native-sound/RNSound/RNSound.m b/node_modules/react-native-sound/RNSound/RNSound.m
index df3784e..aa97df6 100644
--- a/node_modules/react-native-sound/RNSound/RNSound.m
+++ b/node_modules/react-native-sound/RNSound/RNSound.m
@@ -175,8 +175,7 @@ - (NSDictionary *)constantsToExport {
if (category) {
if (mixWithOthers) {
[session setCategory:category
- withOptions:AVAudioSessionCategoryOptionMixWithOthers |
- AVAudioSessionCategoryOptionAllowBluetooth
+ withOptions:AVAudioSessionCategoryOptionMixWithOthers
error:nil];
} else {
[session setCategory:category error:nil];
:bulb: If the bug is confirmed, would you be willing to create a pull request?
Sure
Is your issue with...
- [x] iOS
- [ ] Android
- [ ] Windows
Are you using...
- [x] React Native CLI (e.g.
react-native run-android
) - [ ] Expo
- [ ] Other: (please specify)
Which versions are you using?
- React Native Sound: 0.11.2
- React Native: 0.67.4
- iOS: iOS 15.4
- Android: n/a
- Windows: n/a
Does the problem occur on...
- [ ] Simulator
- [x] Device
If your problem is happening on a device, which device?
- Device: iPhone 13 Pro Max
OH MY GOD thank you so much @dsf3449!!! This is exactly the change I needed to make to get my app sounds to work while spotify is running.
To put the steps succintly.
-
Set your background audio capability in XCode (or info.plist)
-
In your instantiation of the Sound module, write
// Make sure to use Playback and set Mixwithothers to true
Sound.setCategory('Playback', true)
// You only need to call the setActive(true) line *once in your code*, and you never set it to false,
// EVER unless you no longer want audio to be played in background.
// You do not need to try calling setActive(false) at any point in the onCompletion methods
Sound.setActive(true)
- Then navigate to
node_modules/react-native-sound/RNSound/RNSound.m
- Edit the line below as such Change
if (category) {
if (mixWithOthers) {
[session setCategory:category
withOptions:AVAudioSessionCategoryOptionMixWithOthers |
AVAudioSessionCategoryOptionAllowBluetooth
error:nil];
} else {
[session setCategory:category error:nil];
}
}
TO
if (category) {
if (mixWithOthers) {
[session setCategory:category
withOptions:AVAudioSessionCategoryOptionMixWithOthers
// | AVAudioSessionCategoryOptionAllowBluetooth // I moved the '|' to the next line and commented it out
error:nil];
} else {
[session setCategory:category error:nil];
}
}
Thanks, guys! I had reported the same here as well, but as a feature request as I didn't know the lib supported this behavior.
https://github.com/zmxv/react-native-sound/issues/781
I have a peculiar case where I want the sound to play/mix together with Spotify with the device looking and the app in the background. Did you @ucheNkadiCode and @dsf3449 manage to make it work? I have tried iOS 15.4 it didn't
We had the same issue. We have sounds playing with Sound.setCategory('Ambient', true);
which worked fine in the past and stopped working in the meantime.
I can confirm that the patch of @dsf3449 works and solves the issue. Thank you!
@ucheNkadiCode You should check out https://www.npmjs.com/package/patch-package instead of manually editing the file after each npm/yarn install.
More than a year later; do we still have to use this patch? I am still facing it with latest version :/
@pierroo I don't believe the repo author has made any changes regarding this specific issue. Many PRs outstanding and no releases for over 1.5 years. I would assume it to be abandoned at this time.
As for alternatives, unfortunately the state of audio packages for react native seems to be quite fragmented. This reddit thread lists at least 6 different options depending on your use case.
hello !
I modified the RNSound.m ... but the music still stop when my app starts. Here is my code :
init = async () => {
Sound.setCategory('Playback', true);
Sound.setActive(true);
}
play = async () => {
try {
var ding = new Sound("test.mp3", Sound.MAIN_BUNDLE, (error) => {
if (error) {
console.log('Sound / play / failed to load the sound', error);
}
else {
ding.play(async (success) => {
if (success) {
ding.release()
}
});
}
});
}
console.log('Sound / play / exit');
} catch (error) {
console.log('Sound / play / error', error);
}
}
RCT_EXPORT_METHOD(setCategory
: (NSString *)categoryName mixWithOthers
: (BOOL)mixWithOthers) {
AVAudioSession *session = [AVAudioSession sharedInstance];
NSString *category = nil;
if ([categoryName isEqual:@"Ambient"]) {
category = AVAudioSessionCategoryAmbient;
} else if ([categoryName isEqual:@"SoloAmbient"]) {
category = AVAudioSessionCategorySoloAmbient;
} else if ([categoryName isEqual:@"Playback"]) {
category = AVAudioSessionCategoryPlayback;
} else if ([categoryName isEqual:@"Record"]) {
category = AVAudioSessionCategoryRecord;
} else if ([categoryName isEqual:@"PlayAndRecord"]) {
category = AVAudioSessionCategoryPlayAndRecord;
}
#if TARGET_OS_IOS
else if ([categoryName isEqual:@"AudioProcessing"]) {
category = AVAudioSessionCategoryAudioProcessing;
}
#endif
else if ([categoryName isEqual:@"MultiRoute"]) {
category = AVAudioSessionCategoryMultiRoute;
}
if (category) {
if (mixWithOthers) {
[session setCategory:category
withOptions:AVAudioSessionCategoryOptionMixWithOthers
// | AVAudioSessionCategoryOptionAllowBluetooth
error:nil];
} else {
[session setCategory:category error:nil];
}
}
}