-
Apportez une synthèse vocale avancée à votre app avec SpeechAnalyzer
Découvrez la nouvelle API SpeechAnalyzer pour la synthèse vocale. Nous en apprendrons davantage sur l'API Swift et ses capacités, qui alimentent les fonctionnalités dans Notes, Dictaphone, Journal, et bien plus encore. Nous explorerons en détail le fonctionnement de la synthèse vocale et la façon dont SpeechAnalyzer et SpeechTranscriber peuvent vous aider à créer des fonctionnalités innovantes et performantes. Et vous apprendrez à incorporer SpeechAnalyzer et la transcription en direct dans votre app grâce à une séance code-along.
Chapitres
- 0:00 - Introduction
- 2:41 - API SpeechAnalyzer
- 7:03 - Modèle SpeechTranscriber
- 9:06 - Créer une fonctionnalité de synthèse vocale
Ressources
Vidéos connexes
WWDC23
-
Rechercher dans cette vidéo…
-
-
5:21 - Transcribe a file
// Set up transcriber. Read results asynchronously, and concatenate them together. let transcriber = SpeechTranscriber(locale: locale, preset: .offlineTranscription) async let transcriptionFuture = try transcriber.results .reduce("") { str, result in str + result.text } let analyzer = SpeechAnalyzer(modules: [transcriber]) if let lastSample = try await analyzer.analyzeSequence(from: file) { try await analyzer.finalizeAndFinish(through: lastSample) } else { await analyzer.cancelAndFinishNow() } return try await transcriptionFuture -
11:02 - Speech Transcriber setup (volatile results + timestamps)
func setUpTranscriber() async throws { transcriber = SpeechTranscriber(locale: Locale.current, transcriptionOptions: [], reportingOptions: [.volatileResults], attributeOptions: [.audioTimeRange]) } -
11:47 - Speech Transcriber setup (volatile results, no timestamps)
// transcriber = SpeechTranscriber(locale: Locale.current, preset: .progressiveLiveTranscription) -
11:54 - Set up SpeechAnalyzer
func setUpTranscriber() async throws { transcriber = SpeechTranscriber(locale: Locale.current, transcriptionOptions: [], reportingOptions: [.volatileResults], attributeOptions: [.audioTimeRange]) guard let transcriber else { throw TranscriptionError.failedToSetupRecognitionStream } analyzer = SpeechAnalyzer(modules: [transcriber]) } -
12:00 - Get audio format
func setUpTranscriber() async throws { transcriber = SpeechTranscriber(locale: Locale.current, transcriptionOptions: [], reportingOptions: [.volatileResults], attributeOptions: [.audioTimeRange]) guard let transcriber else { throw TranscriptionError.failedToSetupRecognitionStream } analyzer = SpeechAnalyzer(modules: [transcriber]) self.analyzerFormat = await SpeechAnalyzer.bestAvailableAudioFormat(compatibleWith: [transcriber]) } -
12:06 - Ensure models
func setUpTranscriber() async throws { transcriber = SpeechTranscriber(locale: Locale.current, transcriptionOptions: [], reportingOptions: [.volatileResults], attributeOptions: [.audioTimeRange]) guard let transcriber else { throw TranscriptionError.failedToSetupRecognitionStream } analyzer = SpeechAnalyzer(modules: [transcriber]) self.analyzerFormat = await SpeechAnalyzer.bestAvailableAudioFormat(compatibleWith: [transcriber]) do { try await ensureModel(transcriber: transcriber, locale: Locale.current) } catch let error as TranscriptionError { print(error) return } } -
12:15 - Finish SpeechAnalyzer setup
func setUpTranscriber() async throws { transcriber = SpeechTranscriber(locale: Locale.current, transcriptionOptions: [], reportingOptions: [.volatileResults], attributeOptions: [.audioTimeRange]) guard let transcriber else { throw TranscriptionError.failedToSetupRecognitionStream } analyzer = SpeechAnalyzer(modules: [transcriber]) self.analyzerFormat = await SpeechAnalyzer.bestAvailableAudioFormat(compatibleWith: [transcriber]) do { try await ensureModel(transcriber: transcriber, locale: Locale.current) } catch let error as TranscriptionError { print(error) return } (inputSequence, inputBuilder) = AsyncStream<AnalyzerInput>.makeStream() guard let inputSequence else { return } try await analyzer?.start(inputSequence: inputSequence) } -
12:30 - Check for language support
public func ensureModel(transcriber: SpeechTranscriber, locale: Locale) async throws { guard await supported(locale: locale) else { throw TranscriptionError.localeNotSupported } } func supported(locale: Locale) async -> Bool { let supported = await SpeechTranscriber.supportedLocales return supported.map { $0.identifier(.bcp47) }.contains(locale.identifier(.bcp47)) } func installed(locale: Locale) async -> Bool { let installed = await Set(SpeechTranscriber.installedLocales) return installed.map { $0.identifier(.bcp47) }.contains(locale.identifier(.bcp47)) } -
12:39 - Check for model installation
public func ensureModel(transcriber: SpeechTranscriber, locale: Locale) async throws { guard await supported(locale: locale) else { throw TranscriptionError.localeNotSupported } if await installed(locale: locale) { return } else { try await downloadIfNeeded(for: transcriber) } } func supported(locale: Locale) async -> Bool { let supported = await SpeechTranscriber.supportedLocales return supported.map { $0.identifier(.bcp47) }.contains(locale.identifier(.bcp47)) } func installed(locale: Locale) async -> Bool { let installed = await Set(SpeechTranscriber.installedLocales) return installed.map { $0.identifier(.bcp47) }.contains(locale.identifier(.bcp47)) } -
12:52 - Download the model
func downloadIfNeeded(for module: SpeechTranscriber) async throws { if let downloader = try await AssetInventory.assetInstallationRequest(supporting: [module]) { self.downloadProgress = downloader.progress try await downloader.downloadAndInstall() } } -
13:19 - Deallocate an asset
func deallocate() async { let allocated = await AssetInventory.allocatedLocales for locale in allocated { await AssetInventory.deallocate(locale: locale) } } -
13:31 - Speech result handling
recognizerTask = Task { do { for try await case let result in transcriber.results { let text = result.text if result.isFinal { finalizedTranscript += text volatileTranscript = "" updateStoryWithNewText(withFinal: text) print(text.audioTimeRange) } else { volatileTranscript = text volatileTranscript.foregroundColor = .purple.opacity(0.4) } } } catch { print("speech recognition failed") } } -
15:13 - Set up audio recording
func record() async throws { self.story.url.wrappedValue = url guard await isAuthorized() else { print("user denied mic permission") return } #if os(iOS) try setUpAudioSession() #endif try await transcriber.setUpTranscriber() for await input in try await audioStream() { try await self.transcriber.streamAudioToTranscriber(input) } } -
15:37 - Set up audio recording via AVAudioEngine
#if os(iOS) func setUpAudioSession() throws { let audioSession = AVAudioSession.sharedInstance() try audioSession.setCategory(.playAndRecord, mode: .spokenAudio) try audioSession.setActive(true, options: .notifyOthersOnDeactivation) } #endif private func audioStream() async throws -> AsyncStream<AVAudioPCMBuffer> { try setupAudioEngine() audioEngine.inputNode.installTap(onBus: 0, bufferSize: 4096, format: audioEngine.inputNode.outputFormat(forBus: 0)) { [weak self] (buffer, time) in guard let self else { return } writeBufferToDisk(buffer: buffer) self.outputContinuation?.yield(buffer) } audioEngine.prepare() try audioEngine.start() return AsyncStream(AVAudioPCMBuffer.self, bufferingPolicy: .unbounded) { continuation in outputContinuation = continuation } } -
16:01 - Stream audio to SpeechAnalyzer and SpeechTranscriber
func streamAudioToTranscriber(_ buffer: AVAudioPCMBuffer) async throws { guard let inputBuilder, let analyzerFormat else { throw TranscriptionError.invalidAudioDataType } let converted = try self.converter.convertBuffer(buffer, to: analyzerFormat) let input = AnalyzerInput(buffer: converted) inputBuilder.yield(input) } -
16:29 - Finalize the transcript stream
try await analyzer?.finalizeAndFinishThroughEndOfInput()
-
-
- 0:00 - Introduction
Apple dévoile SpeechAnalyzer, une nouvelle API et solution de reconnaissance vocale intégrée à iOS 26, qui succède à SFSpeechRecognizer lancé avec iOS 10. Conçue avec Swift, cette technologie offre des performances accrues et une plus grande souplesse d’utilisation, notamment pour l’analyse des enregistrements audio de longue durée ou captés à distance. Elle s’avère particulièrement adaptée pour diverses situations, comme les conférences, les réunions ou les conversations courantes. La nouvelle API vous permet de créer des fonctionnalités de transcription en direct et est déjà intégrée à des apps système, telles que Notes, Dictaphone et Journal. Associée à Apple Intelligence, elle facilite l’utilisation de fonctionnalités puissantes, telles que la synthèse d’appels.
- 2:41 - API SpeechAnalyzer
L’architecture de l’API est centrée sur la classe SpeechAnalyzer qui permet de gérer les sessions d’analyse. Lorsqu’on y ajoute un module de transcription, la session se transforme en une session capable de convertir la parole en texte. Les flux audio sont envoyés à l’analyseur, qui les transmet ensuite au modèle de reconnaissance vocale du transcripteur. Le modèle génère alors le texte et les métadonnées correspondants, qui sont ensuite transmis à l’app de façon asynchrone grâce aux séquences Swift. Les interactions avec l’API sont synchronisées grâce à des marqueurs temporels sur la piste audio, ce qui garantit une séquence logique et une autonomie complète. La transcription se fait de manière ordonnée, segment par segment. Une option permet d’obtenir des résultats préliminaires rapidement pour une meilleure réactivité de l’interface, même si ces premiers résultats sont approximatifs. Ils sont ensuite perfectionnés pour aboutir à la version définitive. La suite de cette présentation explore un exemple concret qui illustre la création d’un module de transcription. Nous verrons comment configurer la langue, traiter un fichier audio, assembler les résultats grâce à des séquences asynchrones et obtenir la transcription finale sous forme de texte. Cette API offre l’avantage de gérer le traitement en parallèle et de manière asynchrone tout en séparant le traitement audio des résultats. Elle peut également s’adapter à des besoins plus sophistiqués dans différentes architectures, comme nous le découvrirons par la suite.
- 7:03 - Modèle SpeechTranscriber
Apple a développé un nouveau système de reconnaissance vocale intégré à SpeechTranscriber. Cette innovation permet de transcrire efficacement différents types de contenus audio, qu’il s’agisse de longs enregistrements, de réunions ou même de conversations en temps réel, avec une excellente précision et une latence faible. Le système fonctionne directement sur l’appareil, ce qui garantit la confidentialité et l’efficacité. Avantage non négligeable : il n’alourdit pas l’app et sa mémoire, et se met automatiquement à jour. Vous pouvez facilement intégrer le modèle dans vos apps à l’aide de l’API AssetInventory. La fonction SpeechTranscriber est compatible avec plusieurs langues et fonctionne sur la majorité des appareils Apple. Pour les langues ou les appareils non pris en charge, une solution alternative, DictationTranscriber, est mise à votre disposition.
- 9:06 - Créer une fonctionnalité de synthèse vocale
Dans iOS 18, l’app Notes a été améliorée avec de nouvelles fonctionnalités qui permettent d’enregistrer et de transcrire des appels téléphoniques, des enregistrements audio en direct et des enregistrements audio. Ces fonctionnalités sont intégrées à Apple Intelligence pour générer des résumés. L’équipe Speech a développé SpeechAnalyzer et SpeechTranscriber, qui permettent une transcription de haute qualité sur l’appareil, rapide et précise, même à distance. Grâce à ces outils, vous pouvez développer vos propres solutions de transcription. Prenons l’exemple d’une app qui permet d’enregistrer et de retranscrire des contes pour enfants. Elle affiche la transcription en direct et souligne le texte au fur et à mesure de la lecture audio. Pour utiliser la transcription en direct dans une app, procédez comme suit : configurez SpeechTranscriber avec les paramètres régionaux et les options appropriées, assurez-vous que le modèle de reconnaissance vocale nécessaire est téléchargé et installé sur l’appareil, puis traitez les résultats de la transcription à mesure qu’ils sont reçus via un AsyncStream. Les résultats affichent simultanément la transcription en direct et la version définitive, ce qui permet une synchronisation fluide entre le texte et la lecture audio. Lorsqu’un résultat final est obtenu, « volatileTranscript » est effacé et le résultat est ajouté à « finalizedTranscript » afin d’éviter les doublons. Le résultat final est également enregistré dans le modèle Story pour une utilisation ultérieure et visualisé avec une mise en forme conditionnelle à l’aide des API SwiftUI AttributedString. Configurez l’entrée audio en demandant l’autorisation, en démarrant « AVAudioSession » et en configurant AVAudioEngine pour qu’il renvoie un AsyncStream. L’audio est enregistré sur le disque et transmis au transcripteur après avoir été converti au meilleur format audio disponible. Lorsque l’enregistrement est arrêté, le système coupe le moteur audio et le transcripteur, puis finalise tous les résultats temporaires. Pendant l’enregistrement, la fenêtre de transcription affiche à la fois le texte définitif et les éléments provisoires. Lors de la lecture, elle montre la transcription complète tirée du modèle de données, en surlignant les mots au fur et à mesure qu’ils sont prononcés. Dans l’exemple d’app, Apple Intelligence permet de créer automatiquement un titre grâce à l’API Foundation Models. Vous verrez ainsi comment exploiter les capacités d’Apple Intelligence pour transformer efficacement le texte issu de la reconnaissance vocale. Le framework Speech permet le développement de cette app avec un temps de démarrage minimal. Vous trouverez plus de détails dans sa documentation.