Animated emoji sounds improvements

This commit is contained in:
Ilya Laktyushin 2020-10-27 20:17:38 +04:00
parent 24e18d6f7a
commit fb809af412
6 changed files with 20 additions and 13 deletions

View File

@ -111,6 +111,7 @@ private final class MediaPlayerContext {
private var baseRate: Double private var baseRate: Double
private let fetchAutomatically: Bool private let fetchAutomatically: Bool
private var playAndRecord: Bool private var playAndRecord: Bool
private var ambient: Bool
private var keepAudioSessionWhilePaused: Bool private var keepAudioSessionWhilePaused: Bool
private var continuePlayingWithoutSoundOnLostAudioSession: Bool private var continuePlayingWithoutSoundOnLostAudioSession: Bool
@ -132,7 +133,7 @@ private final class MediaPlayerContext {
private var stoppedAtEnd = false private var stoppedAtEnd = false
init(queue: Queue, audioSessionManager: ManagedAudioSession, playerStatus: Promise<MediaPlayerStatus>, audioLevelPipe: ValuePipe<Float>, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String?, streamable: MediaPlayerStreaming, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, playAndRecord: Bool, keepAudioSessionWhilePaused: Bool, continuePlayingWithoutSoundOnLostAudioSession: Bool) { init(queue: Queue, audioSessionManager: ManagedAudioSession, playerStatus: Promise<MediaPlayerStatus>, audioLevelPipe: ValuePipe<Float>, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String?, streamable: MediaPlayerStreaming, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, playAndRecord: Bool, ambient: Bool, keepAudioSessionWhilePaused: Bool, continuePlayingWithoutSoundOnLostAudioSession: Bool) {
assert(queue.isCurrent()) assert(queue.isCurrent())
self.queue = queue self.queue = queue
@ -149,6 +150,7 @@ private final class MediaPlayerContext {
self.baseRate = baseRate self.baseRate = baseRate
self.fetchAutomatically = fetchAutomatically self.fetchAutomatically = fetchAutomatically
self.playAndRecord = playAndRecord self.playAndRecord = playAndRecord
self.ambient = ambient
self.keepAudioSessionWhilePaused = keepAudioSessionWhilePaused self.keepAudioSessionWhilePaused = keepAudioSessionWhilePaused
self.continuePlayingWithoutSoundOnLostAudioSession = continuePlayingWithoutSoundOnLostAudioSession self.continuePlayingWithoutSoundOnLostAudioSession = continuePlayingWithoutSoundOnLostAudioSession
@ -368,7 +370,7 @@ private final class MediaPlayerContext {
self.audioRenderer = nil self.audioRenderer = nil
let queue = self.queue let queue = self.queue
renderer = MediaPlayerAudioRenderer(audioSession: .manager(self.audioSessionManager), playAndRecord: self.playAndRecord, forceAudioToSpeaker: self.forceAudioToSpeaker, baseRate: self.baseRate, audioLevelPipe: self.audioLevelPipe, updatedRate: { [weak self] in renderer = MediaPlayerAudioRenderer(audioSession: .manager(self.audioSessionManager), playAndRecord: self.playAndRecord, ambient: self.ambient, forceAudioToSpeaker: self.forceAudioToSpeaker, baseRate: self.baseRate, audioLevelPipe: self.audioLevelPipe, updatedRate: { [weak self] in
queue.async { queue.async {
if let strongSelf = self { if let strongSelf = self {
strongSelf.tick() strongSelf.tick()
@ -446,7 +448,7 @@ private final class MediaPlayerContext {
self.lastStatusUpdateTimestamp = nil self.lastStatusUpdateTimestamp = nil
if self.enableSound { if self.enableSound {
let queue = self.queue let queue = self.queue
let renderer = MediaPlayerAudioRenderer(audioSession: .manager(self.audioSessionManager), playAndRecord: self.playAndRecord, forceAudioToSpeaker: self.forceAudioToSpeaker, baseRate: self.baseRate, audioLevelPipe: self.audioLevelPipe, updatedRate: { [weak self] in let renderer = MediaPlayerAudioRenderer(audioSession: .manager(self.audioSessionManager), playAndRecord: self.playAndRecord, ambient: self.ambient, forceAudioToSpeaker: self.forceAudioToSpeaker, baseRate: self.baseRate, audioLevelPipe: self.audioLevelPipe, updatedRate: { [weak self] in
queue.async { queue.async {
if let strongSelf = self { if let strongSelf = self {
strongSelf.tick() strongSelf.tick()
@ -998,10 +1000,10 @@ public final class MediaPlayer {
} }
} }
public init(audioSessionManager: ManagedAudioSession, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String? = nil, streamable: MediaPlayerStreaming, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool = false, enableSound: Bool, baseRate: Double = 1.0, fetchAutomatically: Bool, playAndRecord: Bool = false, keepAudioSessionWhilePaused: Bool = false, continuePlayingWithoutSoundOnLostAudioSession: Bool = false) { public init(audioSessionManager: ManagedAudioSession, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String? = nil, streamable: MediaPlayerStreaming, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool = false, enableSound: Bool, baseRate: Double = 1.0, fetchAutomatically: Bool, playAndRecord: Bool = false, ambient: Bool = false, keepAudioSessionWhilePaused: Bool = false, continuePlayingWithoutSoundOnLostAudioSession: Bool = false) {
let audioLevelPipe = self.audioLevelPipe let audioLevelPipe = self.audioLevelPipe
self.queue.async { self.queue.async {
let context = MediaPlayerContext(queue: self.queue, audioSessionManager: audioSessionManager, playerStatus: self.statusValue, audioLevelPipe: audioLevelPipe, postbox: postbox, resourceReference: resourceReference, tempFilePath: tempFilePath, streamable: streamable, video: video, preferSoftwareDecoding: preferSoftwareDecoding, playAutomatically: playAutomatically, enableSound: enableSound, baseRate: baseRate, fetchAutomatically: fetchAutomatically, playAndRecord: playAndRecord, keepAudioSessionWhilePaused: keepAudioSessionWhilePaused, continuePlayingWithoutSoundOnLostAudioSession: continuePlayingWithoutSoundOnLostAudioSession) let context = MediaPlayerContext(queue: self.queue, audioSessionManager: audioSessionManager, playerStatus: self.statusValue, audioLevelPipe: audioLevelPipe, postbox: postbox, resourceReference: resourceReference, tempFilePath: tempFilePath, streamable: streamable, video: video, preferSoftwareDecoding: preferSoftwareDecoding, playAutomatically: playAutomatically, enableSound: enableSound, baseRate: baseRate, fetchAutomatically: fetchAutomatically, playAndRecord: playAndRecord, ambient: ambient, keepAudioSessionWhilePaused: keepAudioSessionWhilePaused, continuePlayingWithoutSoundOnLostAudioSession: continuePlayingWithoutSoundOnLostAudioSession)
self.contextRef = Unmanaged.passRetained(context) self.contextRef = Unmanaged.passRetained(context)
} }
} }

View File

@ -239,6 +239,7 @@ private final class AudioPlayerRendererContext {
let audioSessionDisposable = MetaDisposable() let audioSessionDisposable = MetaDisposable()
var audioSessionControl: ManagedAudioSessionControl? var audioSessionControl: ManagedAudioSessionControl?
let playAndRecord: Bool let playAndRecord: Bool
let ambient: Bool
var forceAudioToSpeaker: Bool { var forceAudioToSpeaker: Bool {
didSet { didSet {
if self.forceAudioToSpeaker != oldValue { if self.forceAudioToSpeaker != oldValue {
@ -249,7 +250,7 @@ private final class AudioPlayerRendererContext {
} }
} }
init(controlTimebase: CMTimebase, audioSession: MediaPlayerAudioSessionControl, playAndRecord: Bool, forceAudioToSpeaker: Bool, baseRate: Double, audioLevelPipe: ValuePipe<Float>, updatedRate: @escaping () -> Void, audioPaused: @escaping () -> Void) { init(controlTimebase: CMTimebase, audioSession: MediaPlayerAudioSessionControl, playAndRecord: Bool, ambient: Bool, forceAudioToSpeaker: Bool, baseRate: Double, audioLevelPipe: ValuePipe<Float>, updatedRate: @escaping () -> Void, audioPaused: @escaping () -> Void) {
assert(audioPlayerRendererQueue.isCurrent()) assert(audioPlayerRendererQueue.isCurrent())
self.audioSession = audioSession self.audioSession = audioSession
@ -262,6 +263,7 @@ private final class AudioPlayerRendererContext {
self.audioPaused = audioPaused self.audioPaused = audioPaused
self.playAndRecord = playAndRecord self.playAndRecord = playAndRecord
self.ambient = ambient
self.audioStreamDescription = audioRendererNativeStreamDescription() self.audioStreamDescription = audioRendererNativeStreamDescription()
@ -481,7 +483,7 @@ private final class AudioPlayerRendererContext {
switch self.audioSession { switch self.audioSession {
case let .manager(manager): case let .manager(manager):
self.audioSessionDisposable.set(manager.push(audioSessionType: self.playAndRecord ? .playWithPossiblePortOverride : .play, outputMode: self.forceAudioToSpeaker ? .speakerIfNoHeadphones : .system, once: true, manualActivate: { [weak self] control in self.audioSessionDisposable.set(manager.push(audioSessionType: self.ambient ? .ambient : (self.playAndRecord ? .playWithPossiblePortOverride : .play), outputMode: self.forceAudioToSpeaker ? .speakerIfNoHeadphones : .system, once: true, manualActivate: { [weak self] control in
audioPlayerRendererQueue.async { audioPlayerRendererQueue.async {
if let strongSelf = self { if let strongSelf = self {
strongSelf.audioSessionControl = control strongSelf.audioSessionControl = control
@ -751,7 +753,7 @@ public final class MediaPlayerAudioRenderer {
private let audioClock: CMClock private let audioClock: CMClock
public let audioTimebase: CMTimebase public let audioTimebase: CMTimebase
public init(audioSession: MediaPlayerAudioSessionControl, playAndRecord: Bool, forceAudioToSpeaker: Bool, baseRate: Double, audioLevelPipe: ValuePipe<Float>, updatedRate: @escaping () -> Void, audioPaused: @escaping () -> Void) { public init(audioSession: MediaPlayerAudioSessionControl, playAndRecord: Bool, ambient: Bool, forceAudioToSpeaker: Bool, baseRate: Double, audioLevelPipe: ValuePipe<Float>, updatedRate: @escaping () -> Void, audioPaused: @escaping () -> Void) {
var audioClock: CMClock? var audioClock: CMClock?
CMAudioClockCreate(allocator: nil, clockOut: &audioClock) CMAudioClockCreate(allocator: nil, clockOut: &audioClock)
if audioClock == nil { if audioClock == nil {
@ -764,7 +766,7 @@ public final class MediaPlayerAudioRenderer {
self.audioTimebase = audioTimebase! self.audioTimebase = audioTimebase!
audioPlayerRendererQueue.async { audioPlayerRendererQueue.async {
let context = AudioPlayerRendererContext(controlTimebase: audioTimebase!, audioSession: audioSession, playAndRecord: playAndRecord, forceAudioToSpeaker: forceAudioToSpeaker, baseRate: baseRate, audioLevelPipe: audioLevelPipe, updatedRate: updatedRate, audioPaused: audioPaused) let context = AudioPlayerRendererContext(controlTimebase: audioTimebase!, audioSession: audioSession, playAndRecord: playAndRecord, ambient: ambient, forceAudioToSpeaker: forceAudioToSpeaker, baseRate: baseRate, audioLevelPipe: audioLevelPipe, updatedRate: updatedRate, audioPaused: audioPaused)
self.contextRef = Unmanaged.passRetained(context) self.contextRef = Unmanaged.passRetained(context)
} }
} }

View File

@ -4,6 +4,7 @@ import AVFoundation
import UIKit import UIKit
public enum ManagedAudioSessionType: Equatable { public enum ManagedAudioSessionType: Equatable {
case ambient
case play case play
case playWithPossiblePortOverride case playWithPossiblePortOverride
case record(speaker: Bool) case record(speaker: Bool)
@ -22,6 +23,8 @@ public enum ManagedAudioSessionType: Equatable {
private func nativeCategoryForType(_ type: ManagedAudioSessionType, headphones: Bool, outputMode: AudioSessionOutputMode) -> AVAudioSession.Category { private func nativeCategoryForType(_ type: ManagedAudioSessionType, headphones: Bool, outputMode: AudioSessionOutputMode) -> AVAudioSession.Category {
switch type { switch type {
case .ambient:
return .ambient
case .play: case .play:
return .playback return .playback
case .record, .voiceCall, .videoCall: case .record, .voiceCall, .videoCall:
@ -665,7 +668,7 @@ public final class ManagedAudioSession {
print("ManagedAudioSession setting category for \(type) (native: \(nativeCategory))") print("ManagedAudioSession setting category for \(type) (native: \(nativeCategory))")
var options: AVAudioSession.CategoryOptions = [] var options: AVAudioSession.CategoryOptions = []
switch type { switch type {
case .play: case .play, .ambient:
break break
case .playWithPossiblePortOverride: case .playWithPossiblePortOverride:
if case .playAndRecord = nativeCategory { if case .playAndRecord = nativeCategory {

View File

@ -34,7 +34,7 @@ private final class PresentationCallToneRenderer {
self.toneRenderer = MediaPlayerAudioRenderer(audioSession: .custom({ control in self.toneRenderer = MediaPlayerAudioRenderer(audioSession: .custom({ control in
return controlImpl?(control) ?? EmptyDisposable return controlImpl?(control) ?? EmptyDisposable
}), playAndRecord: false, forceAudioToSpeaker: false, baseRate: 1.0, audioLevelPipe: self.audioLevelPipe, updatedRate: {}, audioPaused: {}) }), playAndRecord: false, ambient: false, forceAudioToSpeaker: false, baseRate: 1.0, audioLevelPipe: self.audioLevelPipe, updatedRate: {}, audioPaused: {})
controlImpl = { [weak self] control in controlImpl = { [weak self] control in
queue.async { queue.async {

View File

@ -1282,7 +1282,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
for (emoji, file) in emojiSounds.sounds { for (emoji, file) in emojiSounds.sounds {
if emoji.unicodeScalars.first == firstScalar { if emoji.unicodeScalars.first == firstScalar {
let mediaManager = item.context.sharedContext.mediaManager let mediaManager = item.context.sharedContext.mediaManager
let mediaPlayer = MediaPlayer(audioSessionManager: mediaManager.audioSession, postbox: item.context.account.postbox, resourceReference: .standalone(resource: file.resource), streamable: .none, video: false, preferSoftwareDecoding: false, enableSound: true, fetchAutomatically: true) let mediaPlayer = MediaPlayer(audioSessionManager: mediaManager.audioSession, postbox: item.context.account.postbox, resourceReference: .standalone(resource: file.resource), streamable: .none, video: false, preferSoftwareDecoding: false, enableSound: true, fetchAutomatically: true, ambient: true)
mediaPlayer.togglePlayPause() mediaPlayer.togglePlayPause()
mediaPlayer.actionAtEnd = .action({ [weak self] in mediaPlayer.actionAtEnd = .action({ [weak self] in
self?.mediaPlayer = nil self?.mediaPlayer = nil

View File

@ -214,7 +214,7 @@ final class ManagedAudioRecorderContext {
} }
return ActionDisposable { return ActionDisposable {
} }
}), playAndRecord: true, forceAudioToSpeaker: false, baseRate: 1.0, audioLevelPipe: ValuePipe<Float>(), updatedRate: { }), playAndRecord: true, ambient: false, forceAudioToSpeaker: false, baseRate: 1.0, audioLevelPipe: ValuePipe<Float>(), updatedRate: {
}, audioPaused: {}) }, audioPaused: {})
self.toneRenderer = toneRenderer self.toneRenderer = toneRenderer