AudioRecorderProtocol.swift 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import Foundation
  2. import Combine
  3. /// The physical source selected for one recording segment.
  4. enum RecordingSourceChoice: Identifiable, Equatable {
  5. case iPhone
  6. case spark(deviceID: String, displayName: String)
  7. var id: String {
  8. switch self {
  9. case .iPhone:
  10. return "iphone"
  11. case .spark(let deviceID, _):
  12. return "spark-\(deviceID)"
  13. }
  14. }
  15. var displayName: String {
  16. switch self {
  17. case .iPhone:
  18. return "iPhone 麦克风"
  19. case .spark(_, let displayName):
  20. return displayName
  21. }
  22. }
  23. var systemImage: String {
  24. switch self {
  25. case .iPhone: return "iphone"
  26. case .spark: return "wave.3.right.circle.fill"
  27. }
  28. }
  29. }
  30. enum AudioRecorderError: LocalizedError {
  31. case failedToStart(String)
  32. case failedToStop(String)
  33. var errorDescription: String? {
  34. switch self {
  35. case .failedToStart(let message), .failedToStop(let message):
  36. return message
  37. }
  38. }
  39. }
  40. /// Protocol abstracting audio recording capabilities.
  41. protocol AudioRecorderProtocol: ObservableObject {
  42. /// Current recording state
  43. var isRecording: Bool { get }
  44. /// Elapsed recording time in seconds
  45. var elapsedTime: TimeInterval { get }
  46. /// Current amplitude level (0.0 - 1.0) for waveform visualization
  47. var currentAmplitude: Float { get }
  48. /// Rolling buffer of recent amplitude samples for waveform drawing
  49. var waveformSamples: [Float] { get }
  50. /// The local file URL of the current/last recording.
  51. var outputFileURL: URL? { get }
  52. /// Whether the active recording is paused
  53. var isPaused: Bool { get }
  54. /// User-facing name of the active recording source.
  55. var sourceDisplayName: String { get }
  56. /// Current source status, such as connecting or recording.
  57. var statusMessage: String { get }
  58. /// A recoverable error that should be shown in the recording UI.
  59. var errorMessage: String? { get }
  60. func startRecording()
  61. func stopRecording()
  62. func stopRecording(completion: @escaping (Result<URL?, Error>) -> Void)
  63. func pauseRecording()
  64. func resumeRecording()
  65. }
  66. extension AudioRecorderProtocol {
  67. var sourceDisplayName: String { "iPhone 麦克风" }
  68. var statusMessage: String { isRecording ? "正在录音" : "正在准备" }
  69. var errorMessage: String? { nil }
  70. func stopRecording(completion: @escaping (Result<URL?, Error>) -> Void) {
  71. stopRecording()
  72. completion(.success(outputFileURL))
  73. }
  74. }