import AVFoundation import Speech /// The transcribing service that transcribes recorded audio into text on device. /// /// The service runs the given recorded audio file through a `SpeechAnalyzer` with a `SpeechTranscriber` module, joining the finalized /// results into the returned transcription. The transcription happens in the locale given at each call — or rather in the closest equivalent /// the transcriber supports. The speech model assets for that locale are downloaded and installed on first use; every transcription after /// that happens entirely offline. public struct AudioTranscribing: Transcribing { // MARK: Initializers /// Creates an audio transcribing service. public init() {} // MARK: Methods /// Transcribes the given recorded audio file into text, deleting the file when the transcription finishes. /// /// - Parameters: /// - audio: The location of the recorded audio file to transcribe. /// - locale: The locale of the spoken language to transcribe. /// - Returns: The transcription of the recorded audio. /// - Throws: ``AudioTranscribingError/localeNotSupported`` when the transcriber supports no equivalent of the given locale, /// or any error thrown while installing the speech model assets, reading the audio file, or analyzing its contents. public func callAsFunction( _ audio: URL, locale: Locale ) async throws -> Transcription { defer { try? FileManager.default.removeItem(at: audio) } guard let locale = await SpeechTranscriber.supportedLocale( equivalentTo: locale ) else { throw AudioTranscribingError.localeNotSupported } let transcriber = SpeechTranscriber( locale: locale, preset: .transcription ) if let request = try await AssetInventory.assetInstallationRequest(supporting: [transcriber]) { try await request.downloadAndInstall() } let analyzer = SpeechAnalyzer(modules: [transcriber]) async let text = transcriber.results.reduce(into: "") { text, result in text += String(result.text.characters) } let file = try AVAudioFile(forReading: audio) if let lastSampleTime = try await analyzer.analyzeSequence(from: file) { try await analyzer.finalizeAndFinish(through: lastSampleTime) } else { await analyzer.cancelAndFinishNow() } return try await .init(text: text) } } // MARK: - Errors /// The errors thrown by ``AudioTranscribing``. public enum AudioTranscribingError: Error { /// The transcriber supports no equivalent of the locale the transcription was requested with. case localeNotSupported }