Skip to content
This repository was archived by the owner on Feb 17, 2026. It is now read-only.
Closed
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
20 changes: 19 additions & 1 deletion ios/AudioPro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class AudioPro: RCTEventEmitter {
private let EVENT_TYPE_REMOTE_NEXT = "REMOTE_NEXT"
private let EVENT_TYPE_REMOTE_PREV = "REMOTE_PREV"
private let EVENT_TYPE_PLAYBACK_SPEED_CHANGED = "PLAYBACK_SPEED_CHANGED"
private let EVENT_TYPE_DUCK_BEGIN = "DUCK_BEGIN"
private let EVENT_TYPE_DUCK_END = "DUCK_END"

// Seek trigger sources
private let TRIGGER_SOURCE_USER = "USER"
Expand Down Expand Up @@ -128,6 +130,13 @@ class AudioPro: RCTEventEmitter {
wasPlayingBeforeInterruption = player?.rate != 0
log("wasPlayingBeforeInterruption set to", wasPlayingBeforeInterruption)

// Emit AUDIO_DUCK_BEGIN event
let duckBeginPayload: [String: Any] = [
"wasPlaying": wasPlayingBeforeInterruption,
"reason": "interruption"
]
sendEvent(type: EVENT_TYPE_DUCK_BEGIN, track: currentTrack, payload: duckBeginPayload)

if wasPlayingBeforeInterruption {
log("Interruption began while playing, pausing playback")
// Pause playback without changing shouldBePlaying flag
Expand All @@ -152,8 +161,17 @@ class AudioPro: RCTEventEmitter {
log("wasPlayingBeforeInterruption at end:", wasPlayingBeforeInterruption)
log("shouldResume:", options.contains(.shouldResume))

// Emit AUDIO_DUCK_END event
let willResume = wasPlayingBeforeInterruption && options.contains(.shouldResume)
let duckEndPayload: [String: Any] = [
"shouldResume": options.contains(.shouldResume),
"willResume": willResume,
"reason": "interruption"
]
sendEvent(type: EVENT_TYPE_DUCK_END, track: currentTrack, payload: duckEndPayload)

// If playback should resume and we have permission to do so
if wasPlayingBeforeInterruption && options.contains(.shouldResume) {
if willResume {
log("Interruption ended with resume option, resuming playback")

// Try to reactivate the audio session
Expand Down
6 changes: 4 additions & 2 deletions src/internalStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,12 @@ export const internalStore = create<AudioProStore>((set, get) => ({
setVolume: (volume) => set({ volume: normalizeVolume(volume) }),
setError: (error) => set({ error }),
updateFromEvent: (event) => {
// Early exit for simple remote commands (no state change)
// Early exit for simple remote commands and duck events (no state change)
if (
event.type === AudioProEventType.REMOTE_NEXT ||
event.type === AudioProEventType.REMOTE_PREV
event.type === AudioProEventType.REMOTE_PREV ||
event.type === AudioProEventType.DUCK_BEGIN ||
event.type === AudioProEventType.DUCK_END
) {
return;
}
Expand Down
4 changes: 4 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ export interface AudioProEvent {
error?: string;
errorCode?: number;
speed?: number;
wasPlaying?: boolean;
reason?: string;
shouldResume?: boolean;
willResume?: boolean;
};
}

Expand Down
4 changes: 4 additions & 0 deletions src/values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export enum AudioProEventType {
REMOTE_PREV = 'REMOTE_PREV',
/** Playback error has occurred */
PLAYBACK_ERROR = 'PLAYBACK_ERROR',
/** Audio ducking started (interruption began) */
DUCK_BEGIN = 'DUCK_BEGIN',
/** Audio ducking ended (interruption ended) */
DUCK_END = 'DUCK_END',
}

/**
Expand Down