
-
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…
Bonjour ! Je m’appelle Donovan et je suis ingénieur au sein de l’équipe Speech Framework, et je suis Shantini, ingénieure au sein de l’équipe Notes. Cette année, nous sommes ravis de vous présenter les avancées de notre API et de notre technologie de synthèse vocale : SpeechAnalyzer. Au cours de cette séance, nous discuterons de l’API SpeechAnalyzer et de ses principaux concepts. Nous aborderons également brièvement certaines fonctionnalités nouvelles du modèle sous-tendant l’API. Et enfin, nous effectuerons une démonstration de codage en direct avec l’API. SpeechAnalyzer alimente déjà les fonctionnalités de nombreuses apps système, telles que Notes, Dictaphone, Journal, etc.
En associant SpeechAnalyzer à Apple Intelligence, nous créons des fonctionnalités incroyablement performantes, comme la synthèse d’appels. Plus tard, je vous montrerai comment utiliser l’API pour créer votre propre fonctionnalité de transcription en direct. Tout d’abord, Donovan vous donnera un aperçu de la nouvelle API SpeechAnalyzer. La synthèse vocale, ou reconnaissance automatique de la parole ou ASR, est une technologie polyvalente qui permet de créer de superbes expériences utilisateur en utilisant la parole en direct ou enregistrée, et en la convertissant en une forme textuelle qu’un appareil peut facilement afficher ou interpréter. Les apps peuvent enregistrer, rechercher ou envoyer ce texte en temps réel, ou le transmettre à un grand modèle de langage basé sur du texte.
Dans iOS 10, nous avons introduit SFSpeechRecognizer. Cette classe vous donnait accès au modèle de synthèse vocale qui alimente Siri. Elle fonctionnait bien pour la dictée courte et permettait d’utiliser les serveurs Apple sur des appareils aux ressources limitées. Mais elle ne gérait pas certains cas d’utilisation aussi bien que nous l’aurions tous souhaité, et s’appuyait sur l’utilisateur pour ajouter des langues. Aujourd’hui, dans iOS 26, nous présentons une nouvelle API pour toutes nos plates-formes, appelée SpeechAnalyzer, qui gère plus de cas d’utilisation et de façon améliorée. La nouvelle API exploite la puissance de Swift pour effectuer des opérations de synthèse vocale et gérer les ressources du modèle sur l’appareil de l’utilisateur avec très peu de code. En plus de l’API, nous fournissons un nouveau modèle de synthèse vocale qui alimente déjà les fonctionnalités des apps sur nos plates-formes. Le nouveau modèle est à la fois plus rapide et plus flexible que celui précédemment disponible via SFSpeechRecognizer. Il est idéal pour les contenus audio longs et à distance, comme les conférences, les réunions ou les conversations. En raison de ces améliorations, Apple utilise ce nouveau modèle (et la nouvelle API) dans Notes et les autres apps mentionnées précédemment. Vous pouvez utiliser ces nouvelles fonctionnalités pour créer votre propre app avec le même type de fonctions de synthèse vocale que Notes et nos autres apps. Mais tout d’abord, examinons la conception de l’API. L’API se compose de la classe SpeechAnalyzer et de plusieurs autres classes. La classe SpeechAnalyzer gère une séance d’analyse. Vous pouvez ajouter une classe « module » à la séance pour effectuer un type d’analyse spécifique. L’ajout d’un module transcripteur à la séance en fait une séance de transcription qui effectue des opérations de synthèse vocale. L’analyseur reçoit les tampons audio, les transmet via le transcripteur et son modèle de synthèse vocale. Le modèle prédit le texte qui correspond à l’audio parlé et renvoie ce texte, accompagné de certaines métadonnées, à votre app.
Tout cela fonctionne de manière asynchrone. Votre app peut ajouter de l’audio à mesure qu’il devient disponible dans une tâche, et afficher ou traiter les résultats indépendamment dans une autre tâche. Les séquences asynchrones de Swift mettent en mémoire tampon et dissocient l’entrée et les résultats.
La séance « Meet AsyncSequence » de la WWDC21 explique comment fournir une séquence d’entrée et comment lire la séquence de résultats.
Pour corréler l’entrée avec les résultats, l’API utilise le code temporel de l’audio correspondant. En fait, toutes les opérations de l’API sont programmées à l’aide de codes temporels sur la ligne de temps audio, ce qui rend leur ordre prévisible et indépendant du moment de leur appel. Les codes temporels ont une précision de l’ordre d’un échantillon audio. Notez comment le transcripteur fournit les résultats de façon séquentielle, chacun couvrant sa propre plage audio sans chevauchement. C’est normalement ainsi que cela fonctionne. Mais, en option, vous pouvez rendre la transcription itérative à l’intérieur d’une plage audio. Vous pouvez le faire pour fournir un retour plus immédiat dans l’interface utilisateur de votre app. Vous pouvez afficher un premier résultat approximatif, puis de meilleures itérations de ce résultat dans les secondes suivantes. Les premiers résultats approximatifs sont appelés « résultats volatils ». Ils sont fournis aussitôt prononcés ou presque, mais ils manquent de précision. Cependant, le modèle améliore sa transcription à mesure qu’il reçoit plus d’audio et plus de contexte. Le résultat finira par être aussi bon que possible, et le transcripteur fournira un dernier « résultat finalisé ». Cela fait, le transcripteur ne fournira plus de résultats pour cette plage audio et passera à la suivante. Notez que les codes temporels montrent que les résultats ultérieurs améliorés remplacent les résultats précédents. Cela ne se produit que lorsque vous activez les résultats volatils. Normalement, le transcripteur ne fournit que des résultats finalisés, et aucun d’entre eux ne remplace les résultats précédents. Vous pouvez créer une fonction de transcription en une seule fonction si tout ce que vous avez à faire est de lire le fichier et de renvoyer la transcription. Ce travail ne nécessite pas de gestion volatile des résultats ou de simultanéité importante. Voici la fonction. Ici, nous créons le module de transcription. Nous lui indiquons la langue dans laquelle nous voulons transcrire. Il n’y a pas encore de résultats, mais nous les lirons au fur et à mesure de leur arrivée et utiliserons la version AsyncSequence de « reduce » pour les concaténer. Nous ferons cela en arrière-plan en utilisant « async let ». Ici, nous créons l’analyseur et ajoutons le module transcripteur. Ensuite, nous commençons à analyser le fichier. La méthode analyzeSequence lit le fichier et ajoute l’audio à une séquence d’entrée. Une fois le fichier lu, nous demandons à l’analyseur de terminer, car nous ne prévoyons pas de travailler sur d’autres données audio. Pour finir, nous renvoyons la transcription que nous avons assemblée en arrière-plan. Il s’agira des mots prononcés dans le fichier, sous forme d’une seule chaîne attribuée. Et nous avons terminé.
J’ai maintenant couvert les concepts et l’utilisation de base de l’API. Vous ajoutez des modules à une séance d’analyse pour effectuer, par exemple, une transcription. Cela fonctionne de manière simultanée et asynchrone, en dissociant l’entrée audio des résultats. Vous corrélez l’audio, les résultats et les opérations à l’aide de la ligne de temps audio de la séance. Certains de ces résultats sont volatils, si vous le souhaitez, et les autres sont définitifs et ne changeront pas. Et j’ai montré comment les pièces s’assemblent dans un cas d’utilisation à fonction unique. Plus tard, Shantini vous montrera comment étendre le travail de cette fonction à des vues, des modèles et des modèles de vue différents. Elle vous présentera plusieurs méthodes et propriétés des classes SpeechAnalyzer et Transcriber que vous pouvez utiliser pour gérer certains besoins courants. Vous pouvez également consulter la documentation à ce sujet. À présent, nous aimerions décrire certains des avantages du nouveau modèle de synthèse vocale de la classe SpeechTranscriber. SpeechTranscriber repose sur un tout nouveau modèle conçu par Apple pour prendre en charge un large éventail de cas d’utilisation. Nous voulions un modèle gérant les contenus audio longs et de type conversation, où certains locuteurs peuvent être éloignés du micro, comme lors de l’enregistrement d’une réunion. Nous voulions également faciliter les expériences de transcription en direct qui exigent une latence faible, sans sacrifier la précision ou la lisibilité, et nous voulions préserver la confidentialité des paroles. Notre nouveau modèle intégré répond à toutes ces exigences. Nous avons travaillé en étroite collaboration avec nos partenaires internes pour vous offrir une expérience de développement exceptionnelle. Et vous pouvez désormais prendre en charge les mêmes cas d’utilisation dans vos propres apps. Avec SpeechTranscriber, vous bénéficiez d’un puissant modèle de synthèse vocale que vous n’avez pas besoin d’acquérir et de gérer vous-même. Il vous suffit d’installer les ressources du modèle concerné via la nouvelle API AssetInventory. Vous pouvez les télécharger en cas de besoin. Le modèle est conservé dans le stockage système et n’augmente pas la taille de téléchargement ou de stockage de votre app. Il n’augmente pas non plus la taille de la mémoire d’exécution. Il fonctionne en dehors de l’espace mémoire de votre app. Vous n’avez donc pas à vous soucier de dépasser la limite de taille. Et comme nous améliorons constamment le modèle, le système installe automatiquement les mises à jour dès qu’elles sont disponibles. SpeechTranscriber peut actuellement transcrire ces langues, et d’autres à venir. Il est disponible pour toutes les plates-formes sauf watchOS, avec certaines exigences matérielles. Si vous avez besoin d’une langue ou d’un appareil non pris en charge, nous proposons également une deuxième classe de transcripteur : DictationTranscriber. Elle prend en charge les langues, le modèle de synthèse vocale et les appareils de la classe intégrée SFSpeechRecognizer sur iOS 10. Avec cette version améliorée de SFSpeechRecognizer, vos utilisateurs n’auront pas besoin d’aller dans Réglages et d’activer Siri ou la dictée au clavier pour une langue particulière. Voilà pour cette présentation de la nouvelle API et du nouveau modèle. C’était assez abstrait, mais nous allons maintenant passer au concret. Je laisse la parole à Shantini, qui vous montrera comment intégrer SpeechAnalyzer dans votre app. Merci pour cet excellent aperçu, Donovan ! Vous avez peut-être vu les incroyables fonctionnalités que nous avons ajoutées à Notes dans iOS 18 pour enregistrer et transcrire des appels téléphoniques, de l’audio en direct et de l’audio enregistré. Nous avons également intégré ces fonctionnalités à Apple Intelligence, ce qui permet d’obtenir des synthèses utiles et rapides. Nous avons travaillé en lien avec l’équipe Speech pour que SpeechAnalyzer et SpeechTranscriber nous permettent de fournir une fonctionnalité Notes de grande qualité. SpeechTranscriber est un excellent choix, car il est rapide et précis, même sur de longues distances, et intégré à l’appareil. L’un de nos autres objectifs était de vous permettre, en tant que développeur, de créer des fonctionnalités similaires à celles ajoutées à Notes et de les personnaliser pour les utilisateurs. Je serais ravie de vous aider à démarrer. Voici une app que je suis en train de créer avec une fonctionnalité de transcription en direct. Cette app est destinée aux enfants et enregistre et transcrit des histoires pour le coucher, ce qui vous permet de les réécouter. Voici les résultats de la transcription en temps réel.
Et, lorsque vous réécoutez l’audio, le segment de texte correspondant est mis en évidence, ce qui permet aux enfants de suivre l’histoire. Examinons la configuration du projet.
Dans mon exemple de code d’app, j’ai une classe Recorder et une classe SpokenWordTranscriber. Je les ai toutes deux rendues observables.
J’ai également créé ce modèle d’histoire pour encapsuler les informations de transcription et d’autres détails pertinents pour l’affichage. Pour finir, j’ai notre vue de transcription, avec des vues de transcription en direct et de lecture, et des boutons d’enregistrement et de lecture. Elle gère également l’état d’enregistrement et de la lecture. Voyons tout d’abord la transcription. Nous pouvons configurer la transcription en direct en trois étapes simples : Configurer le SpeechTranscriber. S’assurer que le modèle est présent. Gérer les résultats. Nous configurons notre SpeechTranscriber en l’initialisant avec un objet dans une langue donnée et les options nécessaires. Le code de langue correspond à la langue dans laquelle nous voulons recevoir la transcription. Comme Donovan l’a déjà souligné, les résultats volatils sont des suppositions en temps réel, et les résultats finalisés les meilleures suppositions. Ici, les deux sont utilisés, les résultats volatils avec une opacité moindre, remplacés par les résultats finalisés lorsqu’ils arrivent. Pour configurer cela dans SpeechTranscriber, nous allons définir ces types d’options. J’ajoute l’option audioTimeRange afin d’obtenir des informations temporelles.
Cela nous permettra de synchroniser la lecture du texte avec l’audio.
Il existe également des préréglages préconfigurés qui offrent différentes options.
Nous allons maintenant configurer l’objet SpeechAnalyzer avec notre module SpeechTranscriber.
Cela nous permet d’obtenir le format audio que nous devons utiliser.
Nous sommes également en mesure de nous assurer que notre modèle de synthèse vocale est en place.
Pour terminer la configuration de SpeechTranscriber, nous voulons enregistrer les références vers l’entrée AsyncStream et démarrer l’analyseur.
Maintenant que nous avons terminé de configurer SpeechTranscriber, voyons comment obtenir les modèles. Dans notre méthode visant à « garantir un modèle », nous allons ajouter des vérifications pour savoir si SpeechTranscriber prend en charge la transcription pour la langue souhaitée.
Nous vérifierons également si la langue est téléchargée et installée.
Si la langue est prise en charge mais non téléchargée, nous pouvons faire une demande à AssetInventory pour télécharger la prise en charge.
N’oubliez pas, la transcription se fait entièrement sur l’appareil, mais les modèles doivent être récupérés. La demande de téléchargement inclut un objet « progress » que vous pouvez utiliser pour informer l’utilisateur de ce qui se passe.
Votre app peut prendre en charge un nombre limité de langues à la fois. Si vous dépassez la limite, demandez à AssetInventory de supprimer l’attribution d’une ou plusieurs d’entre elles pour faire de la place.
Maintenant que nous avons nos modèles, passons à la partie amusante : les résultats.
À côté du code de configuration de notre transcripteur vocal, je crée une tâche et j’enregistre une référence vers celle-ci.
Je crée également deux variables pour suivre nos résultats volatils et finalisés.
SpeechTranscriber renvoie des résultats via AsyncStream. Chaque objet de résultat comporte plusieurs champs différents.
Le premier que nous voulons obtenir est « text », qui est représenté par une AttributedString. Il s’agit du résultat de la transcription d’un segment audio. Chaque fois que nous obtenons un résultat dans le flux, nous devons vérifier s’il est volatil ou finalisé en utilisant la propriété « isFinal ».
S’il est volatil, nous l’enregistrons dans volatileTranscript.
Chaque fois que nous obtenons un résultat finalisé, nous effaçons volatileTranscript et ajoutons le résultat à finalizedTranscript.
Si nous n’effaçons pas nos résultats volatils, nous pouvons avoir des doublons.
Chaque fois que nous obtenons un résultat finalisé, je l’écris également dans notre modèle d’histoire pour l’utiliser plus tard.
Je définis également une mise en forme conditionnelle à l’aide des API AttributedString de SwiftUI.
Cela nous permettra de visualiser les résultats de la transcription à mesure qu’ils passent de volatils à finalisés.
Si vous ignorez comment obtenir les données temporelles de la transcription, elles font partie de la chaîne attribuée, ce qui est pratique.
Chaque exécution possède un attribut « audioTimeRange » représenté par CMTimeRange. Je l’utilise dans mon code de visualisation pour mettre en évidence le bon segment. Voyons maintenant comment configurer notre entrée audio.
Dans ma fonction d’enregistrement, que j’appelle lorsque l’utilisateur appuie sur « Record », je demande une autorisation audio et démarre l’AVAudioSession. Nous devons également nous assurer que l’app est configurée pour utiliser le micro, dans les paramètres du projet.
J’appelle ensuite la fonction setUpTranscriber créée précédemment.
Enfin, je gère les entrées de mon flux audio. Voyons comment j’ai configuré cela. Il se passe plusieurs choses ici : Nous configurons AVAudioEngine pour renvoyer un flux asynchrone et transmettons les tampons entrants au flux.
Nous écrivons également l’audio sur le disque.
Enfin, nous démarrons l’audioEngine.
De retour dans ma fonction Record, je transmets l’entrée AsyncStream au transcripteur.
Les sources audio ont des formats de sortie et des fréquences d’échantillonnage différents. SpeechTranscriber nous a donné un bestAvailableAudioFormat que nous pouvons utiliser.
Je transmets nos tampons audio via une étape de conversion pour m’assurer que le format correspond à bestAvailableAudioFormat.
J’achemine ensuite le flux asynchrone vers l’objet inputBuilder à partir de SpeechTranscriber. Lorsque nous arrêtons l’enregistrement, nous voulons faire plusieurs choses. J’ai arrêté le moteur audio et le transcripteur. Il est important d’annuler vos tâches et d’appeler « finalize » sur votre flux d’analyseur. Ainsi, tous les résultats volatils seront finalisés. Voyons comment relier tout cela à notre vue.
Ma TranscriptView est reliée à l’histoire en cours et à SpokenWordTranscriber. Si nous enregistrons, nous montrons une concaténation de la transcription finalisée avec la transcription volatile que nous observons depuis la classe SpokenWordTranscriber. Pour la lecture, nous affichons la transcription finale du modèle de données. J’ai ajouté une méthode pour découper les phrases afin de les alléger visuellement.
Une fonctionnalité clé déjà mentionnée est la mise en évidence de chaque mot lors de la lecture. J’utilise des méthodes d’aide pour calculer si chaque exécution doit être mise en évidence, en fonction de son attribut audioTimeRange et de la durée de lecture actuelle.
La précision de SpeechTranscriber est excellente pour de nombreuses raisons, comme la possibilité d’utiliser Apple Intelligence pour effectuer des transformations utiles sur la sortie.
Ici, j’utilise la nouvelle API Foundation Models pour générer un titre pour mon histoire lorsqu’elle sera terminée. L’API facilite grandement la création d’un titre intelligent, ce qui m’évite d’avoir à en trouver un ! Pour en savoir plus sur les API Foundation Models, consultez la séance intitulée « Meet the Foundation Models Framework ».
Voyons notre fonctionnalité en action ! Je vais appuyer sur le bouton + pour créer une nouvelle histoire.
Ensuite, je vais lancer l’enregistrement. « Il était une fois, dans le pays légendaire de Magenta, une petite fille nommée Delilah qui vivait dans un château sur la colline. Delilah passait ses journées à parcourir la forêt et à s’occuper des animaux qui s’y trouvaient. »
Lorsque l’utilisateur a terminé, il peut réécouter l’histoire. Chaque mot est mis en évidence en même temps que l’audio.
Il était une fois, dans le pays légendaire de Magenta, une petite fille nommée Delilah qui vivait dans un château sur la colline.
Delilah passait ses journées à parcourir la forêt et à s’occuper des animaux qui s’y trouvaient. SpeechAnalyzer et SpeechTranscriber nous ont permis de créer une app complète avec un temps de démarrage très réduit. Pour en savoir plus, consultez la documentation de Speech Framework qui comprend l’exemple d’app que nous avons créée. Et voilà pour SpeechAnalyzer ! Nous savons que vous créerez des fonctionnalités incroyables avec. Je vous remercie pour votre attention.
-
-
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.