Cache volume samples
This commit is contained in:
parent
6fbc7396b8
commit
1e8b1d3fca
|
@ -572,6 +572,7 @@
|
||||||
C31D1DE32521718E005D4DA8 /* UserSelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31D1DE22521718E005D4DA8 /* UserSelectionVC.swift */; };
|
C31D1DE32521718E005D4DA8 /* UserSelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31D1DE22521718E005D4DA8 /* UserSelectionVC.swift */; };
|
||||||
C31D1DE9252172D4005D4DA8 /* ContactUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31D1DE8252172D4005D4DA8 /* ContactUtilities.swift */; };
|
C31D1DE9252172D4005D4DA8 /* ContactUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31D1DE8252172D4005D4DA8 /* ContactUtilities.swift */; };
|
||||||
C31F8117252546F200DD9FD9 /* file_example_MP3_2MG.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C31F8116252546F200DD9FD9 /* file_example_MP3_2MG.mp3 */; };
|
C31F8117252546F200DD9FD9 /* file_example_MP3_2MG.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C31F8116252546F200DD9FD9 /* file_example_MP3_2MG.mp3 */; };
|
||||||
|
C31F812625258FB000DD9FD9 /* Storage+VolumeSamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31F812525258FB000DD9FD9 /* Storage+VolumeSamples.swift */; };
|
||||||
C329FEEC24F7277900B1C64C /* LightModeSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C329FEEB24F7277900B1C64C /* LightModeSheet.swift */; };
|
C329FEEC24F7277900B1C64C /* LightModeSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C329FEEB24F7277900B1C64C /* LightModeSheet.swift */; };
|
||||||
C329FEEF24F7743F00B1C64C /* UIViewController+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C329FEED24F7742E00B1C64C /* UIViewController+Utilities.swift */; };
|
C329FEEF24F7743F00B1C64C /* UIViewController+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C329FEED24F7742E00B1C64C /* UIViewController+Utilities.swift */; };
|
||||||
C34C8F7423A7830B00D82669 /* SpaceMono-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C34C8F7323A7830A00D82669 /* SpaceMono-Bold.ttf */; };
|
C34C8F7423A7830B00D82669 /* SpaceMono-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C34C8F7323A7830A00D82669 /* SpaceMono-Bold.ttf */; };
|
||||||
|
@ -1372,6 +1373,7 @@
|
||||||
C31D1DE22521718E005D4DA8 /* UserSelectionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSelectionVC.swift; sourceTree = "<group>"; };
|
C31D1DE22521718E005D4DA8 /* UserSelectionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSelectionVC.swift; sourceTree = "<group>"; };
|
||||||
C31D1DE8252172D4005D4DA8 /* ContactUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactUtilities.swift; sourceTree = "<group>"; };
|
C31D1DE8252172D4005D4DA8 /* ContactUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactUtilities.swift; sourceTree = "<group>"; };
|
||||||
C31F8116252546F200DD9FD9 /* file_example_MP3_2MG.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = file_example_MP3_2MG.mp3; sourceTree = "<group>"; };
|
C31F8116252546F200DD9FD9 /* file_example_MP3_2MG.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = file_example_MP3_2MG.mp3; sourceTree = "<group>"; };
|
||||||
|
C31F812525258FB000DD9FD9 /* Storage+VolumeSamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+VolumeSamples.swift"; sourceTree = "<group>"; };
|
||||||
C329FEEB24F7277900B1C64C /* LightModeSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightModeSheet.swift; sourceTree = "<group>"; };
|
C329FEEB24F7277900B1C64C /* LightModeSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightModeSheet.swift; sourceTree = "<group>"; };
|
||||||
C329FEED24F7742E00B1C64C /* UIViewController+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Utilities.swift"; sourceTree = "<group>"; };
|
C329FEED24F7742E00B1C64C /* UIViewController+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Utilities.swift"; sourceTree = "<group>"; };
|
||||||
C34C8F7323A7830A00D82669 /* SpaceMono-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SpaceMono-Bold.ttf"; sourceTree = "<group>"; };
|
C34C8F7323A7830A00D82669 /* SpaceMono-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SpaceMono-Bold.ttf"; sourceTree = "<group>"; };
|
||||||
|
@ -2602,6 +2604,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
B8CCF63B239757C10091D419 /* Components */,
|
B8CCF63B239757C10091D419 /* Components */,
|
||||||
|
C31F812425258F9C00DD9FD9 /* Database */,
|
||||||
C32B405424A961E1001117B5 /* Dependencies */,
|
C32B405424A961E1001117B5 /* Dependencies */,
|
||||||
B8CCF63C239757DB0091D419 /* Utilities */,
|
B8CCF63C239757DB0091D419 /* Utilities */,
|
||||||
B8CCF63D2397580E0091D419 /* View Controllers */,
|
B8CCF63D2397580E0091D419 /* View Controllers */,
|
||||||
|
@ -2754,6 +2757,14 @@
|
||||||
path = "View Controllers";
|
path = "View Controllers";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C31F812425258F9C00DD9FD9 /* Database */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C31F812525258FB000DD9FD9 /* Storage+VolumeSamples.swift */,
|
||||||
|
);
|
||||||
|
path = Database;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C32B405424A961E1001117B5 /* Dependencies */ = {
|
C32B405424A961E1001117B5 /* Dependencies */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -3998,6 +4009,7 @@
|
||||||
340FC8AC204DAC8D007AEB0F /* PrivacySettingsTableViewController.m in Sources */,
|
340FC8AC204DAC8D007AEB0F /* PrivacySettingsTableViewController.m in Sources */,
|
||||||
B88847BC23E10BC6009836D2 /* GroupMembersVC.swift in Sources */,
|
B88847BC23E10BC6009836D2 /* GroupMembersVC.swift in Sources */,
|
||||||
B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */,
|
B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */,
|
||||||
|
C31F812625258FB000DD9FD9 /* Storage+VolumeSamples.swift in Sources */,
|
||||||
B85357C723A1FB5100AAF6CD /* LinkDeviceVCDelegate.swift in Sources */,
|
B85357C723A1FB5100AAF6CD /* LinkDeviceVCDelegate.swift in Sources */,
|
||||||
340FC8C5204DE223007AEB0F /* DebugUIBackup.m in Sources */,
|
340FC8C5204DE223007AEB0F /* DebugUIBackup.m in Sources */,
|
||||||
C35E8AAE2485E51D00ACB629 /* IP2Country.swift in Sources */,
|
C35E8AAE2485E51D00ACB629 /* IP2Country.swift in Sources */,
|
||||||
|
|
|
@ -6,7 +6,6 @@ final class VoiceMessageView2 : UIView {
|
||||||
private var isAnimating = false
|
private var isAnimating = false
|
||||||
private var volumeSamples: [Float] = [] { didSet { updateShapeLayers() } }
|
private var volumeSamples: [Float] = [] { didSet { updateShapeLayers() } }
|
||||||
private var progress: CGFloat = 0
|
private var progress: CGFloat = 0
|
||||||
private var duration: CGFloat = 1 // Not initialized at 0 to avoid division by zero
|
|
||||||
|
|
||||||
// MARK: Components
|
// MARK: Components
|
||||||
private lazy var loader: UIView = {
|
private lazy var loader: UIView = {
|
||||||
|
@ -31,6 +30,8 @@ final class VoiceMessageView2 : UIView {
|
||||||
private let margin: CGFloat = 4
|
private let margin: CGFloat = 4
|
||||||
private let sampleSpacing: CGFloat = 1
|
private let sampleSpacing: CGFloat = 1
|
||||||
|
|
||||||
|
@objc public static let contentHeight: CGFloat = 40
|
||||||
|
|
||||||
// MARK: Initialization
|
// MARK: Initialization
|
||||||
@objc(initWithVoiceMessage:)
|
@objc(initWithVoiceMessage:)
|
||||||
init(voiceMessage: TSAttachment) {
|
init(voiceMessage: TSAttachment) {
|
||||||
|
@ -54,12 +55,21 @@ final class VoiceMessageView2 : UIView {
|
||||||
guard let url = (voiceMessage as? TSAttachmentStream)?.originalMediaURL else {
|
guard let url = (voiceMessage as? TSAttachmentStream)?.originalMediaURL else {
|
||||||
return print("[Loki] Couldn't get URL for voice message.")
|
return print("[Loki] Couldn't get URL for voice message.")
|
||||||
}
|
}
|
||||||
AudioUtilities.getVolumeSamples(for: url).done(on: DispatchQueue.main) { [weak self] volumeSamples in
|
if let cachedVolumeSamples = Storage.getVolumeSamples(for: voiceMessage.uniqueId!) {
|
||||||
guard let self = self else { return }
|
self.volumeSamples = cachedVolumeSamples
|
||||||
self.volumeSamples = volumeSamples
|
|
||||||
self.stopAnimating()
|
self.stopAnimating()
|
||||||
}.catch(on: DispatchQueue.main) { error in
|
} else {
|
||||||
print("[Loki] Couldn't sample audio file due to error: \(error).")
|
let voiceMessageID = voiceMessage.uniqueId!
|
||||||
|
AudioUtilities.getVolumeSamples(for: url).done(on: DispatchQueue.main) { [weak self] volumeSamples in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.volumeSamples = volumeSamples
|
||||||
|
Storage.write { transaction in
|
||||||
|
Storage.setVolumeSamples(for: voiceMessageID, to: volumeSamples, using: transaction)
|
||||||
|
}
|
||||||
|
self.stopAnimating()
|
||||||
|
}.catch(on: DispatchQueue.main) { error in
|
||||||
|
print("[Loki] Couldn't sample audio file due to error: \(error).")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
showLoader()
|
showLoader()
|
||||||
|
@ -68,7 +78,7 @@ final class VoiceMessageView2 : UIView {
|
||||||
|
|
||||||
private func setUpViewHierarchy() {
|
private func setUpViewHierarchy() {
|
||||||
set(.width, to: 200)
|
set(.width, to: 200)
|
||||||
set(.height, to: 40)
|
set(.height, to: VoiceMessageView2.contentHeight)
|
||||||
addSubview(loader)
|
addSubview(loader)
|
||||||
loader.pin(to: self)
|
loader.pin(to: self)
|
||||||
layer.insertSublayer(backgroundShapeLayer, at: 0)
|
layer.insertSublayer(backgroundShapeLayer, at: 0)
|
||||||
|
@ -83,9 +93,9 @@ final class VoiceMessageView2 : UIView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func animateLoader() {
|
private func animateLoader() {
|
||||||
loader.frame = CGRect(x: 0, y: 0, width: 0, height: 40)
|
loader.frame = CGRect(x: 0, y: 0, width: 0, height: VoiceMessageView2.contentHeight)
|
||||||
UIView.animate(withDuration: 2) { [weak self] in
|
UIView.animate(withDuration: 2) { [weak self] in
|
||||||
self?.loader.frame = CGRect(x: 0, y: 0, width: 200, height: 40)
|
self?.loader.frame = CGRect(x: 0, y: 0, width: 200, height: VoiceMessageView2.contentHeight)
|
||||||
} completion: { [weak self] _ in
|
} completion: { [weak self] _ in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
if self.isAnimating { self.animateLoader() }
|
if self.isAnimating { self.animateLoader() }
|
||||||
|
@ -102,10 +112,9 @@ final class VoiceMessageView2 : UIView {
|
||||||
updateShapeLayers()
|
updateShapeLayers()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc(updateForProgress:duration:)
|
@objc(updateForProgress:)
|
||||||
func update(for progress: CGFloat, duration: CGFloat) {
|
func update(for progress: CGFloat) {
|
||||||
self.progress = progress
|
self.progress = progress
|
||||||
self.duration = duration
|
|
||||||
updateShapeLayers()
|
updateShapeLayers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +134,7 @@ final class VoiceMessageView2 : UIView {
|
||||||
let y = margin + (h - sH) / 2
|
let y = margin + (h - sH) / 2
|
||||||
let subPath = UIBezierPath(roundedRect: CGRect(x: x, y: y, width: sW, height: sH), cornerRadius: sW / 2)
|
let subPath = UIBezierPath(roundedRect: CGRect(x: x, y: y, width: sW, height: sH), cornerRadius: sW / 2)
|
||||||
backgroundPath.append(subPath)
|
backgroundPath.append(subPath)
|
||||||
if progress / duration > CGFloat(i) / CGFloat(volumeSamples.count) { foregroundPath.append(subPath) }
|
if progress > CGFloat(i) / CGFloat(volumeSamples.count) { foregroundPath.append(subPath) }
|
||||||
}
|
}
|
||||||
backgroundPath.close()
|
backgroundPath.close()
|
||||||
foregroundPath.close()
|
foregroundPath.close()
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
extension Storage {
|
||||||
|
|
||||||
|
static let volumeSamplesCollection = "LokiVolumeSamplesCollection"
|
||||||
|
|
||||||
|
static func getVolumeSamples(for attachment: String) -> [Float]? {
|
||||||
|
var result: [Float]?
|
||||||
|
read { transaction in
|
||||||
|
result = transaction.object(forKey: attachment, inCollection: volumeSamplesCollection) as? [Float]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
static func setVolumeSamples(for attachment: String, to volumeSamples: [Float], using transaction: YapDatabaseReadWriteTransaction) {
|
||||||
|
transaction.setObject(volumeSamples, forKey: attachment, inCollection: volumeSamplesCollection)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1063,7 +1063,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
case OWSMessageCellType_Audio:
|
case OWSMessageCellType_Audio:
|
||||||
result = CGSizeMake(maxMessageWidth, 40.0f);
|
result = CGSizeMake(maxMessageWidth, LKVoiceMessageView2.contentHeight);
|
||||||
break;
|
break;
|
||||||
case OWSMessageCellType_GenericAttachment: {
|
case OWSMessageCellType_GenericAttachment: {
|
||||||
TSAttachment *attachment = (self.viewItem.attachmentStream ?: self.viewItem.attachmentPointer);
|
TSAttachment *attachment = (self.viewItem.attachmentStream ?: self.viewItem.attachmentPointer);
|
||||||
|
|
|
@ -484,7 +484,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
||||||
|
|
||||||
self.audioProgressSeconds = progress;
|
self.audioProgressSeconds = progress;
|
||||||
|
|
||||||
[self.lastAudioMessageView updateForProgress:progress duration:duration];
|
[self.lastAudioMessageView updateForProgress:progress / duration];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Displayable Text
|
#pragma mark - Displayable Text
|
||||||
|
|
|
@ -146,7 +146,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
[self.audioPlayer play];
|
[self.audioPlayer play];
|
||||||
[self.audioPlayerPoller invalidate];
|
[self.audioPlayerPoller invalidate];
|
||||||
self.audioPlayerPoller = [NSTimer weakScheduledTimerWithTimeInterval:.5f
|
self.audioPlayerPoller = [NSTimer weakScheduledTimerWithTimeInterval:.05f
|
||||||
target:self
|
target:self
|
||||||
selector:@selector(audioPlayerUpdated:)
|
selector:@selector(audioPlayerUpdated:)
|
||||||
userInfo:nil
|
userInfo:nil
|
||||||
|
|
Loading…
Reference in New Issue