-
Notifications
You must be signed in to change notification settings - Fork 88
/
Copy pathSFBAudioPlayer.h
363 lines (316 loc) · 21.6 KB
/
SFBAudioPlayer.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
//
// Copyright (c) 2006-2025 Stephen F. Booth <[email protected]>
// Part of https://github.com/sbooth/SFBAudioEngine
// MIT license
//
#import <os/log.h>
#import <Foundation/Foundation.h>
#import <AVFAudio/AVFAudio.h>
#if !TARGET_OS_IPHONE
#import <CoreAudio/CoreAudio.h>
#endif /* !TARGET_OS_IPHONE */
#import <SFBAudioEngine/SFBAudioPlayerNode.h>
NS_ASSUME_NONNULL_BEGIN
@protocol SFBAudioPlayerDelegate;
/// Playback position information for `SFBAudioPlayer`
typedef SFBAudioPlayerNodePlaybackPosition SFBAudioPlayerPlaybackPosition NS_REFINED_FOR_SWIFT;
/// Playback time information for `SFBAudioPlayer`
typedef SFBAudioPlayerNodePlaybackTime SFBAudioPlayerPlaybackTime NS_REFINED_FOR_SWIFT;
/// A block accepting a single `AVAudioEngine` parameter
typedef void (^SFBAudioPlayerAVAudioEngineBlock)(AVAudioEngine *engine) NS_SWIFT_NAME(AudioPlayer.AVAudioEngineClosure);
/// The possible playback states for `SFBAudioPlayer`
typedef NS_ENUM(NSUInteger, SFBAudioPlayerPlaybackState) {
/// `SFBAudioPlayer.engineIsRunning` and `SFBAudioPlayer.playerNodeIsPlaying`
SFBAudioPlayerPlaybackStatePlaying = 0,
/// `SFBAudioPlayer.engineIsRunning` and `!SFBAudioPlayer.playerNodeIsPlaying`
SFBAudioPlayerPlaybackStatePaused = 1,
/// `!SFBAudioPlayer.engineIsRunning`
SFBAudioPlayerPlaybackStateStopped = 2,
} NS_SWIFT_NAME(AudioPlayer.PlaybackState);
/// An audio player wrapping an `AVAudioEngine` processing graph supplied by `SFBAudioPlayerNode`
///
/// `SFBAudioPlayer` supports gapless playback for audio with the same sample rate and number of channels.
/// For audio with different sample rates or channels, the audio processing graph is automatically reconfigured.
///
/// An `SFBAudioPlayer` may be in one of three playback states: playing, paused, or stopped. These states are
/// based on whether the underlying `AVAudioEngine` is running (`SFBAudioPlayer.engineIsRunning`)
/// and the `SFBAudioPlayerNode` is playing (`SFBAudioPlayer.playerNodeIsPlaying`).
///
/// `SFBAudioPlayer` supports delegate-based callbacks for the following events:
///
/// 1. Decoding started
/// 2. Decoding complete
/// 3. Rendering will start
/// 4. Rendering started
/// 5. Rendering will complete
/// 6. Rendering complete
/// 7. Now playing changed
/// 8. Playback state changed
/// 9. End of audio
/// 10. Decoder canceled
/// 11. Processing graph format change with custom nodes present
/// 12. `AVAudioEngineConfigurationChange` notification received
/// 13. Asynchronous error encountered
///
/// The dispatch queue on which callbacks are performed is not specified.
NS_SWIFT_NAME(AudioPlayer) @interface SFBAudioPlayer : NSObject <SFBAudioPlayerNodeDelegate>
#pragma mark - Playlist Management
/// Cancels the current decoder, clears any queued decoders, creates and enqueues a decoder, and starts playback
/// - note: This is equivalent to `-enqueueURL:forImmediatePlayback:error:` with `YES` for `forImmediatePlayback` followed by `-playReturningError:`
/// - parameter url: The URL to play
/// - parameter error: An optional pointer to an `NSError` object to receive error information
/// - returns: `YES` if a decoder was created and enqueued and playback started successfully
- (BOOL)playURL:(NSURL *)url error:(NSError **)error NS_SWIFT_NAME(play(_:));
/// Cancels the current decoder, clears any queued decoders, enqueues a decoder, and starts playback
/// - note: This is equivalent to `-enqueueDecoder:forImmediatePlayback:error:` with `YES` for `forImmediatePlayback` followed by `-playReturningError:`
/// - parameter decoder: The decoder to play
/// - parameter error: An optional pointer to an `NSError` object to receive error information
/// - returns: `YES` if the decoder was enqueued and playback started successfully
- (BOOL)playDecoder:(id <SFBPCMDecoding>)decoder error:(NSError **)error NS_SWIFT_NAME(play(_:));
/// Creates and enqueues a decoder for subsequent playback
/// - note: This is equivalent to `-enqueueURL:forImmediatePlayback:error:` with `NO` for `forImmediatePlayback`
/// - parameter url: The URL to enqueue
/// - parameter error: An optional pointer to an `NSError` object to receive error information
/// - returns: `YES` if a decoder was created and enqueued successfully
- (BOOL)enqueueURL:(NSURL *)url error:(NSError **)error NS_SWIFT_NAME(enqueue(_:));
/// Creates and enqueues a decoder for subsequent playback, optionally canceling the current decoder and clearing any queued decoders
/// - note: This is equivalent to creating an `SFBAudioDecoder` object for `url` and passing that object to `-enqueueDecoder:forImmediatePlayback:error:`
/// - parameter url: The URL to enqueue
/// - parameter forImmediatePlayback: If `YES` the current decoder is canceled and any queued decoders are cleared before enqueuing
/// - parameter error: An optional pointer to an `NSError` object to receive error information
/// - returns: `YES` if a decoder was created and enqueued successfully
- (BOOL)enqueueURL:(NSURL *)url forImmediatePlayback:(BOOL)forImmediatePlayback error:(NSError **)error NS_SWIFT_NAME(enqueue(_:immediate:));
/// Enqueues a decoder for subsequent playback
/// - note: This is equivalent to `-enqueueDecoder:forImmediatePlayback:error:` with `NO` for `forImmediatePlayback`
/// - parameter decoder: The decoder to enqueue
/// - parameter error: An optional pointer to an `NSError` object to receive error information
/// - returns: `YES` if the decoder was enqueued successfully
- (BOOL)enqueueDecoder:(id <SFBPCMDecoding>)decoder error:(NSError **)error NS_SWIFT_NAME(enqueue(_:));
/// Enqueues a decoder for subsequent playback, optionally canceling the current decoder and clearing any queued decoders
/// - note: If `forImmediatePlayback` is `YES`, the audio processing graph is reconfigured for `decoder.processingFormat` if necessary
/// - parameter decoder: The decoder to enqueue
/// - parameter forImmediatePlayback: If `YES` the current decoder is canceled and any queued decoders are cleared before enqueuing
/// - parameter error: An optional pointer to an `NSError` object to receive error information
/// - returns: `YES` if the decoder was enqueued successfully
- (BOOL)enqueueDecoder:(id <SFBPCMDecoding>)decoder forImmediatePlayback:(BOOL)forImmediatePlayback error:(NSError **)error NS_SWIFT_NAME(enqueue(_:immediate:));
/// Returns `YES` if audio with `format` will be played gaplessly
- (BOOL)formatWillBeGaplessIfEnqueued:(AVAudioFormat *)format;
/// Empties the decoder queue
- (void)clearQueue;
/// Returns `YES` if the decoder queue is empty
@property (nonatomic, readonly) BOOL queueIsEmpty;
#pragma mark - Playback Control
/// Starts the underlying `AVAudioEngine` and plays the `SFBAudioPlayerNode`
/// - note: If the current `playbackState` is `SFBAudioPlayerPlaybackStatePlaying` this method has no effect
/// - parameter error: An optional pointer to an `NSError` object to receive error information
/// - returns: `YES` if the underlying `AVAudioEngine` was successfully started
- (BOOL)playReturningError:(NSError **)error NS_SWIFT_NAME(play());
/// Pauses the `SFBAudioPlayerNode`
/// - note: If the current `playbackState` is not `SFBAudioPlayerPlaybackStatePlaying` this method has no effect
- (void)pause;
/// Plays the `SFBAudioPlayerNode`
/// - note: If the current `playbackState` is not `SFBAudioPlayerPlaybackStatePaused` this method has no effect
- (void)resume;
/// Stops both the underlying `AVAudioEngine` and `SFBAudioPlayerNode`
/// - note: This method cancels the current decoder and clears any queued decoders
/// - note: If the current `playbackState` is `SFBAudioPlayerPlaybackStateStopped` this method has no effect
- (void)stop;
/// Toggles the player between playing and paused states, starting playback if stopped
///
/// If the current `playbackState` is `SFBAudioPlayerPlaybackStateStopped` this method sends `-playReturningError:`
/// If the current `playbackState` is `SFBAudioPlayerPlaybackStatePlaying` this method sends `-pause`
/// If the current `playbackState` is `SFBAudioPlayerPlaybackStatePaused` this method sends `-resume`
- (BOOL)togglePlayPauseReturningError:(NSError **)error NS_SWIFT_NAME(togglePlayPause());
/// Resets both the underlying `AVAudioEngine` and `SFBAudioPlayerNode`
/// - note: This method cancels the current decoder and clears any queued decoders
- (void)reset;
#pragma mark - Player State
/// Returns `YES` if the `AVAudioEngine` is running
@property (nonatomic, readonly) BOOL engineIsRunning;
/// Returns `YES` if the `SFBAudioPlayerNode` is playing
@property (nonatomic, readonly) BOOL playerNodeIsPlaying;
/// Returns the current playback state
@property (nonatomic, readonly) SFBAudioPlayerPlaybackState playbackState;
/// Returns `YES` if `engineIsRunning` and `playerNodeIsPlaying`
@property (nonatomic, readonly) BOOL isPlaying;
/// Returns `YES` if `engineIsRunning` and `!playerNodeIsPlaying`
@property (nonatomic, readonly) BOOL isPaused;
/// Returns `NO` if `engineIsRunning`
@property (nonatomic, readonly) BOOL isStopped;
/// Returns `YES` if a decoder is available to supply audio for the next render cycle
@property (nonatomic, readonly) BOOL isReady;
/// Returns the decoder supplying the earliest audio frame for the next render cycle or `nil` if none
/// - warning: Do not change any properties of the returned object
@property (nonatomic, nullable, readonly) id <SFBPCMDecoding> currentDecoder;
/// Returns the decoder approximating what a user would expect to see as the "now playing" item- the decoder that is
/// currently rendering audio.
/// - warning: Do not change any properties of the returned object
@property (nonatomic, nullable, readonly) id <SFBPCMDecoding> nowPlaying;
#pragma mark - Playback Properties
/// Returns the frame position in the current decoder or `SFBUnknownFramePosition` if the current decoder is `nil`
@property (nonatomic, readonly) AVAudioFramePosition framePosition NS_REFINED_FOR_SWIFT;
/// Returns the frame length of the current decoder or `SFBUnknownFrameLength` if the current decoder is `nil`
@property (nonatomic, readonly) AVAudioFramePosition frameLength NS_REFINED_FOR_SWIFT;
/// Returns the playback position in the current decoder or `{SFBUnknownFramePosition, SFBUnknownFrameLength}` if the current decoder is `nil`
@property (nonatomic, readonly) SFBAudioPlayerPlaybackPosition playbackPosition NS_REFINED_FOR_SWIFT;
/// Returns the current time in the current decoder or `SFBUnknownTime` if the current decoder is `nil`
@property (nonatomic, readonly) NSTimeInterval currentTime NS_REFINED_FOR_SWIFT;
/// Returns the total time of the current decoder or `SFBUnknownTime` if the current decoder is `nil`
@property (nonatomic, readonly) NSTimeInterval totalTime NS_REFINED_FOR_SWIFT;
/// Returns the playback time in the current decoder or `{SFBUnknownTime, SFBUnknownTime}` if the current decoder is `nil`
@property (nonatomic, readonly) SFBAudioPlayerPlaybackTime playbackTime NS_REFINED_FOR_SWIFT;
/// Retrieves the playback position and time
/// - parameter playbackPosition: An optional pointer to an `SFBAudioPlayerPlaybackPosition` struct to receive playback position information
/// - parameter playbackTime: An optional pointer to an `SFBAudioPlayerPlaybackTime` struct to receive playback time information
/// - returns: `NO` if the current decoder is `nil`
- (BOOL)getPlaybackPosition:(nullable SFBAudioPlayerPlaybackPosition *)playbackPosition andTime:(nullable SFBAudioPlayerPlaybackTime *)playbackTime NS_REFINED_FOR_SWIFT;
#pragma mark - Seeking
/// Seeks forward in the current decoder by 3 seconds
/// - returns: `NO` if the current decoder is `nil`
- (BOOL)seekForward;
/// Seeks backward in the current decoder by 3 seconds
/// - returns: `NO` if the current decoder is `nil`
- (BOOL)seekBackward;
/// Seeks forward in the current decoder by the specified number of seconds
/// - parameter secondsToSkip: The number of seconds to skip forward
/// - returns: `NO` if the current decoder is `nil`
- (BOOL)seekForward:(NSTimeInterval)secondsToSkip NS_SWIFT_NAME(seek(forward:));
/// Seeks backward in the current decoder by the specified number of seconds
/// - parameter secondsToSkip: The number of seconds to skip backward
/// - returns: `NO` if the current decoder is `nil`
- (BOOL)seekBackward:(NSTimeInterval)secondsToSkip NS_SWIFT_NAME(seek(backward:));
/// Seeks to the specified time in the current decoder
/// - parameter timeInSeconds: The desired time in seconds
/// - returns: `NO` if the current decoder is `nil`
- (BOOL)seekToTime:(NSTimeInterval)timeInSeconds NS_SWIFT_NAME(seek(time:));
/// Seeks to the specified positioni n the current decoder
/// - parameter position: The desired position in the interval `[0, 1)`
/// - returns: `NO` if the current decoder is `nil`
- (BOOL)seekToPosition:(double)position NS_SWIFT_NAME(seek(position:));
/// Seeks to the specified audio frame in the current decoder
/// - parameter frame: The desired audio frame
/// - returns: `NO` if the current decoder is `nil`
- (BOOL)seekToFrame:(AVAudioFramePosition)frame NS_SWIFT_NAME(seek(frame:));
/// Returns `YES` if the current decoder supports seeking
@property (nonatomic, readonly) BOOL supportsSeeking;
#if !TARGET_OS_IPHONE
#pragma mark - Volume Control
/// Returns `kHALOutputParam_Volume` on channel `0` for `AVAudioEngine.outputNode.audioUnit` or `NaN` on error
@property (nonatomic, readonly) float volume;
/// Sets `kHALOutputParam_Volume` on channel `0` for `AVAudioEngine.outputNode.audioUnit`
/// - parameter volume: The desired volume
/// - parameter error: An optional pointer to an `NSError` object to receive error information
/// - returns: `YES` if the volume was successfully set
- (BOOL)setVolume:(float)volume error:(NSError **)error;
/// Returns `kHALOutputParam_Volume` on `channel` for `AVAudioEngine.outputNode.audioUnit` or `NaN` on error
- (float)volumeForChannel:(AudioObjectPropertyElement)channel;
/// Sets `kHALOutputParam_Volume` on `channel` for `AVAudioEngine.outputNode.audioUnit`
/// - parameter volume: The desired volume
/// - parameter channel: The channel to adjust
/// - parameter error: An optional pointer to an `NSError` object to receive error information
/// - returns: `YES` if the volume was successfully set
- (BOOL)setVolume:(float)volume forChannel:(AudioObjectPropertyElement)channel error:(NSError **)error;
#pragma mark - Output Device
/// Returns the output device object ID for `AVAudioEngine.outputNode`
@property (nonatomic, readonly) AUAudioObjectID outputDeviceID;
/// Sets the output device for `AVAudioEngine.outputNode`
/// - parameter outputDeviceID: The audio object ID of the desired output device
/// - parameter error: An optional pointer to an `NSError` object to receive error information
/// - returns: `YES` if the output device was successfully set
- (BOOL)setOutputDeviceID:(AUAudioObjectID)outputDeviceID error:(NSError **)error;
#endif /* !TARGET_OS_IPHONE */
#pragma mark - Delegate
/// An optional delegate
@property (nonatomic, nullable, weak) id<SFBAudioPlayerDelegate> delegate;
#pragma mark - AVAudioEngine Access
/// Peforms an operation on the underlying `AVAudioEngine`
/// - important: Graph modifications may only be made between `playerNode` and `engine.mainMixerNode`
/// - parameter block: A block performing operations on the underlying `AVAudioEngine`
- (void)withEngine:(SFBAudioPlayerAVAudioEngineBlock)block;
/// Returns the `SFBAudioPlayerNode` that is the source of the audio processing graph
@property (nonatomic, nonnull, readonly) SFBAudioPlayerNode *playerNode;
#pragma mark - Debugging
/// Asynchronously logs a description of the player's audio processing graph
/// - parameter log: An `os_log_t` object to receive the message
/// - parameter type: The type of log message
-(void)logProcessingGraphDescription:(os_log_t)log type:(os_log_type_t)type;
@end
#pragma mark - SFBAudioPlayerDelegate
/// Delegate methods supported by `SFBAudioPlayer`
NS_SWIFT_NAME(AudioPlayer.Delegate) @protocol SFBAudioPlayerDelegate <NSObject>
@optional
/// Called to notify the delegate before decoding the first frame of audio from a decoder
/// - warning: Do not change any properties of `decoder`
/// - parameter audioPlayer: The `SFBAudioPlayer` object processing `decoder`
/// - parameter decoder: The decoder for which decoding started
- (void)audioPlayer:(SFBAudioPlayer *)audioPlayer decodingStarted:(id<SFBPCMDecoding>)decoder;
/// Called to notify the delegate after decoding the final frame of audio from a decoder
/// - warning: Do not change any properties of `decoder`
/// - parameter audioPlayer: The `SFBAudioPlayer` object processing `decoder`
/// - parameter decoder: The decoder for which decoding is complete
- (void)audioPlayer:(SFBAudioPlayer *)audioPlayer decodingComplete:(id<SFBPCMDecoding>)decoder;
/// Called to notify the delegate that the first audio frame from a decoder will render
/// - warning: Do not change any properties of `decoder`
/// - parameter audioPlayer: The `SFBAudioPlayer` object processing `decoder`
/// - parameter decoder: The decoder for which rendering will start
/// - parameter hostTime: The host time at which the first audio frame from `decoder` will reach the device
- (void)audioPlayer:(SFBAudioPlayer *)audioPlayer renderingWillStart:(id<SFBPCMDecoding>)decoder atHostTime:(uint64_t)hostTime NS_SWIFT_NAME(audioPlayer(_:renderingWillStart:at:));
/// Called to notify the delegate when rendering the first frame of audio from a decoder
/// - warning: Do not change any properties of `decoder`
/// - parameter audioPlayer: The `SFBAudioPlayer` object processing decoder
/// - parameter decoder: The decoder for which rendering started
- (void)audioPlayer:(SFBAudioPlayer *)audioPlayer renderingStarted:(id<SFBPCMDecoding>)decoder;
/// Called to notify the delegate that the final audio frame from a decoder will render
/// - warning: Do not change any properties of `decoder`
/// - parameter audioPlayer: The `SFBAudioPlayer` object processing `decoder`
/// - parameter decoder: The decoder for which rendering will complete
/// - parameter hostTime: The host time at which the final audio frame from `decoder` will finish playing on the device
- (void)audioPlayer:(SFBAudioPlayer *)audioPlayer renderingWillComplete:(id<SFBPCMDecoding>)decoder atHostTime:(uint64_t)hostTime NS_SWIFT_NAME(audioPlayer(_:renderingWillComplete:at:));
/// Called to notify the delegate when rendering the final frame of audio from a decoder
/// - warning: Do not change any properties of `decoder`
/// - parameter audioPlayer: The `SFBAudioPlayer` object processing `decoder`
/// - parameter decoder: The decoder for which rendering is complete
- (void)audioPlayer:(SFBAudioPlayer *)audioPlayer renderingComplete:(id<SFBPCMDecoding>)decoder;
/// Called to notify the delegate when the now playing item changes
/// - warning: Do not change any properties of `nowPlaying`
/// - parameter audioPlayer: The `SFBAudioPlayer` object
/// - parameter nowPlaying: The decoder that is now playing
- (void)audioPlayer:(SFBAudioPlayer *)audioPlayer nowPlayingChanged:(nullable id <SFBPCMDecoding>)nowPlaying;
/// Called to notify the delegate when the playback state changes
/// - parameter audioPlayer: The `SFBAudioPlayer` object
/// - parameter playbackState: The current playback state
- (void)audioPlayer:(SFBAudioPlayer *)audioPlayer playbackStateChanged:(SFBAudioPlayerPlaybackState)playbackState;
/// Called to notify the delegate when rendering is complete for all available decoders
/// - parameter audioPlayer: The `SFBAudioPlayer` object
- (void)audioPlayerEndOfAudio:(SFBAudioPlayer *)audioPlayer NS_SWIFT_NAME(audioPlayerEndOfAudio(_:));
/// Called to notify the delegate that the decoding and rendering process for a decoder has been canceled
/// - warning: Do not change any properties of `decoder`
/// - parameter audioPlayer: The `SFBAudioPlayer` object processing `decoder`
/// - parameter decoder: The decoder for which decoding and rendering is canceled
/// - parameter framesRendered: The number of audio frames from `decoder` that were rendered
- (void)audioPlayer:(SFBAudioPlayer *)audioPlayer decoderCanceled:(id<SFBPCMDecoding>)decoder framesRendered:(AVAudioFramePosition)framesRendered;
/// Called to notify the delegate when additional changes to the `AVAudioEngine` processing graph may need to be made in response to a format change
///
/// Before this method is called the main mixer node will be connected to the output node, and the player node will be attached
/// to the processing graph with no connections.
///
/// The delegate should establish or update any connections in the processing graph segment between the node to be returned and the main mixer node.
///
/// After this method returns the player node will be connected to the returned node using the specified format.
/// - important: This method is called from a context where it is safe to modify `engine`
/// - note: This method is only called when one or more nodes have been inserted between the player node and main mixer node.
/// - parameter audioPlayer: The `SFBAudioPlayer` object
/// - parameter engine: The `AVAudioEngine` object
/// - parameter format: The rendering format of the player node
/// - returns: The `AVAudioNode` to which the player node should be connected
- (AVAudioNode *)audioPlayer:(SFBAudioPlayer *)audioPlayer reconfigureProcessingGraph:(AVAudioEngine *)engine withFormat:(AVAudioFormat *)format NS_SWIFT_NAME(audioPlayer(_:reconfigureProcessingGraph:with:));
/// Called to notify the delegate when the configuration of the underlying `AVAudioEngine` changes
/// - note: Use this instead of listening for `AVAudioEngineConfigurationChangeNotification`
/// - parameter audioPlayer: The `SFBAudioPlayer` object
- (void)audioPlayerAVAudioEngineConfigurationChange:(SFBAudioPlayer *)audioPlayer NS_SWIFT_NAME(audioPlayerAVAudioEngineConfigurationChange(_:));
/// Called to notify the delegate when an asynchronous error occurs
/// - parameter audioPlayer: The `SFBAudioPlayer` object
/// - parameter error: The error
- (void)audioPlayer:(SFBAudioPlayer *)audioPlayer encounteredError:(NSError *)error;
@end
NS_ASSUME_NONNULL_END