@capgo/native-audio 7.3.18 → 7.3.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ios/Plugin/AudioAsset.swift +91 -38
- package/ios/Plugin/Plugin.swift +26 -9
- package/ios/Plugin/RemoteAudioAsset.swift +90 -44
- package/package.json +1 -1
@@ -67,7 +67,8 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
67
67
|
// Limit channels to a reasonable maximum to prevent resource issues
|
68
68
|
let channelCount = min(max(channels ?? 1, 1), MAX_CHANNELS)
|
69
69
|
|
70
|
-
owner.executeOnAudioQueue { [self] in
|
70
|
+
owner.executeOnAudioQueue { [weak self] in
|
71
|
+
guard let self = self else { return }
|
71
72
|
for _ in 0..<channelCount {
|
72
73
|
do {
|
73
74
|
let player = try AVAudioPlayer(contentsOf: pathUrl)
|
@@ -88,10 +89,10 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
88
89
|
deinit {
|
89
90
|
currentTimeTimer?.invalidate()
|
90
91
|
currentTimeTimer = nil
|
91
|
-
|
92
|
+
|
92
93
|
fadeTimer?.invalidate()
|
93
94
|
fadeTimer = nil
|
94
|
-
|
95
|
+
|
95
96
|
// Clean up any players that might still be playing
|
96
97
|
for player in channels {
|
97
98
|
if player.isPlaying {
|
@@ -107,7 +108,9 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
107
108
|
*/
|
108
109
|
func getCurrentTime() -> TimeInterval {
|
109
110
|
var result: TimeInterval = 0
|
110
|
-
owner?.executeOnAudioQueue { [self] in
|
111
|
+
owner?.executeOnAudioQueue { [weak self] in
|
112
|
+
guard let self = self else { return }
|
113
|
+
|
111
114
|
if channels.isEmpty || playIndex >= channels.count {
|
112
115
|
result = 0
|
113
116
|
return
|
@@ -123,7 +126,9 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
123
126
|
* - Parameter time: Time in seconds
|
124
127
|
*/
|
125
128
|
func setCurrentTime(time: TimeInterval) {
|
126
|
-
owner?.executeOnAudioQueue { [self] in
|
129
|
+
owner?.executeOnAudioQueue { [weak self] in
|
130
|
+
guard let self = self else { return }
|
131
|
+
|
127
132
|
if channels.isEmpty || playIndex >= channels.count {
|
128
133
|
return
|
129
134
|
}
|
@@ -140,7 +145,9 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
140
145
|
*/
|
141
146
|
func getDuration() -> TimeInterval {
|
142
147
|
var result: TimeInterval = 0
|
143
|
-
owner?.executeOnAudioQueue { [self] in
|
148
|
+
owner?.executeOnAudioQueue { [weak self] in
|
149
|
+
guard let self = self else { return }
|
150
|
+
|
144
151
|
if channels.isEmpty || playIndex >= channels.count {
|
145
152
|
result = 0
|
146
153
|
return
|
@@ -161,7 +168,9 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
161
168
|
stopCurrentTimeUpdates()
|
162
169
|
stopFadeTimer()
|
163
170
|
|
164
|
-
owner?.executeOnAudioQueue { [self] in
|
171
|
+
owner?.executeOnAudioQueue { [weak self] in
|
172
|
+
guard let self = self else { return }
|
173
|
+
|
165
174
|
guard !channels.isEmpty else { return }
|
166
175
|
|
167
176
|
// Reset play index if it's out of bounds
|
@@ -186,14 +195,16 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
186
195
|
} else {
|
187
196
|
player.play()
|
188
197
|
}
|
189
|
-
|
198
|
+
|
190
199
|
playIndex = (playIndex + 1) % channels.count
|
191
200
|
startCurrentTimeUpdates()
|
192
201
|
}
|
193
202
|
}
|
194
203
|
|
195
204
|
func playWithFade(time: TimeInterval) {
|
196
|
-
owner?.executeOnAudioQueue { [self] in
|
205
|
+
owner?.executeOnAudioQueue { [weak self] in
|
206
|
+
guard let self = self else { return }
|
207
|
+
|
197
208
|
guard !channels.isEmpty else { return }
|
198
209
|
|
199
210
|
// Reset play index if it's out of bounds
|
@@ -234,36 +245,58 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
234
245
|
|
235
246
|
player.volume = startVolume
|
236
247
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
return
|
241
|
-
}
|
248
|
+
// Create timer on main thread
|
249
|
+
DispatchQueue.main.async { [weak self, weak player] in
|
250
|
+
guard let self = self else { return }
|
242
251
|
|
243
|
-
|
244
|
-
|
245
|
-
|
252
|
+
let timer = Timer.scheduledTimer(withTimeInterval: TimeInterval(timeInterval), repeats: true) { [weak self, weak player] timer in
|
253
|
+
guard let strongSelf = self, let strongPlayer = player else {
|
254
|
+
timer.invalidate()
|
255
|
+
return
|
256
|
+
}
|
246
257
|
|
247
|
-
|
258
|
+
currentStep += 1
|
259
|
+
let progress = Float(currentStep) / Float(totalSteps)
|
260
|
+
let newVolume = startVolume + progress * (endVolume - startVolume)
|
248
261
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
262
|
+
// Update player on audio queue
|
263
|
+
strongSelf.owner?.executeOnAudioQueue {
|
264
|
+
strongPlayer.volume = newVolume
|
265
|
+
}
|
266
|
+
|
267
|
+
if currentStep >= totalSteps {
|
268
|
+
strongSelf.owner?.executeOnAudioQueue {
|
269
|
+
strongPlayer.volume = endVolume
|
270
|
+
}
|
271
|
+
timer.invalidate()
|
272
|
+
|
273
|
+
// Update timer reference on main thread
|
274
|
+
DispatchQueue.main.async {
|
275
|
+
strongSelf.fadeTimer = nil
|
276
|
+
}
|
277
|
+
}
|
253
278
|
}
|
279
|
+
|
280
|
+
self.fadeTimer = timer
|
281
|
+
RunLoop.current.add(timer, forMode: .common)
|
254
282
|
}
|
255
|
-
RunLoop.current.add(fadeTimer!, forMode: .common)
|
256
283
|
}
|
257
284
|
|
258
285
|
internal func stopFadeTimer() {
|
259
286
|
DispatchQueue.main.async { [weak self] in
|
260
|
-
self
|
261
|
-
|
287
|
+
guard let self = self else { return }
|
288
|
+
|
289
|
+
if let timer = self.fadeTimer {
|
290
|
+
timer.invalidate()
|
291
|
+
self.fadeTimer = nil
|
292
|
+
}
|
262
293
|
}
|
263
294
|
}
|
264
295
|
|
265
296
|
func pause() {
|
266
|
-
owner?.executeOnAudioQueue { [self] in
|
297
|
+
owner?.executeOnAudioQueue { [weak self] in
|
298
|
+
guard let self = self else { return }
|
299
|
+
|
267
300
|
stopCurrentTimeUpdates()
|
268
301
|
|
269
302
|
// Check for valid playIndex
|
@@ -275,7 +308,9 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
275
308
|
}
|
276
309
|
|
277
310
|
func resume() {
|
278
|
-
owner?.executeOnAudioQueue { [self] in
|
311
|
+
owner?.executeOnAudioQueue { [weak self] in
|
312
|
+
guard let self = self else { return }
|
313
|
+
|
279
314
|
// Check for valid playIndex
|
280
315
|
guard !channels.isEmpty && playIndex < channels.count else { return }
|
281
316
|
|
@@ -287,7 +322,9 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
287
322
|
}
|
288
323
|
|
289
324
|
func stop() {
|
290
|
-
owner?.executeOnAudioQueue { [self] in
|
325
|
+
owner?.executeOnAudioQueue { [weak self] in
|
326
|
+
guard let self = self else { return }
|
327
|
+
|
291
328
|
stopCurrentTimeUpdates()
|
292
329
|
stopFadeTimer()
|
293
330
|
|
@@ -304,7 +341,9 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
304
341
|
|
305
342
|
func stopWithFade() {
|
306
343
|
// Store current player locally to avoid race conditions with playIndex
|
307
|
-
owner?.executeOnAudioQueue { [self] in
|
344
|
+
owner?.executeOnAudioQueue { [weak self] in
|
345
|
+
guard let self = self else { return }
|
346
|
+
|
308
347
|
guard !channels.isEmpty && playIndex < channels.count else {
|
309
348
|
stop()
|
310
349
|
return
|
@@ -329,7 +368,9 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
329
368
|
}
|
330
369
|
|
331
370
|
func loop() {
|
332
|
-
owner?.executeOnAudioQueue { [self] in
|
371
|
+
owner?.executeOnAudioQueue { [weak self] in
|
372
|
+
guard let self = self else { return }
|
373
|
+
|
333
374
|
self.stop()
|
334
375
|
|
335
376
|
guard !channels.isEmpty && playIndex < channels.count else { return }
|
@@ -344,7 +385,9 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
344
385
|
}
|
345
386
|
|
346
387
|
func unload() {
|
347
|
-
owner?.executeOnAudioQueue { [self] in
|
388
|
+
owner?.executeOnAudioQueue { [weak self] in
|
389
|
+
guard let self = self else { return }
|
390
|
+
|
348
391
|
self.stop()
|
349
392
|
stopCurrentTimeUpdates()
|
350
393
|
stopFadeTimer()
|
@@ -357,7 +400,9 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
357
400
|
* - Parameter volume: Volume level (0.0-1.0)
|
358
401
|
*/
|
359
402
|
func setVolume(volume: NSNumber!) {
|
360
|
-
owner?.executeOnAudioQueue { [self] in
|
403
|
+
owner?.executeOnAudioQueue { [weak self] in
|
404
|
+
guard let self = self else { return }
|
405
|
+
|
361
406
|
// Ensure volume is in valid range
|
362
407
|
let validVolume = min(max(volume.floatValue, Constant.MinVolume), Constant.MaxVolume)
|
363
408
|
for player in channels {
|
@@ -371,7 +416,9 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
371
416
|
* - Parameter rate: Playback rate (0.5-2.0 is typical range)
|
372
417
|
*/
|
373
418
|
func setRate(rate: NSNumber!) {
|
374
|
-
owner?.executeOnAudioQueue { [self] in
|
419
|
+
owner?.executeOnAudioQueue { [weak self] in
|
420
|
+
guard let self = self else { return }
|
421
|
+
|
375
422
|
// Ensure rate is in valid range
|
376
423
|
let validRate = min(max(rate.floatValue, Constant.MinRate), Constant.MaxRate)
|
377
424
|
for player in channels {
|
@@ -384,11 +431,13 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
384
431
|
* AVAudioPlayerDelegate method called when playback finishes
|
385
432
|
*/
|
386
433
|
public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
|
387
|
-
owner?.executeOnAudioQueue { [self] in
|
434
|
+
owner?.executeOnAudioQueue { [weak self] in
|
435
|
+
guard let self = self else { return }
|
436
|
+
|
388
437
|
self.owner?.notifyListeners("complete", data: [
|
389
438
|
"assetId": self.assetId
|
390
439
|
])
|
391
|
-
|
440
|
+
|
392
441
|
// Notify the owner that this player finished
|
393
442
|
// The owner will check if any other assets are still playing
|
394
443
|
owner?.audioPlayerDidFinishPlaying(player, successfully: flag)
|
@@ -403,7 +452,9 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
403
452
|
|
404
453
|
func isPlaying() -> Bool {
|
405
454
|
var result: Bool = false
|
406
|
-
owner?.executeOnAudioQueue { [self] in
|
455
|
+
owner?.executeOnAudioQueue { [weak self] in
|
456
|
+
guard let self = self else { return }
|
457
|
+
|
407
458
|
if channels.isEmpty || playIndex >= channels.count {
|
408
459
|
result = false
|
409
460
|
return
|
@@ -440,8 +491,10 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
440
491
|
|
441
492
|
internal func stopCurrentTimeUpdates() {
|
442
493
|
DispatchQueue.main.async { [weak self] in
|
443
|
-
self
|
444
|
-
|
494
|
+
guard let self = self else { return }
|
495
|
+
|
496
|
+
self.currentTimeTimer?.invalidate()
|
497
|
+
self.currentTimeTimer = nil
|
445
498
|
}
|
446
499
|
}
|
447
500
|
}
|
package/ios/Plugin/Plugin.swift
CHANGED
@@ -143,7 +143,7 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
143
143
|
} else {
|
144
144
|
try self.session.setCategory(AVAudioSession.Category.playback, options: .mixWithOthers)
|
145
145
|
}
|
146
|
-
|
146
|
+
|
147
147
|
// Only activate if needed (background mode)
|
148
148
|
if background {
|
149
149
|
try self.session.setActive(true)
|
@@ -195,7 +195,7 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
195
195
|
return false
|
196
196
|
}
|
197
197
|
}
|
198
|
-
|
198
|
+
|
199
199
|
// Only deactivate if no assets are playing
|
200
200
|
if !hasPlayingAssets {
|
201
201
|
try self.session.setActive(false, options: .notifyOthersOnDeactivation)
|
@@ -210,7 +210,7 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
210
210
|
// Instead, check if all players are done
|
211
211
|
audioQueue.async { [weak self] in
|
212
212
|
guard let self = self else { return }
|
213
|
-
|
213
|
+
|
214
214
|
// Avoid recursive calls by checking if the asset is still in the list
|
215
215
|
let hasPlayingAssets = self.audioList.values.contains { asset in
|
216
216
|
if let audioAsset = asset as? AudioAsset {
|
@@ -219,7 +219,7 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
219
219
|
}
|
220
220
|
return false
|
221
221
|
}
|
222
|
-
|
222
|
+
|
223
223
|
// Only end the session if no more assets are playing
|
224
224
|
if !hasPlayingAssets {
|
225
225
|
self.endSession()
|
@@ -478,11 +478,28 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
478
478
|
|
479
479
|
var basePath: String?
|
480
480
|
if let url = URL(string: assetPath), url.scheme != nil {
|
481
|
-
//
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
481
|
+
// Check if it's a local file URL or a remote URL
|
482
|
+
if url.isFileURL {
|
483
|
+
// Handle local file URL
|
484
|
+
let fileURL = url
|
485
|
+
basePath = fileURL.path
|
486
|
+
|
487
|
+
if let basePath = basePath, FileManager.default.fileExists(atPath: basePath) {
|
488
|
+
let audioAsset = AudioAsset(
|
489
|
+
owner: self,
|
490
|
+
withAssetId: audioId, withPath: basePath, withChannels: channels,
|
491
|
+
withVolume: volume, withFadeDelay: delay)
|
492
|
+
self.audioList[audioId] = audioAsset
|
493
|
+
call.resolve()
|
494
|
+
return
|
495
|
+
}
|
496
|
+
} else {
|
497
|
+
// Handle remote URL
|
498
|
+
let remoteAudioAsset = RemoteAudioAsset(owner: self, withAssetId: audioId, withPath: assetPath, withChannels: channels, withVolume: volume, withFadeDelay: delay)
|
499
|
+
self.audioList[audioId] = remoteAudioAsset
|
500
|
+
call.resolve()
|
501
|
+
return
|
502
|
+
}
|
486
503
|
} else if isLocalUrl == false {
|
487
504
|
// Handle public folder
|
488
505
|
assetPath = assetPath.starts(with: "public/") ? assetPath : "public/" + assetPath
|
@@ -15,7 +15,9 @@ public class RemoteAudioAsset: AudioAsset {
|
|
15
15
|
override init(owner: NativeAudio, withAssetId assetId: String, withPath path: String!, withChannels channels: Int!, withVolume volume: Float!, withFadeDelay delay: Float!) {
|
16
16
|
super.init(owner: owner, withAssetId: assetId, withPath: path, withChannels: channels ?? 1, withVolume: volume ?? 1.0, withFadeDelay: delay ?? 0.0)
|
17
17
|
|
18
|
-
owner.executeOnAudioQueue { [self] in
|
18
|
+
owner.executeOnAudioQueue { [weak self] in
|
19
|
+
guard let self = self else { return }
|
20
|
+
|
19
21
|
guard let url = URL(string: path ?? "") else {
|
20
22
|
print("Invalid URL: \(String(describing: path))")
|
21
23
|
return
|
@@ -86,7 +88,9 @@ public class RemoteAudioAsset: AudioAsset {
|
|
86
88
|
}
|
87
89
|
|
88
90
|
func playerDidFinishPlaying(player: AVPlayer) {
|
89
|
-
owner?.executeOnAudioQueue { [self] in
|
91
|
+
owner?.executeOnAudioQueue { [weak self] in
|
92
|
+
guard let self = self else { return }
|
93
|
+
|
90
94
|
self.owner?.notifyListeners("complete", data: [
|
91
95
|
"assetId": self.assetId
|
92
96
|
])
|
@@ -94,7 +98,9 @@ public class RemoteAudioAsset: AudioAsset {
|
|
94
98
|
}
|
95
99
|
|
96
100
|
override func play(time: TimeInterval, delay: TimeInterval) {
|
97
|
-
owner?.executeOnAudioQueue { [self] in
|
101
|
+
owner?.executeOnAudioQueue { [weak self] in
|
102
|
+
guard let self = self else { return }
|
103
|
+
|
98
104
|
guard !players.isEmpty else { return }
|
99
105
|
|
100
106
|
// Reset play index if it's out of bounds
|
@@ -124,7 +130,9 @@ public class RemoteAudioAsset: AudioAsset {
|
|
124
130
|
}
|
125
131
|
|
126
132
|
override func pause() {
|
127
|
-
owner?.executeOnAudioQueue { [self] in
|
133
|
+
owner?.executeOnAudioQueue { [weak self] in
|
134
|
+
guard let self = self else { return }
|
135
|
+
|
128
136
|
guard !players.isEmpty && playIndex < players.count else { return }
|
129
137
|
|
130
138
|
let player = players[playIndex]
|
@@ -134,7 +142,9 @@ public class RemoteAudioAsset: AudioAsset {
|
|
134
142
|
}
|
135
143
|
|
136
144
|
override func resume() {
|
137
|
-
owner?.executeOnAudioQueue { [self] in
|
145
|
+
owner?.executeOnAudioQueue { [weak self] in
|
146
|
+
guard let self = self else { return }
|
147
|
+
|
138
148
|
guard !players.isEmpty && playIndex < players.count else { return }
|
139
149
|
|
140
150
|
let player = players[playIndex]
|
@@ -143,24 +153,27 @@ public class RemoteAudioAsset: AudioAsset {
|
|
143
153
|
// Add notification observer for when playback stops
|
144
154
|
cleanupNotificationObservers()
|
145
155
|
|
156
|
+
// Capture weak reference to self
|
146
157
|
let observer = NotificationCenter.default.addObserver(
|
147
158
|
forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
|
148
159
|
object: player.currentItem,
|
149
|
-
queue:
|
150
|
-
|
160
|
+
queue: OperationQueue.main) { [weak self, weak player] notification in
|
161
|
+
guard let strongSelf = self, let strongPlayer = player else { return }
|
151
162
|
|
152
|
-
|
153
|
-
|
154
|
-
|
163
|
+
if let currentItem = notification.object as? AVPlayerItem,
|
164
|
+
strongPlayer.currentItem == currentItem {
|
165
|
+
strongSelf.playerDidFinishPlaying(player: strongPlayer)
|
166
|
+
}
|
155
167
|
}
|
156
|
-
}
|
157
168
|
notificationObservers.append(observer)
|
158
169
|
startCurrentTimeUpdates()
|
159
170
|
}
|
160
171
|
}
|
161
172
|
|
162
173
|
override func stop() {
|
163
|
-
owner?.executeOnAudioQueue { [self] in
|
174
|
+
owner?.executeOnAudioQueue { [weak self] in
|
175
|
+
guard let self = self else { return }
|
176
|
+
|
164
177
|
stopCurrentTimeUpdates()
|
165
178
|
|
166
179
|
for player in players {
|
@@ -178,7 +191,9 @@ public class RemoteAudioAsset: AudioAsset {
|
|
178
191
|
}
|
179
192
|
|
180
193
|
override func loop() {
|
181
|
-
owner?.executeOnAudioQueue { [self] in
|
194
|
+
owner?.executeOnAudioQueue { [weak self] in
|
195
|
+
guard let self = self else { return }
|
196
|
+
|
182
197
|
cleanupNotificationObservers()
|
183
198
|
|
184
199
|
for (index, player) in players.enumerated() {
|
@@ -189,14 +204,15 @@ public class RemoteAudioAsset: AudioAsset {
|
|
189
204
|
let observer = NotificationCenter.default.addObserver(
|
190
205
|
forName: .AVPlayerItemDidPlayToEndTime,
|
191
206
|
object: playerItem,
|
192
|
-
queue:
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
207
|
+
queue: OperationQueue.main) { [weak self, weak player] notification in
|
208
|
+
guard let strongPlayer = player,
|
209
|
+
let strongSelf = self,
|
210
|
+
let item = notification.object as? AVPlayerItem,
|
211
|
+
strongPlayer.currentItem === item else { return }
|
212
|
+
|
213
|
+
strongPlayer.seek(to: .zero)
|
214
|
+
strongPlayer.play()
|
215
|
+
}
|
200
216
|
|
201
217
|
notificationObservers.append(observer)
|
202
218
|
|
@@ -218,7 +234,9 @@ public class RemoteAudioAsset: AudioAsset {
|
|
218
234
|
}
|
219
235
|
|
220
236
|
@objc func playerItemDidReachEnd(notification: Notification) {
|
221
|
-
owner?.executeOnAudioQueue { [self] in
|
237
|
+
owner?.executeOnAudioQueue { [weak self] in
|
238
|
+
guard let self = self else { return }
|
239
|
+
|
222
240
|
if let playerItem = notification.object as? AVPlayerItem,
|
223
241
|
let player = players.first(where: { $0.currentItem == playerItem }) {
|
224
242
|
player.seek(to: .zero)
|
@@ -228,7 +246,9 @@ public class RemoteAudioAsset: AudioAsset {
|
|
228
246
|
}
|
229
247
|
|
230
248
|
override func unload() {
|
231
|
-
owner?.executeOnAudioQueue { [self] in
|
249
|
+
owner?.executeOnAudioQueue { [weak self] in
|
250
|
+
guard let self = self else { return }
|
251
|
+
|
232
252
|
stopCurrentTimeUpdates()
|
233
253
|
stop()
|
234
254
|
|
@@ -245,7 +265,9 @@ public class RemoteAudioAsset: AudioAsset {
|
|
245
265
|
}
|
246
266
|
|
247
267
|
override func setVolume(volume: NSNumber!) {
|
248
|
-
owner?.executeOnAudioQueue { [self] in
|
268
|
+
owner?.executeOnAudioQueue { [weak self] in
|
269
|
+
guard let self = self else { return }
|
270
|
+
|
249
271
|
// Ensure volume is in valid range (0.0-1.0)
|
250
272
|
let validVolume = min(max(volume.floatValue, Constant.MinVolume), Constant.MaxVolume)
|
251
273
|
for player in players {
|
@@ -255,7 +277,9 @@ public class RemoteAudioAsset: AudioAsset {
|
|
255
277
|
}
|
256
278
|
|
257
279
|
override func setRate(rate: NSNumber!) {
|
258
|
-
owner?.executeOnAudioQueue { [self] in
|
280
|
+
owner?.executeOnAudioQueue { [weak self] in
|
281
|
+
guard let self = self else { return }
|
282
|
+
|
259
283
|
// Ensure rate is in valid range
|
260
284
|
let validRate = min(max(rate.floatValue, Constant.MinRate), Constant.MaxRate)
|
261
285
|
for player in players {
|
@@ -266,7 +290,9 @@ public class RemoteAudioAsset: AudioAsset {
|
|
266
290
|
|
267
291
|
override func isPlaying() -> Bool {
|
268
292
|
var result = false
|
269
|
-
owner?.executeOnAudioQueue { [self] in
|
293
|
+
owner?.executeOnAudioQueue { [weak self] in
|
294
|
+
guard let self = self else { return }
|
295
|
+
|
270
296
|
guard !players.isEmpty && playIndex < players.count else {
|
271
297
|
result = false
|
272
298
|
return
|
@@ -279,7 +305,9 @@ public class RemoteAudioAsset: AudioAsset {
|
|
279
305
|
|
280
306
|
override func getCurrentTime() -> TimeInterval {
|
281
307
|
var result: TimeInterval = 0
|
282
|
-
owner?.executeOnAudioQueue { [self] in
|
308
|
+
owner?.executeOnAudioQueue { [weak self] in
|
309
|
+
guard let self = self else { return }
|
310
|
+
|
283
311
|
guard !players.isEmpty && playIndex < players.count else {
|
284
312
|
result = 0
|
285
313
|
return
|
@@ -292,7 +320,9 @@ public class RemoteAudioAsset: AudioAsset {
|
|
292
320
|
|
293
321
|
override func getDuration() -> TimeInterval {
|
294
322
|
var result: TimeInterval = 0
|
295
|
-
owner?.executeOnAudioQueue { [self] in
|
323
|
+
owner?.executeOnAudioQueue { [weak self] in
|
324
|
+
guard let self = self else { return }
|
325
|
+
|
296
326
|
guard !players.isEmpty && playIndex < players.count else {
|
297
327
|
result = 0
|
298
328
|
return
|
@@ -308,7 +338,9 @@ public class RemoteAudioAsset: AudioAsset {
|
|
308
338
|
}
|
309
339
|
|
310
340
|
override func playWithFade(time: TimeInterval) {
|
311
|
-
owner?.executeOnAudioQueue { [self] in
|
341
|
+
owner?.executeOnAudioQueue { [weak self] in
|
342
|
+
guard let self = self else { return }
|
343
|
+
|
312
344
|
guard !players.isEmpty && playIndex < players.count else { return }
|
313
345
|
|
314
346
|
let player = players[playIndex]
|
@@ -332,7 +364,9 @@ public class RemoteAudioAsset: AudioAsset {
|
|
332
364
|
}
|
333
365
|
|
334
366
|
override func stopWithFade() {
|
335
|
-
owner?.executeOnAudioQueue { [self] in
|
367
|
+
owner?.executeOnAudioQueue { [weak self] in
|
368
|
+
guard let self = self else { return }
|
369
|
+
|
336
370
|
guard !players.isEmpty && playIndex < players.count else {
|
337
371
|
stop()
|
338
372
|
return
|
@@ -390,24 +424,36 @@ public class RemoteAudioAsset: AudioAsset {
|
|
390
424
|
|
391
425
|
stopFadeTimer()
|
392
426
|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
return
|
397
|
-
}
|
427
|
+
// Ensure timer creation happens on main thread
|
428
|
+
DispatchQueue.main.async { [weak self, weak player] in
|
429
|
+
guard let self = self else { return }
|
398
430
|
|
399
|
-
|
400
|
-
|
401
|
-
|
431
|
+
self.fadeTimer = Timer.scheduledTimer(withTimeInterval: TimeInterval(timeInterval), repeats: true) { [weak self, weak player] timer in
|
432
|
+
guard let strongPlayer = player, let strongSelf = self else {
|
433
|
+
timer.invalidate()
|
434
|
+
return
|
435
|
+
}
|
436
|
+
|
437
|
+
currentStep += 1
|
438
|
+
let progress = Float(currentStep) / Float(totalSteps)
|
439
|
+
let newVolume = startVolume + progress * (endVolume - startVolume)
|
440
|
+
|
441
|
+
strongPlayer.volume = newVolume
|
442
|
+
|
443
|
+
if currentStep >= totalSteps {
|
444
|
+
strongPlayer.volume = endVolume
|
445
|
+
timer.invalidate()
|
402
446
|
|
403
|
-
|
447
|
+
// Update timer reference on main thread
|
448
|
+
DispatchQueue.main.async {
|
449
|
+
strongSelf.fadeTimer = nil
|
450
|
+
}
|
451
|
+
}
|
452
|
+
}
|
404
453
|
|
405
|
-
if
|
406
|
-
|
407
|
-
timer.invalidate()
|
408
|
-
self.fadeTimer = nil
|
454
|
+
if let timer = self.fadeTimer {
|
455
|
+
RunLoop.current.add(timer, forMode: .common)
|
409
456
|
}
|
410
457
|
}
|
411
|
-
RunLoop.current.add(fadeTimer!, forMode: .common)
|
412
458
|
}
|
413
459
|
}
|