Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f0e894f86 | |||
| decb12641d | |||
| 4e485f924a |
@@ -73,14 +73,8 @@ final class FileAudioSource: NSObject, CoreAudioStreamSource {
|
||||
}
|
||||
|
||||
private func performOpen(seek seekOffset: Int) throws {
|
||||
|
||||
var reopened = false
|
||||
let streamStatus = inputStream?.streamStatus ?? .closed
|
||||
if streamStatus == .notOpen || streamStatus == .closed || streamStatus == .error || streamStatus == .atEnd {
|
||||
reopened = true
|
||||
close()
|
||||
try open()
|
||||
}
|
||||
close()
|
||||
try open()
|
||||
|
||||
guard let inputStream = inputStream else {
|
||||
return
|
||||
@@ -91,11 +85,6 @@ final class FileAudioSource: NSObject, CoreAudioStreamSource {
|
||||
} else {
|
||||
position = 0
|
||||
}
|
||||
if !reopened {
|
||||
if inputStream.hasBytesAvailable {
|
||||
dataAvailable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func dataAvailable() {
|
||||
|
||||
@@ -142,23 +142,28 @@ open class AudioPlayer {
|
||||
|
||||
serializationQueue = DispatchQueue(label: "streaming.core.queue", qos: .userInitiated)
|
||||
sourceQueue = DispatchQueue(label: "source.queue", qos: .default)
|
||||
|
||||
entryProvider = AudioEntryProvider(networkingClient: NetworkingClient(),
|
||||
underlyingQueue: sourceQueue,
|
||||
outputAudioFormat: outputAudioFormat)
|
||||
|
||||
fileStreamProcessor = AudioFileStreamProcessor(playerContext: playerContext,
|
||||
rendererContext: rendererContext,
|
||||
outputAudioFormat: outputAudioFormat.basicStreamDescription)
|
||||
|
||||
playerRenderProcessor = AudioPlayerRenderProcessor(playerContext: playerContext,
|
||||
rendererContext: rendererContext,
|
||||
outputAudioFormat: outputAudioFormat.basicStreamDescription)
|
||||
|
||||
|
||||
frameFilterProcessor = FrameFilterProcessor(mixerNodeProvider: {
|
||||
engine.mainMixerNode
|
||||
})
|
||||
entryProvider = AudioEntryProvider(
|
||||
networkingClient: NetworkingClient(),
|
||||
underlyingQueue: sourceQueue,
|
||||
outputAudioFormat: outputAudioFormat
|
||||
)
|
||||
|
||||
fileStreamProcessor = AudioFileStreamProcessor(
|
||||
playerContext: playerContext,
|
||||
rendererContext: rendererContext,
|
||||
outputAudioFormat: outputAudioFormat.basicStreamDescription)
|
||||
|
||||
playerRenderProcessor = AudioPlayerRenderProcessor(
|
||||
playerContext: playerContext,
|
||||
rendererContext: rendererContext,
|
||||
outputAudioFormat: outputAudioFormat.basicStreamDescription)
|
||||
|
||||
frameFilterProcessor = FrameFilterProcessor(
|
||||
mixerNodeProvider: {
|
||||
engine.mainMixerNode
|
||||
}
|
||||
)
|
||||
configPlayerContext()
|
||||
configPlayerNode()
|
||||
setupEngine()
|
||||
@@ -509,6 +514,7 @@ open class AudioPlayer {
|
||||
/// Pauses the audio engine and stops the player's hardware
|
||||
private func pauseEngine() {
|
||||
guard isEngineRunning else { return }
|
||||
audioEngine.reset()
|
||||
audioEngine.pause()
|
||||
player.auAudioUnit.stopHardware()
|
||||
Logger.debug("engine paused ⏸", category: .generic)
|
||||
@@ -653,24 +659,6 @@ open class AudioPlayer {
|
||||
|
||||
let isPlayingSameItemProbablySeek = playerContext.audioPlayingEntry === nextEntry
|
||||
|
||||
let notifyDelegateEntryFinishedPlaying: (AudioEntry?, Bool) -> Void = { [weak self] entry, _ in
|
||||
guard let self = self else { return }
|
||||
if let entry = entry, !isPlayingSameItemProbablySeek {
|
||||
let entryId = entry.id
|
||||
let progressInFrames = entry.progressInFrames()
|
||||
let progress = Double(progressInFrames) / self.outputAudioFormat.basicStreamDescription.mSampleRate
|
||||
let duration = entry.duration()
|
||||
|
||||
asyncOnMain {
|
||||
self.delegate?.audioPlayerDidFinishPlaying(player: self,
|
||||
entryId: entryId,
|
||||
stopReason: self.stopReason,
|
||||
progress: progress,
|
||||
duration: duration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let nextEntry = nextEntry {
|
||||
if !isPlayingSameItemProbablySeek {
|
||||
nextEntry.lock.withLock {
|
||||
@@ -685,7 +673,22 @@ open class AudioPlayer {
|
||||
let playingQueueEntryId = playerContext.audioPlayingEntry?.id ?? AudioEntryId(id: "")
|
||||
playerContext.entriesLock.unlock()
|
||||
|
||||
notifyDelegateEntryFinishedPlaying(entry, isPlayingSameItemProbablySeek)
|
||||
if let entry = entry, !isPlayingSameItemProbablySeek {
|
||||
let entryId = entry.id
|
||||
let progressInFrames = entry.progressInFrames()
|
||||
let progress = Double(progressInFrames) / self.outputAudioFormat.basicStreamDescription.mSampleRate
|
||||
let duration = entry.duration()
|
||||
asyncOnMain { [weak self] in
|
||||
guard let self else { return }
|
||||
self.delegate?.audioPlayerDidFinishPlaying(
|
||||
player: self,
|
||||
entryId: entryId,
|
||||
stopReason: self.stopReason,
|
||||
progress: progress,
|
||||
duration: duration
|
||||
)
|
||||
}
|
||||
}
|
||||
if !isPlayingSameItemProbablySeek {
|
||||
playerContext.setInternalState(to: .waitingForData)
|
||||
|
||||
@@ -695,10 +698,29 @@ open class AudioPlayer {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
notifyDelegateEntryFinishedPlaying(entry, isPlayingSameItemProbablySeek)
|
||||
playerContext.entriesLock.lock()
|
||||
playerContext.audioPlayingEntry = nil
|
||||
playerContext.entriesLock.unlock()
|
||||
if let entry = entry, !isPlayingSameItemProbablySeek {
|
||||
let entryId = entry.id
|
||||
let progressInFrames = entry.progressInFrames()
|
||||
let progress = Double(progressInFrames) / self.outputAudioFormat.basicStreamDescription.mSampleRate
|
||||
let duration = entry.duration()
|
||||
|
||||
sourceQueue.async { [weak self] in
|
||||
guard let self else { return }
|
||||
self.processSource()
|
||||
asyncOnMain {
|
||||
self.delegate?.audioPlayerDidFinishPlaying(
|
||||
player: self,
|
||||
entryId: entryId,
|
||||
stopReason: self.stopReason,
|
||||
progress: progress,
|
||||
duration: duration
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceQueue.async { [weak self] in
|
||||
self?.processSource()
|
||||
|
||||
@@ -42,7 +42,9 @@ internal final class AudioPlayerContext {
|
||||
when inState: ((AudioPlayer.InternalState) -> Bool)? = nil)
|
||||
{
|
||||
let newValues = playerStateAndStopReason(for: state)
|
||||
stopReason.write { $0 = newValues.stopReason }
|
||||
if let stopReason = newValues.stopReason {
|
||||
self.stopReason.write { $0 = stopReason }
|
||||
}
|
||||
guard state != internalState else { return }
|
||||
if let inState = inState, !inState(internalState) {
|
||||
return
|
||||
|
||||
@@ -30,26 +30,27 @@ extension AudioPlayer {
|
||||
/// Helper method that returns `AudioPlayerState` and `StopReason` based on the given `InternalState`
|
||||
/// - Parameter internalState: A value of `InternalState`
|
||||
/// - Returns: A tuple of `(AudioPlayerState, AudioPlayerStopReason)`
|
||||
func playerStateAndStopReason(for internalState: AudioPlayer.InternalState) -> (state: AudioPlayerState,
|
||||
stopReason: AudioPlayerStopReason)
|
||||
func playerStateAndStopReason(
|
||||
for internalState: AudioPlayer.InternalState
|
||||
) -> (state: AudioPlayerState, stopReason: AudioPlayerStopReason?)
|
||||
{
|
||||
switch internalState {
|
||||
case .initial:
|
||||
return (.ready, .none)
|
||||
return (.ready, AudioPlayerStopReason.none)
|
||||
case .running, .playing, .waitingForDataAfterSeek:
|
||||
return (.playing, .none)
|
||||
return (.playing, AudioPlayerStopReason.none)
|
||||
case .pendingNext, .rebuffering, .waitingForData:
|
||||
return (.bufferring, .none)
|
||||
return (.bufferring, AudioPlayerStopReason.none)
|
||||
case .stopped:
|
||||
return (.stopped, .userAction)
|
||||
return (.stopped, nil)
|
||||
case .paused:
|
||||
return (.paused, .none)
|
||||
return (.paused, AudioPlayerStopReason.none)
|
||||
case .disposed:
|
||||
return (.disposed, .userAction)
|
||||
case .error:
|
||||
return (.error, .error)
|
||||
return (.error, AudioPlayerStopReason.error)
|
||||
default:
|
||||
return (.ready, .none)
|
||||
return (.ready, AudioPlayerStopReason.none)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user