expo: [expo-av] Ducked Audio doesn't stop being ducked.

Summary

I’m working on updating an app that utilizes Audio playback from Expo 44 to Expo 46. (expo-av ~10.2.0 to ~12.0.4). Prior to updating, using sound.stopAsync / sound.pauseAsync would resume the ducked audio (e.g. of music playing).

Now, no matter what I do, the ducked audio won’t resume at full volume. Also, the ducked audio will pause when the app is closed now.

Note that unlike https://github.com/expo/expo/issues/16272 , this is for ducked audio:

      await Audio.setAudioModeAsync({
        playsInSilentModeIOS: true,
        interruptionModeIOS: InterruptionModeIOS.DuckOthers,
      });

See snack for a simple reproduction of this.

It’s unclear to me if the API has changed, and the audio mode needs to be cleared, or something else done with the sound when paused now, or if this is broken. Any help would be very appreciated!

What platform(s) does this occur on?

iOS

Environment

expo-env-info 1.0.5 environment info: System: OS: macOS 12.3 Shell: 5.8 - /bin/zsh Binaries: Node: 14.20.0 - ~/.nvm/versions/node/v14.20.0/bin/node Yarn: 1.22.19 - /opt/homebrew/bin/yarn npm: 6.14.17 - ~/.nvm/versions/node/v14.20.0/bin/npm Managers: CocoaPods: 1.11.3 - /opt/homebrew/bin/pod SDKs: iOS SDK: Platforms: DriverKit 21.4, iOS 15.5, macOS 12.3, tvOS 15.4, watchOS 8.5 IDEs: Xcode: 13.4.1/13F100 - /usr/bin/xcodebuild npmPackages: expo: ^46.0.0 => 46.0.10 react: 18.0.0 => 18.0.0 react-dom: 18.0.0 => 18.0.0 react-native: 0.69.5 => 0.69.5 react-native-web: ~0.18.7 => 0.18.9 Expo Workflow: managed

Minimal reproducible example

Reproduction steps:

  1. Have a background sound playing (e.g. music from spotify).
  2. Load the snack onto your device
  3. play the audio (music should duck)
  4. pause the audio (music should un-duck, but does NOT)
  5. close the expo app (music will STOP, which is further-bad)

https://snack.expo.dev/@jacobjaffe/ludicrous-yogurt

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 5
  • Comments: 24 (18 by maintainers)

Most upvoted comments

This issue was closed because it has been inactive for 7 days since being marked as stale. Please open a new issue if you believe you are encountering a related problem.

This issue is stale because it has been open for 60 days with no activity. If there is no activity in the next 7 days, the issue will be closed.

Hey guys, I solved this issue by using native iOS functionality to switch between ducking/mixing. Adjust the RCTAudioModule.m functions to your usecase if needed. We use it to duck whenever an audio in the app is playing and then switch back to mixwithothers when the audio file stopped playing. Also implemented some functionality such as duck/unduck when pausing/playing and unduck whenever the sound is unloaded (e.g if the user leaves the screen).

RN version: 0.71.6 expo version: 48.0.0 expo-av version: 13.2.1

https://gist.github.com/abakers/6d24cfadc9038696badeb23739c0f6d2

Works like a charm for us. Hope this helps.

I can confirm there are some issues here. @de1acr0ix your proposal fixes my problem. Basically:

  1. I have a video playing in the app using isMuted (the video has not even an audio track)
  2. In the background I am listening to music
  3. I put down the Control Center from iOS… and my music/session (background) is stopped

This basically happens because of [self _updateSessionConfiguration]; in moduleDidBackground.

I am using expo-av: 13.1.0.

cc @brentvatne hope this is helpful 😄 .

I digged into this a little more and also struggled to find a good solution, but I’ve came up with one - at least for my use case. (even though I’ve removed expo-av in favor for another library, but because of different reasons (mainly video))

Right now, we’re activating the audio session, but never inform the system that we’re done. This is fine, because it’s a sync and blocking task and should not be called frequently just because we paused or played an audio/video. The decision should be done imperatively.

So what I’ve done now:

  1. I’ve updated my library react-native-volume-manager and added functions like .setCategory(), .setMode(), setActive(). If you like, try the newest version. I can’t promise you that it will work together with expo-av, I had some issues with expo-av and the audio session (I think expo-av did overwrite everything). If you’re only using Audio, you could also consider using react-native-sound.

  2. You need to inform other Apps correctly that you have deactivated the audio session. You’re not forced to, but without informing, stopped background audio won’t resume. This is currently missing totally in expo-av. To make this work, I had to pass withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation. So when you decide to use my library, you can call VolumeManager.setActive(false) which will automatically resume background audio. You will need to call setActive(value: bool, async: bool) before you wan’t to play another audio. Using the 2nd param you can decide if you want to make this blocking or non-blocking. You could also circumvent this totally and use a combination of setMode() and setCategory(). I think it has to be Ambient.

  3. To make background audio resume whenever I backgrounded my App, I added:

   import { VolumeManager } from 'react-native-volume-manager';

  const onAppStateChange = useCallback(async (status: AppStateStatus) => {
    if (status === 'active') {
      VolumeManager.setActive(true);
    } else if (status === 'background') {
      VolumeManager.setActive(false);
    }
  }, []);

  useAppState({
    onChange: onAppStateChange,
  });

This triggers with withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation automatically, using my library. Afaik, Expo has not implemented this. My library does the active/inactive calls async, so its not blocking.

I would recommend not changing the current behaviour in expo-av. Instead, I would recommend to add a method like I’ve done, so we can call it imperatively.

Here is what I have achieved now.

https://user-images.githubusercontent.com/504909/192010803-cc1a6d2e-b0bc-4b65-b000-8324296fbf11.mp4

No, music does not resume. We thought notifying other apps might not be super important, but I made some tests with Insta and TikTok while having Spotify running, and closing those apps will automatically start music on Spotify again, while opening my App will pause Spotify and not resume it, even after I’ve closed my App. I don’t think that this is super important (since we care about our App, not about Spotify) but some users could be annoyed by this. So it might be worth investigating.

I’ve stepped through the versions here: https://github.com/expo/expo/blob/sdk-46/packages/expo-av/CHANGELOG.md

It looks to me like the breaking change for this behavior occurs between 11.0.1 and 11.1.0: a fresh development build on 11.0.1 works as expected, but one made on 11.1.0 has the issue with ducking.

From this testing, I think that the changes in https://github.com/expo/expo/pull/16578 caused this