From 427a395c0724d40ccf308eb26b01e72578eefb9e Mon Sep 17 00:00:00 2001 From: Mufakkharul Islam Nayem Date: Sun, 23 Jan 2022 09:46:51 +0600 Subject: [PATCH 1/4] Defer AVAudioEngine connect & start up until play is requested --- .../Utils/Audio/AudioPlayerManager.swift | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayerManager.swift b/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayerManager.swift index 51cbb8b..04bb522 100644 --- a/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayerManager.swift +++ b/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayerManager.swift @@ -40,12 +40,6 @@ class AudioPlayerManager { for _ in 0.. AudioPlayer { let player = AudioPlayer() engine.attach(player.node) - engine.connect(player.node, to: engine.mainMixerNode, format: nil) + // according to this: https://stackoverflow.com/a/55505717/3687801, we will defer connecting nodes to engine up until play command of player node + // connect is done in the performPlaybackOnFirstAvailablePlayer() function + // engine.connect(player.node, to: engine.mainMixerNode, format: nil) return player } From 513ce71c3a8a122e1d06ef91f788ca3b5d20eda6 Mon Sep 17 00:00:00 2001 From: Mufakkharul Islam Nayem Date: Sun, 23 Jan 2022 10:13:29 +0600 Subject: [PATCH 2/4] Add pause functionality in AudioPlayable protocol --- Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayable.swift | 5 +++++ .../Utils/Audio/AudioPlayerManager.swift | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayable.swift b/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayable.swift index 6f7aa49..4414e0a 100644 --- a/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayable.swift +++ b/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayable.swift @@ -47,4 +47,9 @@ extension AudioPlayable { guard let _audioFile = audioFile else { return } playSound(audioFile: _audioFile) } + + /// Pause sound(s) + func pauseAllSound() { + audioPlayerManager.pauseAll() + } } diff --git a/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayerManager.swift b/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayerManager.swift index 04bb522..ce93498 100644 --- a/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayerManager.swift +++ b/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayerManager.swift @@ -81,6 +81,14 @@ class AudioPlayerManager { } } + func pauseAll() { + // according to `AVAudioEngine` documentation: https://developer.apple.com/documentation/avfaudio/avaudioengine + // pause/stop needs to be done on player & engine both + // we will stop the player(s) but keep our engine paused, because it's highly likely that the engine is going to be used again in a moment + players.synchronizedArray.forEach { $0.node.stop() } + engine.pause() + } + // MARK: - Internal Functions private func performSoundPlayback(_ sound: SoundIdentifier, allowOverlap: Bool) { From 83141f98a3781e95f02ac32d9f79939fce4673ef Mon Sep 17 00:00:00 2001 From: Mufakkharul Islam Nayem Date: Sun, 23 Jan 2022 10:19:01 +0600 Subject: [PATCH 3/4] Pause sound(s) when fortune wheel rotation stops --- .../SwiftFortuneWheel/SwiftFortuneWheel.swift | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftFortuneWheel/SwiftFortuneWheel.swift b/Sources/SwiftFortuneWheel/SwiftFortuneWheel.swift index f965513..2a40864 100644 --- a/Sources/SwiftFortuneWheel/SwiftFortuneWheel.swift +++ b/Sources/SwiftFortuneWheel/SwiftFortuneWheel.swift @@ -415,7 +415,9 @@ public extension SwiftFortuneWheel { self.animator.addRotationAnimation(fullRotationsCount: 0, animationDuration: animationDuration, rotationOffset: rotation, - completionBlock: nil) + completionBlock: { _ in + self.pauseAllSound() + }) } @@ -429,7 +431,9 @@ public extension SwiftFortuneWheel { self.animator.addRotationAnimation(fullRotationsCount: 0, animationDuration: animationDuration, rotationOffset: rotationOffset, - completionBlock: nil) + completionBlock: { _ in + self.pauseAllSound() + }) } @@ -443,7 +447,13 @@ public extension SwiftFortuneWheel { DispatchQueue.main.async { self.stopRotation() - self.animator.addRotationAnimation(fullRotationsCount: fullRotationsCount, animationDuration: animationDuration, rotationOffset: rotationOffset, completionBlock: completion, onEdgeCollision: { [weak self] progress in self?.impactIfNeeded(for: .edge) + self.animator.addRotationAnimation(fullRotationsCount: fullRotationsCount, animationDuration: animationDuration, rotationOffset: rotationOffset, completionBlock: { finished in + if finished { + self.pauseAllSound() + } + completion?(finished) + }, onEdgeCollision: { [weak self] progress in + self?.impactIfNeeded(for: .edge) self?.onEdgeCollision?(progress) }) { [weak self] (progress) in @@ -523,6 +533,7 @@ public extension SwiftFortuneWheel { /// Stops rotation animation func stopRotation() { self.animator.stop() + self.pauseAllSound() } /// Starts rotation animation and stops rotation at the specified index and rotation angle offset From 0b623d4845e3065c69cb17afa9613c3af37e8feb Mon Sep 17 00:00:00 2001 From: Mufakkharul Islam Nayem Date: Thu, 27 Jan 2022 14:45:56 +0600 Subject: [PATCH 4/4] Prevent crash by performing sound playback on the current queue --- .../SwiftFortuneWheel/Utils/Audio/AudioPlayerManager.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayerManager.swift b/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayerManager.swift index ce93498..d938b59 100644 --- a/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayerManager.swift +++ b/Sources/SwiftFortuneWheel/Utils/Audio/AudioPlayerManager.swift @@ -76,9 +76,9 @@ class AudioPlayerManager { /// - sound: Sound's identifier /// - allowOverlap: Allow overlap sound each other func play(_ sound: SoundIdentifier, allowOverlap: Bool = true) { - DispatchQueue.global(qos: .userInitiated).async { [weak self] in - self?.performSoundPlayback(sound, allowOverlap: allowOverlap) - } + // modified to run on the current queue (i.e. main queue) instead of global queue + // to prevent crash at the time of subsequent rotation of spin wheel + performSoundPlayback(sound, allowOverlap: allowOverlap) } func pauseAll() {