From 460ba07e3098e1f2c652b8b511fe7a38614af927 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 15 Sep 2022 13:42:44 +0800 Subject: [PATCH 1/9] Update UI after seeking, dont try to detect end of stream using currentTime This helps with two issues: - after seeking sometimes you will see the loading spinner even after audio has resumed. - sometimes if we are still pulling data we might detect that we are at the end of the stream. --- .../Services/AudioSession/AudioController.swift | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift index 17fc4e9e4..6382da81c 100644 --- a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift @@ -212,7 +212,6 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate // within this index, but this is probably accurate enough for now. player?.removeAllItems() synthesizeFrom(start: foundIdx, playWhenReady: state == .playing, atOffset: remainder) - return } else { // There was no foundIdx, so we are probably trying to seek past the end, so // just seek to the last possible duration. @@ -221,6 +220,7 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate synthesizeFrom(start: durations.count - 1, playWhenReady: state == .playing, atOffset: last) } } + fireTimer() } @AppStorage(UserDefaultKey.textToSpeechPlaybackRate.rawValue) public var playbackRate = 1.0 { @@ -299,8 +299,7 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate if state == .reachedEnd { return false } - return - itemAudioProperties?.itemID == itemID && + return itemAudioProperties?.itemID == itemID && (state == .loading || player?.currentItem == nil || player?.currentItem?.status == .unknown) } @@ -431,7 +430,6 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate func startTimer() { if timer == nil { - // Update every 100ms timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(fireTimer), userInfo: nil, repeats: true) timer?.fire() } @@ -445,13 +443,6 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate stop() } - if player.items().count == 1, let currentTime = player.currentItem?.currentTime(), let duration = player.currentItem?.duration { - if currentTime >= duration { - pause() - state = .reachedEnd - } - } - if let durations = durations { duration = durations.reduce(0, +) durationString = formatTimeInterval(duration) From fcd52cf14930009e35cbe1ba7b6b1edd4da7c213 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 15 Sep 2022 14:05:32 +0800 Subject: [PATCH 2/9] Remove debug --- .../Sources/Services/AudioSession/SpeechSynthesizer.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/SpeechSynthesizer.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/SpeechSynthesizer.swift index 676c0d116..290f0b483 100644 --- a/apple/OmnivoreKit/Sources/Services/AudioSession/SpeechSynthesizer.swift +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/SpeechSynthesizer.swift @@ -187,7 +187,6 @@ func fetchUtterance(appEnvironment: AppEnvironment, if let ssml = try utterance.toSSML(document: document) { request.httpBody = ssml - print("FETCHING: ", String(decoding: ssml, as: UTF8.self)) } for (header, value) in networker.defaultHeaders { @@ -219,7 +218,6 @@ func fetchUtterance(appEnvironment: AppEnvironment, try audioData.write(to: tempPath) try? FileManager.default.removeItem(at: audioPath) try FileManager.default.moveItem(at: tempPath, to: audioPath) - print("wrote", audioData.count, "bytes to", audioPath) } catch { let errorMessage = "audioFetch failed. could not write MP3 data to disk" throw BasicError.message(messageText: errorMessage) From de73c8134541fde6beff845ca9bc457246a9d047 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 15 Sep 2022 14:07:37 +0800 Subject: [PATCH 3/9] Pause player before seeking into another player item. fireTimer if seeking within --- .../Sources/Services/AudioSession/AudioController.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift index 6382da81c..7382bab0e 100644 --- a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift @@ -204,12 +204,14 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate if let playerItem = player?.currentItem as? SpeechPlayerItem { if playerItem.speechItem.audioIdx == foundIdx { playerItem.seek(to: CMTimeMakeWithSeconds(remainder, preferredTimescale: 600), completionHandler: nil) + fireTimer() return } } - // Move the playback to the found index, we should also seek a bit - // within this index, but this is probably accurate enough for now. + // Move the playback to the found index, we also seek by the remainder amount + // before moving we pause the player so playback doesnt jump to a previous spot + player?.pause() player?.removeAllItems() synthesizeFrom(start: foundIdx, playWhenReady: state == .playing, atOffset: remainder) } else { From fa9780722f651097c21e67720e05d5e840ab11cd Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 15 Sep 2022 14:08:15 +0800 Subject: [PATCH 4/9] Only update nowPlaying data if the timeElapsed is accurately calculated --- .../Services/AudioSession/AudioController.swift | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift index 7382bab0e..18816431e 100644 --- a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift @@ -458,17 +458,19 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate let itemElapsed = playerItem.status == .readyToPlay ? CMTimeGetSeconds(playerItem.currentTime()) : 0 timeElapsed = durationBefore(playerIndex: playerItem.speechItem.audioIdx) + itemElapsed timeElapsedString = formatTimeInterval(timeElapsed) - } - if var nowPlaying = MPNowPlayingInfoCenter.default().nowPlayingInfo { - nowPlaying[MPMediaItemPropertyPlaybackDuration] = NSNumber(value: duration) - nowPlaying[MPNowPlayingInfoPropertyElapsedPlaybackTime] = NSNumber(value: timeElapsed) - MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlaying + + if var nowPlaying = MPNowPlayingInfoCenter.default().nowPlayingInfo { + nowPlaying[MPMediaItemPropertyPlaybackDuration] = NSNumber(value: duration) + nowPlaying[MPNowPlayingInfoPropertyElapsedPlaybackTime] = NSNumber(value: timeElapsed) + MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlaying + } } case .scrubStarted: break case let .scrubEnded(seekTime): scrubState = .reset timeElapsed = seekTime + timeElapsedString = formatTimeInterval(timeElapsed) } } From 9c3b9f0523848cf8e841050a7469022e09c4388f Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 15 Sep 2022 15:18:48 +0800 Subject: [PATCH 5/9] Dont update the scrub state from the timer This prevents the slider from jumping back during a seek operation. --- .../Sources/Services/AudioSession/AudioController.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift index 18816431e..1ea7c5a90 100644 --- a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift @@ -468,9 +468,13 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate case .scrubStarted: break case let .scrubEnded(seekTime): - scrubState = .reset timeElapsed = seekTime timeElapsedString = formatTimeInterval(timeElapsed) + if var nowPlaying = MPNowPlayingInfoCenter.default().nowPlayingInfo { + nowPlaying[MPMediaItemPropertyPlaybackDuration] = NSNumber(value: duration) + nowPlaying[MPNowPlayingInfoPropertyElapsedPlaybackTime] = NSNumber(value: timeElapsed) + MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlaying + } } } From e1f28ec18953592866461148381a179113b695c4 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 15 Sep 2022 15:46:20 +0800 Subject: [PATCH 6/9] Remove extra top padding on feedcards, this is applied by the HStack spacing --- apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift index 71223ff7a..551df5727 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift @@ -77,7 +77,7 @@ public struct FeedCard: View { .padding(.top, 8) } } - .padding(.top, 10) + .padding(.top, 0) .padding(.bottom, 8) .frame( minWidth: nil, From fd2df9f926a24b40fe5c73c57967cf2b20ada264 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 15 Sep 2022 15:54:21 +0800 Subject: [PATCH 7/9] Dont scroll the miniplayer when the user is scrubbing audio --- .../Sources/App/Views/AudioPlayer/MiniPlayer.swift | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift index 5081ed0d1..37bb01a2a 100644 --- a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift @@ -402,15 +402,24 @@ public struct MiniPlayer: View { } } + var scrubbing: Bool { + switch audioController.scrubState { + case .scrubStarted: + return true + default: + return false + } + } + func onDragChanged(value: DragGesture.Value) { - if value.translation.height > 0, expanded { + if value.translation.height > 0, expanded, !scrubbing { offset = value.translation.height } } func onDragEnded(value: DragGesture.Value) { withAnimation(.interactiveSpring()) { - if value.translation.height > minExpandedHeight { + if value.translation.height > minExpandedHeight, !scrubbing { expanded = false } offset = 0 From 1a338e8854c41a0bc28e6e8c3c51e5199d687a6b Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 15 Sep 2022 15:54:39 +0800 Subject: [PATCH 8/9] Reset the scrub state after seeking --- .../Sources/Services/AudioSession/AudioController.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift index 1ea7c5a90..269ab2096 100644 --- a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift @@ -204,6 +204,7 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate if let playerItem = player?.currentItem as? SpeechPlayerItem { if playerItem.speechItem.audioIdx == foundIdx { playerItem.seek(to: CMTimeMakeWithSeconds(remainder, preferredTimescale: 600), completionHandler: nil) + scrubState = .reset fireTimer() return } @@ -222,6 +223,8 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate synthesizeFrom(start: durations.count - 1, playWhenReady: state == .playing, atOffset: last) } } + + scrubState = .reset fireTimer() } From da7ec3f5d4a0bad25b145e41b93d54bf9a70c222 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 15 Sep 2022 17:23:28 +0800 Subject: [PATCH 9/9] Remove label top padding as these are spaced now --- apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift index 551df5727..21b509c10 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift @@ -74,7 +74,7 @@ public struct FeedCard: View { Spacer() } } - .padding(.top, 8) + .padding(.top, 0) } } .padding(.top, 0)