| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485 |
- import Foundation
- import Combine
- /// The physical source selected for one recording segment.
- enum RecordingSourceChoice: Identifiable, Equatable {
- case iPhone
- case spark(deviceID: String, displayName: String)
- var id: String {
- switch self {
- case .iPhone:
- return "iphone"
- case .spark(let deviceID, _):
- return "spark-\(deviceID)"
- }
- }
- var displayName: String {
- switch self {
- case .iPhone:
- return "iPhone 麦克风"
- case .spark(_, let displayName):
- return displayName
- }
- }
- var systemImage: String {
- switch self {
- case .iPhone: return "iphone"
- case .spark: return "wave.3.right.circle.fill"
- }
- }
- }
- enum AudioRecorderError: LocalizedError {
- case failedToStart(String)
- case failedToStop(String)
- var errorDescription: String? {
- switch self {
- case .failedToStart(let message), .failedToStop(let message):
- return message
- }
- }
- }
- /// Protocol abstracting audio recording capabilities.
- /// Allows swapping between Mock and Real (AVAudioEngine) implementations.
- protocol AudioRecorderProtocol: ObservableObject {
- /// Current recording state
- var isRecording: Bool { get }
- /// Elapsed recording time in seconds
- var elapsedTime: TimeInterval { get }
- /// Current amplitude level (0.0 - 1.0) for waveform visualization
- var currentAmplitude: Float { get }
- /// Rolling buffer of recent amplitude samples for waveform drawing
- var waveformSamples: [Float] { get }
- /// The local file URL of the current/last recording. nil for mock.
- var outputFileURL: URL? { get }
- /// Whether the active recording is paused
- var isPaused: Bool { get }
- /// User-facing name of the active recording source.
- var sourceDisplayName: String { get }
- /// Current source status, such as connecting or recording.
- var statusMessage: String { get }
- /// A recoverable error that should be shown in the recording UI.
- var errorMessage: String? { get }
-
- func startRecording()
- func stopRecording()
- func stopRecording(completion: @escaping (Result<URL?, Error>) -> Void)
- func pauseRecording()
- func resumeRecording()
- }
- extension AudioRecorderProtocol {
- var sourceDisplayName: String { "iPhone 麦克风" }
- var statusMessage: String { isRecording ? "正在录音" : "正在准备" }
- var errorMessage: String? { nil }
- func stopRecording(completion: @escaping (Result<URL?, Error>) -> Void) {
- stopRecording()
- completion(.success(outputFileURL))
- }
- }
|