-
Incorpora la conversión avanzada de voz a texto a tu app con SpeechAnalyzer
Descubre la nueva API SpeechAnalyzer para conversión de voz a texto. Aprenderemos sobre la API Swift y sus capacidades, que potencian las funcionalidades en Notas, Notas de Voz, Diario y más. Profundizaremos sobre cómo ocurre la conversión de voz a texto y cómo SpeechAnalyzer y SpeechTranscriber te permiten crear funcionalidades interesantes y de alto rendimiento. Además, aprenderás cómo incorporar SpeechAnalyzer y la transcripción en vivo a tu app con un código adjunto.
Capítulos
- 0:00 - Introducción
- 2:41 - API SpeechAnalyzer
- 7:03 - Modelo SpeechTranscriber
- 9:06 - Crea una funcionalidad de conversión de voz a texto
Recursos
Videos relacionados
WWDC23
-
Buscar este video…
-
-
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 - Introducción
Apple presenta SpeechAnalyzer, una nueva API y tecnología de conversión de voz a texto en iOS 26, que reemplaza a SFSpeechRecognizer presentado en iOS 10. SpeechAnalyzer, desarrollado con Swift, es más rápido, más flexible y admite audio de formato largo y distante, lo que lo hace adecuado para diversos casos de uso, como conferencias, reuniones y conversaciones. La nueva API te permite crear funciones de transcripción en vivo y ya está potenciando apps del sistema como Notas, Notas de voz y Diario. Cuando se combina con Apple Intelligence, facilita funcionalidades potentes como el resumen de llamadas.
- 2:41 - API SpeechAnalyzer
El diseño de la API se centra en la clase SpeechAnalyzer, que administra las sesiones de análisis. Al agregar un módulo transcriptor, la sesión se convierte en una sesión de transcripción que es capaz de llevar a cabo el procesamiento de voz a texto. Los búferes de audio se pasan a la instancia de análisis, que los conduce del transcriptor a su modelo de conversión de voz a texto. El modelo predice texto y metadatos, que se devuelven de forma asincrónica a la app mediante las secuencias asincrónicas de Swift. Todas las operaciones de API se programan usando códigos de tiempo en la línea de tiempo de audio, lo que garantiza un orden y una independencia predecibles. El transcriptor entrega resultados en secuencia, cubriendo rangos de audio específicos. Una funcionalidad opcional permite la transcripción iterativa dentro de un rango, brindando “resultados volátiles” inmediatos, aunque menos precisos, para una respuesta de IU más rápida, que luego se refinan en resultados finales. En esta presentación se analiza un caso de uso que demuestra cómo crear un módulo transcriptor, establecer la configuración regional, leer audio de un archivo, concatenar resultados utilizando secuencias asincrónicas y devolver la transcripción final como una cadena de atributos. La API permite el procesamiento simultáneo y asincrónico, desacoplando la entrada de audio de los resultados, y puede ampliarse para administrar necesidades más complejas en diferentes vistas, modelos y modelos de vista, como se demuestra más adelante.
- 7:03 - Modelo SpeechTranscriber
Apple desarrolló un nuevo modelo de conversión de voz a texto para la clase SpeechTranscriber, diseñado para manejar diversos escenarios, como grabaciones de formato largo, reuniones y transcripciones en vivo con baja latencia y alta precisión. El modelo funciona completamente en el dispositivo, lo que garantiza la privacidad y la eficiencia. No aumenta el tamaño de la app ni el uso de memoria y se actualiza automáticamente. Puedes integrar fácilmente el modelo en tus apps usando la API AssetInventory. La clase SpeechTranscriber actualmente admite varios idiomas y está disponible en la mayoría de las plataformas de Apple, con una opción alternativa, DictationTranscriber, para idiomas o dispositivos no compatibles.
- 9:06 - Crea una funcionalidad de conversión de voz a texto
En iOS 18, la app Notas se mejoró con nuevas funcionalidades que permiten a las personas grabar y transcribir llamadas telefónicas, audio en vivo y audio grabado. Estas funcionalidades están integradas con Apple Intelligence para generar resúmenes. El equipo de Speech desarrolló SpeechAnalyzer y SpeechTranscriber, lo que permite una transcripción en el dispositivo de alta calidad que es rápida y precisa, incluso a distancia. Ahora puedes usar estas herramientas para crear tus propias funcionalidades de transcripción personalizadas. Una app de ejemplo está diseñada para niños; graba y transcribe cuentos para antes de dormir. La app muestra los resultados de la transcripción en tiempo real y resalta el segmento de texto correspondiente durante la reproducción de audio. Para implementar la transcripción en vivo en una app, debes seguir tres pasos principales: configura SpeechTranscriber con la configuración regional y las opciones adecuadas, asegúrate de que el modelo de conversión de voz a texto necesario esté descargado e instalado en el dispositivo y luego administra los resultados de la transcripción a medida que se reciben a través de un AsyncStream. Los resultados incluyen texto volátil (conjeturas en tiempo real) y texto finalizado, lo que permite una sincronización fluida entre la reproducción de texto y audio. Cuando se obtiene un resultado final, se borra volatileTranscript y el resultado se agrega a finalizedTranscript para evitar duplicados. El resultado final también se escribe en el modelo Story para su uso posterior y se visualiza con formato condicional mediante las API AttributedString de SwiftUI. Configura la entrada de audio solicitando permiso, iniciando AVAudioSession y configurando AVAudioEngine para que devuelva un AsyncStream. El audio se escribe en el disco y se pasa al transcriptor después de convertirse al mejor formato de audio disponible. Al detener la grabación, se detienen el motor de audio y el transcriptor y se finalizan todos los resultados volátiles. TranscriptView muestra la concatenación de transcripciones finalizadas y volátiles durante la grabación y la transcripción final del modelo de datos durante la reproducción, con palabras resaltadas al ritmo del audio. En la app de ejemplo, se usa Apple Intelligence para generar un título para la historia usando la API FoundationModels, que muestra cómo se puede usar Apple Intelligence para realizar transformaciones útiles en la salida de voz a texto. Speech Framework permite el desarrollo de esta app con un tiempo de inicio mínimo y se pueden encontrar más detalles en la documentación.