mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
Voice message recording enhancements (#2042)
* Use Combine to report AudioRecorder state changes * AudioRecorder is now using AVAudioEngine instead of AVAudioRecorder * Stop recording when audio configuration changes * AudioRecorder error handling * UnitTests * Use Accelerate to calculate RMS
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
// swiftlint:disable all
|
||||
import AnalyticsEvents
|
||||
import AVFoundation
|
||||
import Combine
|
||||
import Foundation
|
||||
import LocalAuthentication
|
||||
@@ -534,7 +535,7 @@ class AudioRecorderMock: AudioRecorderProtocol {
|
||||
set(value) { underlyingIsRecording = value }
|
||||
}
|
||||
var underlyingIsRecording: Bool!
|
||||
var url: URL?
|
||||
var audioFileUrl: URL?
|
||||
|
||||
//MARK: - record
|
||||
|
||||
@@ -544,18 +545,13 @@ class AudioRecorderMock: AudioRecorderProtocol {
|
||||
}
|
||||
var recordWithReceivedRecordID: AudioRecordingIdentifier?
|
||||
var recordWithReceivedInvocations: [AudioRecordingIdentifier] = []
|
||||
var recordWithReturnValue: Result<Void, AudioRecorderError>!
|
||||
var recordWithClosure: ((AudioRecordingIdentifier) async -> Result<Void, AudioRecorderError>)?
|
||||
var recordWithClosure: ((AudioRecordingIdentifier) async -> Void)?
|
||||
|
||||
func record(with recordID: AudioRecordingIdentifier) async -> Result<Void, AudioRecorderError> {
|
||||
func record(with recordID: AudioRecordingIdentifier) async {
|
||||
recordWithCallsCount += 1
|
||||
recordWithReceivedRecordID = recordID
|
||||
recordWithReceivedInvocations.append(recordID)
|
||||
if let recordWithClosure = recordWithClosure {
|
||||
return await recordWithClosure(recordID)
|
||||
} else {
|
||||
return recordWithReturnValue
|
||||
}
|
||||
await recordWithClosure?(recordID)
|
||||
}
|
||||
//MARK: - stopRecording
|
||||
|
||||
@@ -581,28 +577,103 @@ class AudioRecorderMock: AudioRecorderProtocol {
|
||||
deleteRecordingCallsCount += 1
|
||||
await deleteRecordingClosure?()
|
||||
}
|
||||
//MARK: - averagePowerForChannelNumber
|
||||
//MARK: - averagePower
|
||||
|
||||
var averagePowerForChannelNumberCallsCount = 0
|
||||
var averagePowerForChannelNumberCalled: Bool {
|
||||
return averagePowerForChannelNumberCallsCount > 0
|
||||
var averagePowerCallsCount = 0
|
||||
var averagePowerCalled: Bool {
|
||||
return averagePowerCallsCount > 0
|
||||
}
|
||||
var averagePowerForChannelNumberReceivedChannelNumber: Int?
|
||||
var averagePowerForChannelNumberReceivedInvocations: [Int] = []
|
||||
var averagePowerForChannelNumberReturnValue: Float!
|
||||
var averagePowerForChannelNumberClosure: ((Int) -> Float)?
|
||||
var averagePowerReturnValue: Float!
|
||||
var averagePowerClosure: (() -> Float)?
|
||||
|
||||
func averagePowerForChannelNumber(_ channelNumber: Int) -> Float {
|
||||
averagePowerForChannelNumberCallsCount += 1
|
||||
averagePowerForChannelNumberReceivedChannelNumber = channelNumber
|
||||
averagePowerForChannelNumberReceivedInvocations.append(channelNumber)
|
||||
if let averagePowerForChannelNumberClosure = averagePowerForChannelNumberClosure {
|
||||
return averagePowerForChannelNumberClosure(channelNumber)
|
||||
func averagePower() -> Float {
|
||||
averagePowerCallsCount += 1
|
||||
if let averagePowerClosure = averagePowerClosure {
|
||||
return averagePowerClosure()
|
||||
} else {
|
||||
return averagePowerForChannelNumberReturnValue
|
||||
return averagePowerReturnValue
|
||||
}
|
||||
}
|
||||
}
|
||||
class AudioSessionMock: AudioSessionProtocol {
|
||||
|
||||
//MARK: - requestRecordPermission
|
||||
|
||||
var requestRecordPermissionCallsCount = 0
|
||||
var requestRecordPermissionCalled: Bool {
|
||||
return requestRecordPermissionCallsCount > 0
|
||||
}
|
||||
var requestRecordPermissionReceivedResponse: ((Bool) -> Void)?
|
||||
var requestRecordPermissionReceivedInvocations: [((Bool) -> Void)] = []
|
||||
var requestRecordPermissionClosure: ((@escaping (Bool) -> Void) -> Void)?
|
||||
|
||||
func requestRecordPermission(_ response: @escaping (Bool) -> Void) {
|
||||
requestRecordPermissionCallsCount += 1
|
||||
requestRecordPermissionReceivedResponse = response
|
||||
requestRecordPermissionReceivedInvocations.append(response)
|
||||
requestRecordPermissionClosure?(response)
|
||||
}
|
||||
//MARK: - setAllowHapticsAndSystemSoundsDuringRecording
|
||||
|
||||
var setAllowHapticsAndSystemSoundsDuringRecordingThrowableError: Error?
|
||||
var setAllowHapticsAndSystemSoundsDuringRecordingCallsCount = 0
|
||||
var setAllowHapticsAndSystemSoundsDuringRecordingCalled: Bool {
|
||||
return setAllowHapticsAndSystemSoundsDuringRecordingCallsCount > 0
|
||||
}
|
||||
var setAllowHapticsAndSystemSoundsDuringRecordingReceivedInValue: Bool?
|
||||
var setAllowHapticsAndSystemSoundsDuringRecordingReceivedInvocations: [Bool] = []
|
||||
var setAllowHapticsAndSystemSoundsDuringRecordingClosure: ((Bool) throws -> Void)?
|
||||
|
||||
func setAllowHapticsAndSystemSoundsDuringRecording(_ inValue: Bool) throws {
|
||||
if let error = setAllowHapticsAndSystemSoundsDuringRecordingThrowableError {
|
||||
throw error
|
||||
}
|
||||
setAllowHapticsAndSystemSoundsDuringRecordingCallsCount += 1
|
||||
setAllowHapticsAndSystemSoundsDuringRecordingReceivedInValue = inValue
|
||||
setAllowHapticsAndSystemSoundsDuringRecordingReceivedInvocations.append(inValue)
|
||||
try setAllowHapticsAndSystemSoundsDuringRecordingClosure?(inValue)
|
||||
}
|
||||
//MARK: - setCategory
|
||||
|
||||
var setCategoryModeOptionsThrowableError: Error?
|
||||
var setCategoryModeOptionsCallsCount = 0
|
||||
var setCategoryModeOptionsCalled: Bool {
|
||||
return setCategoryModeOptionsCallsCount > 0
|
||||
}
|
||||
var setCategoryModeOptionsReceivedArguments: (category: AVAudioSession.Category, mode: AVAudioSession.Mode, options: AVAudioSession.CategoryOptions)?
|
||||
var setCategoryModeOptionsReceivedInvocations: [(category: AVAudioSession.Category, mode: AVAudioSession.Mode, options: AVAudioSession.CategoryOptions)] = []
|
||||
var setCategoryModeOptionsClosure: ((AVAudioSession.Category, AVAudioSession.Mode, AVAudioSession.CategoryOptions) throws -> Void)?
|
||||
|
||||
func setCategory(_ category: AVAudioSession.Category, mode: AVAudioSession.Mode, options: AVAudioSession.CategoryOptions) throws {
|
||||
if let error = setCategoryModeOptionsThrowableError {
|
||||
throw error
|
||||
}
|
||||
setCategoryModeOptionsCallsCount += 1
|
||||
setCategoryModeOptionsReceivedArguments = (category: category, mode: mode, options: options)
|
||||
setCategoryModeOptionsReceivedInvocations.append((category: category, mode: mode, options: options))
|
||||
try setCategoryModeOptionsClosure?(category, mode, options)
|
||||
}
|
||||
//MARK: - setActive
|
||||
|
||||
var setActiveOptionsThrowableError: Error?
|
||||
var setActiveOptionsCallsCount = 0
|
||||
var setActiveOptionsCalled: Bool {
|
||||
return setActiveOptionsCallsCount > 0
|
||||
}
|
||||
var setActiveOptionsReceivedArguments: (active: Bool, options: AVAudioSession.SetActiveOptions)?
|
||||
var setActiveOptionsReceivedInvocations: [(active: Bool, options: AVAudioSession.SetActiveOptions)] = []
|
||||
var setActiveOptionsClosure: ((Bool, AVAudioSession.SetActiveOptions) throws -> Void)?
|
||||
|
||||
func setActive(_ active: Bool, options: AVAudioSession.SetActiveOptions) throws {
|
||||
if let error = setActiveOptionsThrowableError {
|
||||
throw error
|
||||
}
|
||||
setActiveOptionsCallsCount += 1
|
||||
setActiveOptionsReceivedArguments = (active: active, options: options)
|
||||
setActiveOptionsReceivedInvocations.append((active: active, options: options))
|
||||
try setActiveOptionsClosure?(active, options)
|
||||
}
|
||||
}
|
||||
class BugReportServiceMock: BugReportServiceProtocol {
|
||||
var isRunning: Bool {
|
||||
get { return underlyingIsRunning }
|
||||
@@ -3168,6 +3239,11 @@ class VoiceMessageRecorderMock: VoiceMessageRecorderProtocol {
|
||||
set(value) { underlyingRecordingDuration = value }
|
||||
}
|
||||
var underlyingRecordingDuration: TimeInterval!
|
||||
var actions: AnyPublisher<VoiceMessageRecorderAction, Never> {
|
||||
get { return underlyingActions }
|
||||
set(value) { underlyingActions = value }
|
||||
}
|
||||
var underlyingActions: AnyPublisher<VoiceMessageRecorderAction, Never>!
|
||||
|
||||
//MARK: - startRecording
|
||||
|
||||
@@ -3175,16 +3251,11 @@ class VoiceMessageRecorderMock: VoiceMessageRecorderProtocol {
|
||||
var startRecordingCalled: Bool {
|
||||
return startRecordingCallsCount > 0
|
||||
}
|
||||
var startRecordingReturnValue: Result<Void, VoiceMessageRecorderError>!
|
||||
var startRecordingClosure: (() async -> Result<Void, VoiceMessageRecorderError>)?
|
||||
var startRecordingClosure: (() async -> Void)?
|
||||
|
||||
func startRecording() async -> Result<Void, VoiceMessageRecorderError> {
|
||||
func startRecording() async {
|
||||
startRecordingCallsCount += 1
|
||||
if let startRecordingClosure = startRecordingClosure {
|
||||
return await startRecordingClosure()
|
||||
} else {
|
||||
return startRecordingReturnValue
|
||||
}
|
||||
await startRecordingClosure?()
|
||||
}
|
||||
//MARK: - stopRecording
|
||||
|
||||
@@ -3192,16 +3263,11 @@ class VoiceMessageRecorderMock: VoiceMessageRecorderProtocol {
|
||||
var stopRecordingCalled: Bool {
|
||||
return stopRecordingCallsCount > 0
|
||||
}
|
||||
var stopRecordingReturnValue: Result<Void, VoiceMessageRecorderError>!
|
||||
var stopRecordingClosure: (() async -> Result<Void, VoiceMessageRecorderError>)?
|
||||
var stopRecordingClosure: (() async -> Void)?
|
||||
|
||||
func stopRecording() async -> Result<Void, VoiceMessageRecorderError> {
|
||||
func stopRecording() async {
|
||||
stopRecordingCallsCount += 1
|
||||
if let stopRecordingClosure = stopRecordingClosure {
|
||||
return await stopRecordingClosure()
|
||||
} else {
|
||||
return stopRecordingReturnValue
|
||||
}
|
||||
await stopRecordingClosure?()
|
||||
}
|
||||
//MARK: - cancelRecording
|
||||
|
||||
|
||||
Reference in New Issue
Block a user