@@ -9,6 +9,8 @@ import AVFoundation
99import Combine
1010
1111final class AudioEngineManager : ObservableObject {
12+
13+ // MARK: - Properties
1214 static let shared = AudioEngineManager ( )
1315
1416 var engine = AVAudioEngine ( )
@@ -22,11 +24,8 @@ final class AudioEngineManager: ObservableObject {
2224 private var intervalCancellable : AnyCancellable ?
2325 private var timerSubscription : Cancellable ?
2426
25- @Published var interval : Double = 1.0 {
26- didSet {
27- scheduleNextBuffer ( )
28- }
29- }
27+ @Published private var currentPlayingSound : Playable ?
28+ @Published var interval : Double = 1.0
3029
3130 @Published var pitch : Double = 0 {
3231 didSet {
@@ -48,6 +47,7 @@ final class AudioEngineManager: ObservableObject {
4847 }
4948 }
5049
50+ // MARK: - Initialization
5151 private init ( ) {
5252 setupAudioSession ( )
5353 setupEngine ( )
@@ -74,11 +74,19 @@ extension AudioEngineManager {
7474 }
7575
7676 private func setupConnections( ) {
77+ if engine. isRunning {
78+ engine. stop ( )
79+ }
7780 if let audioFile = audioFile {
7881 engine. connect ( player, to: pitchEffect, format: audioFile. processingFormat)
7982 engine. connect ( pitchEffect, to: volumeEffect, format: audioFile. processingFormat)
8083 engine. connect ( volumeEffect, to: engine. mainMixerNode, format: audioFile. processingFormat)
8184 }
85+ do {
86+ try engine. start ( )
87+ } catch {
88+ print ( " Unable to start engine: \( error. localizedDescription) " )
89+ }
8290 }
8391
8492 /**
@@ -90,6 +98,7 @@ extension AudioEngineManager {
9098 . removeDuplicates ( )
9199 . debounce ( for: . milliseconds( 300 ) , scheduler: RunLoop . main)
92100 . sink { [ weak self] _ in
101+ self ? . timerSubscription? . cancel ( )
93102 self ? . scheduleNextBuffer ( )
94103 }
95104 }
@@ -102,6 +111,8 @@ extension AudioEngineManager {
102111 func play< T: Playable > ( with sound: T ) {
103112 print ( #function)
104113
114+ currentPlayingSound = sound
115+
105116 let targetFile = sound. filter. rawValue
106117 guard let fileURL = Bundle . main. url ( forResource: targetFile, withExtension: MusicExtension . mp3. rawValue) else {
107118 print ( " File not found " )
@@ -117,7 +128,7 @@ extension AudioEngineManager {
117128 audioVariation = customSound. audioVariation
118129 }
119130
120- scheduleNextBuffer ( )
131+ scheduleNextBuffer ( with : sound )
121132
122133 } catch {
123134 print ( error. localizedDescription)
@@ -159,17 +170,27 @@ extension AudioEngineManager {
159170 ๋ค์ ์ค๋์ค ๋ฒํผ๋ฅผ ์ค์ผ์ค๋งํฉ๋๋ค. ์ค๋น๋ ๋ฒํผ๋ฅผ ์ฌ์ฉํด ์ค๋์ค๋ฅผ ์ฌ์ํ๊ณ , ์ฃผ์ด์ง ์ธํฐ๋ฒ์ ๋ฐ๋ผ ๋ค์ ๋ฒํผ ์ฌ์์ ์ค์ผ์ค๋งํฉ๋๋ค.
160171 Combine ํ๋ ์์ํฌ์ Timer.publish๋ฅผ ์ฌ์ฉํ์ฌ ์ง์ ๋ ์ธํฐ๋ฒ๋ง๋ค ๋ฒํผ ์ฌ์์ ๋ฐ๋ณตํฉ๋๋ค.
161172 */
162- private func scheduleNextBuffer( ) {
173+ private func scheduleNextBuffer( with playingSound : Playable ? = nil ) {
163174 print ( #function)
164175
165176 guard let buffer = audioBuffer else {
166177 print ( " Failed to prepare buffer " )
167178 return
168179 }
169180
181+ guard let sound = playingSound ?? currentPlayingSound else {
182+ print ( " No playing sound " )
183+ return
184+ }
185+
170186 // ์ทจ์ ๊ฐ๋ฅํ ํ์ด๋จธ๋ฅผ ๋ง๋ญ๋๋ค.
171187 timerSubscription? . cancel ( )
172- timerSubscription = Timer . publish ( every: interval, on: RunLoop . main, in: . common)
188+
189+ timerSubscription = Timer . publish (
190+ every: interval + sound. filter. duration,
191+ on: RunLoop . main,
192+ in: . common
193+ )
173194 . autoconnect ( )
174195 . sink { [ weak self] _ in
175196 guard let self = self else { return }
0 commit comments