Add support for audio ducking in plugin configuration
🎵 Feature Overview
This PR introduces audio ducking functionality, allowing the plugin to lower the volume of other audio sources instead of completely stopping them when taking audio focus. This provides a much better user experience for apps that need to play audio alongside other media which are not supposed to be stopped.
🔧 Implementation Details
New AudioFocusMode Enum
export enum AudioFocusMode {
NONE = 'none', // Allow mixed audio (default)
EXCLUSIVE = 'exclusive', // Take exclusive focus, pause other audio
DUCK = 'duck' // Take focus but duck other audio volume
}
Updated Configuration API
// Before (old API)
NativeAudio.configure({
fade: false,
focus: true // boolean flag
});
// After (new API)
NativeAudio.configure({
fade: false,
audioFocusMode: AudioFocusMode.DUCK // enum value
});
Platform-Specific Implementations
iOS Implementation
NONE:AVAudioSession.Category.ambientEXCLUSIVE:AVAudioSession.Category.playbackDUCK:AVAudioSession.Category.playbackwith.duckOthersoption
Android Implementation
NONE:abandonAudioFocus()EXCLUSIVE:requestAudioFocus()withAUDIOFOCUS_GAINDUCK:requestAudioFocus()withAUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
Web Implementation
- No changes needed (configure method already throws for web platform)
⚠️ Breaking Changes
What Changed
The focus boolean parameter in ConfigureOptions has been replaced with audioFocusMode enum.
Migration Guide
// ❌ OLD - Will no longer work
NativeAudio.configure({
fade: false,
focus: true
});
NativeAudio.configure({
fade: false,
focus: false
});
// ✅ NEW - Updated API
NativeAudio.configure({
fade: false,
audioFocusMode: AudioFocusMode.EXCLUSIVE // Equivalent to focus: true
});
NativeAudio.configure({
fade: false,
audioFocusMode: AudioFocusMode.NONE // Equivalent to focus: false (default)
});
Impact Assessment
- No impact if you never called
configure()- default behavior unchanged - Breaking change only if you explicitly used
focus: true/falseparameter - Easy migration - direct 1:1 mapping available
🤔 Why Breaking Changes Were Necessary
1. Type Safety
The old boolean focus parameter allowed invalid states and didn't prevent misconfigurations. The enum approach makes invalid states impossible at compile-time:
// Old API - these invalid combinations were possible:
configure({ focus: true, ducking: true }); // ⚠️ Correct setup needs internal implementation knowledge
configure({ focus: false, ducking: true }); // ❌ Invalid - ducking requires focus
// New API - invalid states are impossible:
configure({ audioFocusMode: AudioFocusMode.DUCK }); // ✅ Clear and unambiguous
2. Future Extensibility
The enum approach allows easy addition of new audio focus modes without API changes:
// Future possibilities:
AudioFocusMode.DUCK_WITH_INTERRUPTED_SPEACH;
3. API Clarity
The enum makes the intent explicit and self-documenting:
// Old API - unclear what focus means
configure({ focus: true });
// New API - crystal clear intent
configure({ audioFocusMode: AudioFocusMode.DUCK });
4. Industry Standards
This approach aligns with platform conventions:
- Android: Uses similar enum-like constants (
AUDIOFOCUS_GAIN,AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) - iOS: Uses category + options pattern that maps well to discrete modes
🎯 Most important use case
- Music Apps: Duck background music when playing sound effects or playing spoken instructions etc.
This feature allows e.g. a navigation app to play directions sounds and not stop playing music
🔄 Backward Compatibility Strategy
While this is technically a breaking change, the impact is minimal:
- Default behavior unchanged - apps not using
configure()work identically - Clear migration path - simple 1:1 mapping for existing
focususage - Comprehensive documentation - examples show exact replacements needed
- Enhanced functionality - apps get better audio behavior after migration
Limitations
Audio Session Management
The current implementation uses a resource-based session management approach where audio focus is only released when the last asset is unloaded. This design decision was made to balance simplicity with common usage patterns.
Current Behavior:
- Audio focus is requested when
configure()is called withEXCLUSIVEorDUCKmodes - Focus remains active while any audio assets are loaded (even if not playing)
- Focus is only released when the last asset is unloaded via
unload()
Rationale:
This approach works well for the typical usage pattern of preload() → play() → unload() but may not be optimal for all use cases.
Alternative Session Management Strategies
- Playback-based management: Release focus when all audio stops playing (not just unloaded)
When Current Approach May Not Be Ideal
- Long-lived assets: Apps that preload many assets but play them sporadically
- Background apps: Apps that maintain loaded assets while backgrounded
- Complex audio workflows: Apps with multiple simultaneous audio contexts
Future Considerations
The current implementation prioritizes simplicity and covers the most common usage patterns. More sophisticated session management could be added in future versions based on community feedback and real-world usage requirements.
Let me know if you are interested in this Feature and if I should improve the session state management.
I guess this PR is related or might even fix these two issues: On iOS stops the background music On iOS make the AVAudioSession.Category configurable to support the silent switch
I just wanted to ask if you are interested in my suggested feature. I am happy to improve the implementation further if needed.
It looks like the maintainer bazuka5801 is not active anymore since his GitHub activity is zero since July?
Thats why I am also pinging ryaa who made the most recent release of this plugin.
Thanks in advance.