mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-29 20:56:57 +02:00
#4545 - Switch back to using multiple audio player instances, allow pausing when starting a new player.
This commit is contained in:
@@ -44,6 +44,9 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
|
||||
private let _voiceMessageToolbarView: VoiceMessageToolbarView
|
||||
private var displayLink: CADisplayLink!
|
||||
|
||||
private var audioRecorder: VoiceMessageAudioRecorder?
|
||||
|
||||
private var audioPlayer: VoiceMessageAudioPlayer?
|
||||
private var waveformAnalyser: WaveformAnalyzer?
|
||||
|
||||
private var audioSamples: [Float] = []
|
||||
@@ -53,7 +56,7 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
|
||||
@objc public weak var delegate: VoiceMessageControllerDelegate?
|
||||
|
||||
@objc public var isRecordingAudio: Bool {
|
||||
return mediaServiceProvider.audioRecorder.isRecording || isInLockedMode
|
||||
return audioRecorder?.isRecording ?? false || isInLockedMode
|
||||
}
|
||||
|
||||
@objc public var voiceMessageToolbarView: UIView {
|
||||
@@ -101,8 +104,9 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
|
||||
let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
|
||||
let temporaryFileURL = temporaryDirectoryURL.appendingPathComponent(ProcessInfo().globallyUniqueString).appendingPathExtension("m4a")
|
||||
|
||||
mediaServiceProvider.audioRecorder.registerDelegate(self)
|
||||
mediaServiceProvider.audioRecorder.recordWithOuputURL(temporaryFileURL)
|
||||
audioRecorder = mediaServiceProvider.audioRecorder()
|
||||
audioRecorder?.registerDelegate(self)
|
||||
audioRecorder?.recordWithOuputURL(temporaryFileURL)
|
||||
}
|
||||
|
||||
func voiceMessageToolbarViewDidRequestRecordingFinish(_ toolbarView: VoiceMessageToolbarView) {
|
||||
@@ -111,8 +115,8 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
|
||||
|
||||
func voiceMessageToolbarViewDidRequestRecordingCancel(_ toolbarView: VoiceMessageToolbarView) {
|
||||
isInLockedMode = false
|
||||
mediaServiceProvider.audioRecorder.stopRecording()
|
||||
deleteRecordingAtURL(mediaServiceProvider.audioRecorder.url)
|
||||
audioRecorder?.stopRecording()
|
||||
deleteRecordingAtURL(audioRecorder?.url)
|
||||
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
||||
updateUI()
|
||||
}
|
||||
@@ -123,21 +127,21 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
|
||||
}
|
||||
|
||||
func voiceMessageToolbarViewDidRequestPlaybackToggle(_ toolbarView: VoiceMessageToolbarView) {
|
||||
if mediaServiceProvider.audioPlayer.isPlaying {
|
||||
mediaServiceProvider.audioPlayer.pause()
|
||||
if audioPlayer?.isPlaying ?? false {
|
||||
audioPlayer?.pause()
|
||||
} else {
|
||||
mediaServiceProvider.audioPlayer.play()
|
||||
audioPlayer?.play()
|
||||
}
|
||||
}
|
||||
|
||||
func voiceMessageToolbarViewDidRequestSend(_ toolbarView: VoiceMessageToolbarView) {
|
||||
guard let url = mediaServiceProvider.audioRecorder.url else {
|
||||
guard let url = audioRecorder?.url else {
|
||||
MXLog.error("Invalid audio recording URL")
|
||||
return
|
||||
}
|
||||
|
||||
mediaServiceProvider.audioPlayer.stop()
|
||||
mediaServiceProvider.audioRecorder.stopRecording()
|
||||
audioPlayer?.stop()
|
||||
audioRecorder?.stopRecording()
|
||||
|
||||
sendRecordingAtURL(url)
|
||||
|
||||
@@ -191,23 +195,25 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
|
||||
// MARK: - Private
|
||||
|
||||
private func finishRecording() {
|
||||
let recordDuration = mediaServiceProvider.audioRecorder.currentTime
|
||||
mediaServiceProvider.audioRecorder.stopRecording()
|
||||
let recordDuration = audioRecorder?.currentTime
|
||||
audioRecorder?.stopRecording()
|
||||
|
||||
guard let url = mediaServiceProvider.audioRecorder.url else {
|
||||
guard let url = audioRecorder?.url else {
|
||||
MXLog.error("Invalid audio recording URL")
|
||||
return
|
||||
}
|
||||
|
||||
guard isInLockedMode else {
|
||||
if recordDuration >= Constants.minimumRecordingDuration {
|
||||
if recordDuration ?? 0 >= Constants.minimumRecordingDuration {
|
||||
sendRecordingAtURL(url)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
mediaServiceProvider.audioPlayer.registerDelegate(self)
|
||||
mediaServiceProvider.audioPlayer.loadContentFromURL(url)
|
||||
audioPlayer = mediaServiceProvider.audioPlayer()
|
||||
audioPlayer?.registerDelegate(self)
|
||||
audioPlayer?.loadContentFromURL(url)
|
||||
|
||||
audioSamples = []
|
||||
|
||||
updateUI()
|
||||
@@ -255,7 +261,7 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
|
||||
|
||||
private func updateUI() {
|
||||
|
||||
let shouldUpdateFromAudioPlayer = isInLockedMode && !mediaServiceProvider.audioRecorder.isRecording
|
||||
let shouldUpdateFromAudioPlayer = isInLockedMode && !(audioRecorder?.isRecording ?? false)
|
||||
|
||||
if shouldUpdateFromAudioPlayer {
|
||||
updateUIFromAudioPlayer()
|
||||
@@ -265,7 +271,7 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
|
||||
}
|
||||
|
||||
private func updateUIFromAudioRecorder() {
|
||||
let isRecording = mediaServiceProvider.audioRecorder.isRecording
|
||||
let isRecording = audioRecorder?.isRecording ?? false
|
||||
|
||||
displayLink.isPaused = !isRecording
|
||||
|
||||
@@ -275,11 +281,11 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
|
||||
padSamplesArrayToSize(requiredNumberOfSamples)
|
||||
}
|
||||
|
||||
let sample = mediaServiceProvider.audioRecorder.averagePowerForChannelNumber(0)
|
||||
let sample = audioRecorder?.averagePowerForChannelNumber(0) ?? 0.0
|
||||
audioSamples.insert(sample, at: 0)
|
||||
audioSamples.removeLast()
|
||||
|
||||
let currentTime = mediaServiceProvider.audioRecorder.currentTime
|
||||
let currentTime = audioRecorder?.currentTime ?? 0.0
|
||||
|
||||
if currentTime >= Constants.maximumAudioRecordingDuration {
|
||||
finishRecording()
|
||||
@@ -311,12 +317,16 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
|
||||
}
|
||||
|
||||
private func updateUIFromAudioPlayer() {
|
||||
guard let url = mediaServiceProvider.audioPlayer.url else {
|
||||
guard let audioPlayer = audioPlayer else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let url = audioPlayer.url else {
|
||||
MXLog.error("Invalid audio player url.")
|
||||
return
|
||||
}
|
||||
|
||||
displayLink.isPaused = !mediaServiceProvider.audioPlayer.isPlaying
|
||||
displayLink.isPaused = !audioPlayer.isPlaying
|
||||
|
||||
let requiredNumberOfSamples = _voiceMessageToolbarView.getRequiredNumberOfSamples()
|
||||
if audioSamples.count != requiredNumberOfSamples && requiredNumberOfSamples > 0 {
|
||||
@@ -337,11 +347,11 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate,
|
||||
}
|
||||
|
||||
var details = VoiceMessageToolbarViewDetails()
|
||||
details.state = (mediaServiceProvider.audioRecorder.isRecording ? (isInLockedMode ? .lockedModeRecord : .record) : (isInLockedMode ? .lockedModePlayback : .idle))
|
||||
details.elapsedTime = VoiceMessageController.timeFormatter.string(from: Date(timeIntervalSinceReferenceDate: (mediaServiceProvider.audioPlayer.isPlaying ? mediaServiceProvider.audioPlayer.currentTime : mediaServiceProvider.audioPlayer.duration)))
|
||||
details.state = (audioRecorder?.isRecording ?? false ? (isInLockedMode ? .lockedModeRecord : .record) : (isInLockedMode ? .lockedModePlayback : .idle))
|
||||
details.elapsedTime = VoiceMessageController.timeFormatter.string(from: Date(timeIntervalSinceReferenceDate: (audioPlayer.isPlaying ? audioPlayer.currentTime : audioPlayer.duration)))
|
||||
details.audioSamples = audioSamples
|
||||
details.isPlaying = mediaServiceProvider.audioPlayer.isPlaying
|
||||
details.progress = (mediaServiceProvider.audioPlayer.duration > 0.0 ? mediaServiceProvider.audioPlayer.currentTime / mediaServiceProvider.audioPlayer.duration : 0.0)
|
||||
details.isPlaying = audioPlayer.isPlaying
|
||||
details.progress = (audioPlayer.duration > 0.0 ? audioPlayer.currentTime / audioPlayer.duration : 0.0)
|
||||
_voiceMessageToolbarView.configureWithDetails(details)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,32 +18,63 @@ import Foundation
|
||||
|
||||
@objc public class VoiceMessageMediaServiceProvider: NSObject, VoiceMessageAudioPlayerDelegate, VoiceMessageAudioRecorderDelegate {
|
||||
|
||||
let audioPlayer = VoiceMessageAudioPlayer()
|
||||
var mediaIdentifier: String?
|
||||
let audioRecorder = VoiceMessageAudioRecorder()
|
||||
|
||||
private let audioPlayers: NSHashTable<VoiceMessageAudioPlayer>
|
||||
private let audioRecorders: NSHashTable<VoiceMessageAudioRecorder>
|
||||
|
||||
@objc public static let sharedProvider = VoiceMessageMediaServiceProvider()
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
audioPlayers = NSHashTable<VoiceMessageAudioPlayer>(options: .weakMemory)
|
||||
audioRecorders = NSHashTable<VoiceMessageAudioRecorder>(options: .weakMemory)
|
||||
}
|
||||
|
||||
@objc func audioPlayer() -> VoiceMessageAudioPlayer {
|
||||
let audioPlayer = VoiceMessageAudioPlayer()
|
||||
audioPlayer.registerDelegate(self)
|
||||
audioPlayers.add(audioPlayer)
|
||||
return audioPlayer
|
||||
}
|
||||
|
||||
@objc func audioRecorder() -> VoiceMessageAudioRecorder {
|
||||
let audioRecorder = VoiceMessageAudioRecorder()
|
||||
audioRecorder.registerDelegate(self)
|
||||
audioRecorders.add(audioRecorder)
|
||||
return audioRecorder
|
||||
}
|
||||
|
||||
@objc func stopAllServices() {
|
||||
audioPlayer.stop()
|
||||
audioRecorder.stopRecording()
|
||||
stopAllServicesExcept(nil)
|
||||
}
|
||||
|
||||
// MARK: - VoiceMessageAudioPlayerDelegate
|
||||
|
||||
func audioPlayerDidStartPlaying(_ audioPlayer: VoiceMessageAudioPlayer) {
|
||||
audioRecorder.stopRecording()
|
||||
stopAllServicesExcept(audioPlayer)
|
||||
}
|
||||
|
||||
// MARK: - VoiceMessageAudioRecorderDelegate
|
||||
|
||||
func audioRecorderDidStartRecording(_ audioRecorder: VoiceMessageAudioRecorder) {
|
||||
audioPlayer.stop()
|
||||
stopAllServicesExcept(audioRecorder)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func stopAllServicesExcept(_ service: AnyObject?) {
|
||||
for audioPlayer in audioPlayers.allObjects {
|
||||
if audioPlayer === service {
|
||||
continue
|
||||
}
|
||||
|
||||
audioPlayer.pause()
|
||||
}
|
||||
|
||||
for audioRecoder in audioRecorders.allObjects {
|
||||
if audioRecoder === service {
|
||||
continue
|
||||
}
|
||||
|
||||
audioRecoder.stopRecording()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,8 @@ class VoiceMessagePlaybackController: VoiceMessageAudioPlayerDelegate, VoiceMess
|
||||
}()
|
||||
|
||||
private let cacheManager: VoiceMessageAttachmentCacheManager
|
||||
private let mediaServiceProvider: VoiceMessageMediaServiceProvider
|
||||
|
||||
private let audioPlayer: VoiceMessageAudioPlayer
|
||||
private var displayLink: CADisplayLink!
|
||||
private var samples: [Float] = []
|
||||
private var duration: TimeInterval = 0
|
||||
@@ -46,9 +47,6 @@ class VoiceMessagePlaybackController: VoiceMessageAudioPlayerDelegate, VoiceMess
|
||||
|
||||
private var state: VoiceMessagePlaybackControllerState = .stopped {
|
||||
didSet {
|
||||
if state == .stopped || state == .error {
|
||||
mediaServiceProvider.audioPlayer.deregisterDelegate(self)
|
||||
}
|
||||
updateUI()
|
||||
displayLink.isPaused = (state != .playing)
|
||||
}
|
||||
@@ -59,10 +57,11 @@ class VoiceMessagePlaybackController: VoiceMessageAudioPlayerDelegate, VoiceMess
|
||||
init(mediaServiceProvider: VoiceMessageMediaServiceProvider,
|
||||
cacheManager: VoiceMessageAttachmentCacheManager) {
|
||||
self.cacheManager = cacheManager
|
||||
self.mediaServiceProvider = mediaServiceProvider
|
||||
|
||||
playbackView = VoiceMessagePlaybackView.loadFromNib()
|
||||
audioPlayer = mediaServiceProvider.audioPlayer()
|
||||
|
||||
audioPlayer.registerDelegate(self)
|
||||
playbackView.delegate = self
|
||||
|
||||
displayLink = CADisplayLink(target: WeakTarget(self, selector: #selector(handleDisplayLinkTick)), selector: WeakTarget.triggerSelector)
|
||||
@@ -83,29 +82,16 @@ class VoiceMessagePlaybackController: VoiceMessageAudioPlayerDelegate, VoiceMess
|
||||
// MARK: - VoiceMessagePlaybackViewDelegate
|
||||
|
||||
func voiceMessagePlaybackViewDidRequestPlaybackToggle() {
|
||||
if mediaServiceProvider.mediaIdentifier == attachment?.eventId {
|
||||
if mediaServiceProvider.audioPlayer.isPlaying {
|
||||
mediaServiceProvider.audioPlayer.pause()
|
||||
} else {
|
||||
mediaServiceProvider.audioPlayer.registerDelegate(self)
|
||||
mediaServiceProvider.audioPlayer.play()
|
||||
}
|
||||
if audioPlayer.isPlaying {
|
||||
audioPlayer.pause()
|
||||
} else {
|
||||
if let url = urlToLoad {
|
||||
mediaServiceProvider.mediaIdentifier = attachment?.eventId
|
||||
mediaServiceProvider.audioPlayer.registerDelegate(self)
|
||||
mediaServiceProvider.audioPlayer.loadContentFromURL(url)
|
||||
mediaServiceProvider.audioPlayer.play()
|
||||
}
|
||||
audioPlayer.play()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - VoiceMessageAudioPlayerDelegate
|
||||
|
||||
func audioPlayerDidFinishLoading(_ audioPlayer: VoiceMessageAudioPlayer) {
|
||||
if audioPlayer.url != self.urlToLoad {
|
||||
state = .stopped
|
||||
}
|
||||
updateUI()
|
||||
}
|
||||
|
||||
@@ -149,8 +135,8 @@ class VoiceMessagePlaybackController: VoiceMessageAudioPlayerDelegate, VoiceMess
|
||||
details.currentTime = VoiceMessagePlaybackController.timeFormatter.string(from: Date(timeIntervalSinceReferenceDate: self.duration))
|
||||
details.progress = 0.0
|
||||
default:
|
||||
details.currentTime = VoiceMessagePlaybackController.timeFormatter.string(from: Date(timeIntervalSinceReferenceDate: mediaServiceProvider.audioPlayer.currentTime))
|
||||
details.progress = (mediaServiceProvider.audioPlayer.duration > 0.0 ? mediaServiceProvider.audioPlayer.currentTime / mediaServiceProvider.audioPlayer.duration : 0.0)
|
||||
details.currentTime = VoiceMessagePlaybackController.timeFormatter.string(from: Date(timeIntervalSinceReferenceDate: audioPlayer.currentTime))
|
||||
details.progress = (audioPlayer.duration > 0.0 ? audioPlayer.currentTime / audioPlayer.duration : 0.0)
|
||||
}
|
||||
details.loading = self.loading
|
||||
|
||||
@@ -162,7 +148,6 @@ class VoiceMessagePlaybackController: VoiceMessageAudioPlayerDelegate, VoiceMess
|
||||
return
|
||||
}
|
||||
|
||||
mediaServiceProvider.audioPlayer.deregisterDelegate(self)
|
||||
self.state = .stopped
|
||||
self.loading = true
|
||||
self.samples = []
|
||||
@@ -178,21 +163,10 @@ class VoiceMessagePlaybackController: VoiceMessageAudioPlayerDelegate, VoiceMess
|
||||
}
|
||||
|
||||
self.loading = false
|
||||
self.urlToLoad = result.1
|
||||
self.audioPlayer.loadContentFromURL(result.1)
|
||||
self.duration = result.2
|
||||
self.samples = result.3
|
||||
|
||||
if self.mediaServiceProvider.mediaIdentifier == self.attachment?.eventId {
|
||||
self.mediaServiceProvider.audioPlayer.registerDelegate(self)
|
||||
if self.mediaServiceProvider.audioPlayer.isPlaying {
|
||||
self.state = .playing
|
||||
} else if self.mediaServiceProvider.audioPlayer.currentTime > 0 {
|
||||
self.state = .paused
|
||||
} else {
|
||||
self.state = .stopped
|
||||
}
|
||||
}
|
||||
|
||||
self.updateUI()
|
||||
case .failure:
|
||||
self.state = .error
|
||||
|
||||
Reference in New Issue
Block a user