SwiftUI + SwiftData + iCloud, Apple Speech transcription (German), audio recording, summarization service protocol (LLM-ready), localization scaffolding (EN/DE/ES/FR), basic tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
73 lines
1.9 KiB
Swift
73 lines
1.9 KiB
Swift
import AVFoundation
|
|
import Foundation
|
|
|
|
@Observable
|
|
final class AudioRecorderService: NSObject {
|
|
var isRecording = false
|
|
var recordingDuration: TimeInterval = 0
|
|
|
|
private var audioRecorder: AVAudioRecorder?
|
|
private var timer: Timer?
|
|
private var startTime: Date?
|
|
|
|
func startRecording() throws -> URL {
|
|
let session = AVAudioSession.sharedInstance()
|
|
try session.setCategory(.record, mode: .default)
|
|
try session.setActive(true)
|
|
|
|
let fileName = "memo-\(Date.now.ISO8601Format()).m4a"
|
|
.replacingOccurrences(of: ":", with: "-")
|
|
let url = VoiceMemo.audioDirectory.appendingPathComponent(fileName)
|
|
|
|
let settings: [String: Any] = [
|
|
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
|
|
AVSampleRateKey: 44100.0,
|
|
AVNumberOfChannelsKey: 1,
|
|
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue,
|
|
]
|
|
|
|
audioRecorder = try AVAudioRecorder(url: url, settings: settings)
|
|
audioRecorder?.delegate = self
|
|
audioRecorder?.record()
|
|
|
|
isRecording = true
|
|
recordingDuration = 0
|
|
startTime = .now
|
|
|
|
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in
|
|
guard let self, let startTime = self.startTime else { return }
|
|
self.recordingDuration = Date.now.timeIntervalSince(startTime)
|
|
}
|
|
|
|
return url
|
|
}
|
|
|
|
func stopRecording() -> (url: URL, duration: TimeInterval)? {
|
|
guard let recorder = audioRecorder else { return nil }
|
|
|
|
let url = recorder.url
|
|
let duration = recorder.currentTime
|
|
|
|
recorder.stop()
|
|
timer?.invalidate()
|
|
timer = nil
|
|
audioRecorder = nil
|
|
isRecording = false
|
|
startTime = nil
|
|
|
|
try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
|
|
|
|
return (url, duration)
|
|
}
|
|
}
|
|
|
|
extension AudioRecorderService: AVAudioRecorderDelegate {
|
|
func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
|
|
if !flag {
|
|
isRecording = false
|
|
timer?.invalidate()
|
|
timer = nil
|
|
}
|
|
}
|
|
}
|