
-
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…
¡Hola! Soy Donovan, ingeniero del equipo de la estructura Speech. Y yo soy Shantini, ingeniera del equipo de Notas. Este año, presentamos la próxima evolución de la API y tecnología de detección de voz: SpeechAnalyzer. Hablaremos de la API SpeechAnalyzer y sus conceptos más importantes. Como también de algunas de las nuevas funcionalidades del modelo de la API. Y haremos una demo de codificación en vivo de cómo usar la API. SpeechAnalyzer ya potencia funcionalidades de muchas apps del sistema, como Notas, Notas de Voz, Diario y más.
Cuando combinamos SpeechAnalyzer y Apple Intelligence, creamos funcionalidades potentes, como Resumen de Llamadas. Luego te mostraré cómo crear tu propia funcionalidad de transcripción en vivo con la API. Primero, Donovan te describirá la nueva API SpeechAnalyzer. La conversión de voz a texto o reconocimiento automático de voz, ASR, es una tecnología versátil que crea una gran experiencia de usuario convirtiendo la voz en tiempo real o grabada en texto fácil de mostrar o interpretar por un dispositivo. Las apps almacenan, buscan o transmiten ese texto en tiempo real o lo envían a un gran modelo de lenguaje basado en texto.
En iOS 10, presentamos SFSpeechRecognizer. Esa clase te dio acceso al modelo de voz a texto de Siri. Funcionó bien para dictados cortos y podía usar servidores Apple en dispositivos con recursos limitados. Pero no resolvía algunos casos de uso tan bien como queríamos y dependía del usuario para agregar idiomas. En iOS 26, presentamos una nueva API para todas las plataformas, llamada SpeechAnalyzer, que admite más casos de uso y los ejecuta mejor. La nueva API usa el poder de Swift para procesar voz a texto y administrar los activos del modelo en el dispositivo del usuario con muy poco código. Además, ofrecemos un nuevo modelo de conversión de voz a texto que ya funciona en las apps en nuestras plataformas. El nuevo modelo es más rápido y flexible que el disponible a través de SFSpeechRecognizer. Es ideal para audio de larga duración y a distancia, como conferencias, reuniones y conversaciones. Debido a estas mejoras, Apple usa este nuevo modelo (y nueva API) en Notas y en otras apps que mencionamos antes. Puedes usar estas nuevas capacidades para crear tu propia app con las mismas funcionalidades de conversión de voz a texto de Notas y nuestras otras apps. Pero primero, veamos el diseño de la API. La API consta de la clase SpeechAnalyzer junto con varias otras clases. La clase SpeechAnalyzer administra una sesión de análisis. Puedes agregar una clase “módulo” a la sesión para ejecutar un análisis específico. Agregar un módulo transcriptor a la sesión la convierte en una sesión de transcripción que procesa de voz a texto. Los búferes de audio se pasan al analizador, que los conduce del transcriptor a su modelo de conversión de voz a texto. El modelo predice el texto que corresponde al audio y devuelve ese texto, junto con algunos metadatos, a tu app.
Funciona de forma asíncrona. Tu app puede agregar audio a medida que esté disponible en una tarea y mostrar o procesar los resultados en otra tarea aparte. Las secuencias asíncronas de Swift almacenan y desacoplan la entrada y los resultados.
La sesión “Conoce AsyncSequence” de la WWDC21 cubre cómo proveer una secuencia de entrada y cómo leer la secuencia de resultados.
La API usa el código de tiempo del audio pertinente para relacionar la entrada con los resultados. Las operaciones de la API se programan con códigos de tiempo en la línea de tiempo de audio, por lo que su orden es predecible y no depende de su ejecución. Los códigos de tiempo son precisos hasta en una muestra de audio individual. Mira cómo el transcriptor entrega resultados en secuencia. Cada uno cubre su propio rango de audio sin solaparse. Así es como suele funcionar. Pero, como opción, la transcripción puede ser iterativa en un rango de audio. Puede ser útil para dar una respuesta más inmediata en la IU de la app. Puedes mostrar un resultado inicial de inmediato y, a los segundos, mostrar mejores iteraciones de ese resultado. Les decimos “resultados volátiles” a los resultados iniciales. Se entregan casi tan pronto como se dicen, pero son conjeturas menos precisas. Sin embargo, mejora su transcripción a medida que obtiene más audio con más contexto. Al final, el resultado será lo mejor posible y el transcriptor entregará un último “resultado final”. Una vez que hace eso, el transcriptor no entregará más resultados para este rango de audio y pasará al siguiente rango. Mira cómo los códigos de tiempo muestran que los resultados mejorados reemplazan los anteriores. Esto solo sucede cuando habilitas resultados volátiles. Normalmente, el transcriptor solo entrega resultados finales y ninguno reemplaza los anteriores. Puedes crear una opción de transcripción en una sola función si lo que necesitas es leer el archivo y devolver la transcripción. Es un trabajo que no necesita manejo de resultados volátiles ni mucha concurrencia. Aquí está la función. Aquí creamos el módulo transcriptor. Le indicamos la ubicación a la que queremos transcribir. Aún no tiene resultados, pero los leeremos a medida que lleguen y usaremos la versión AsyncSequence de “reduce” para concatenarlos. Haremos esto en segundo plano con “async let”. Aquí creamos el analizador y agregamos el módulo transcriptor. Luego analizaremos el archivo. El método analyzeSequence lee el archivo y agrega su audio a una secuencia de entrada. Tras leer el archivo, le decimos al analizador que termine, porque no tenemos previsto trabajar con más audio. Finalmente, devolvemos la transcripción que estamos ensamblando en segundo plano. Serán las palabras habladas en el archivo, como una cadena con un solo atributo. Y terminamos. Ya vimos los conceptos y el uso básico de la API. Agregas módulos a una sesión de análisis para realizar la transcripción. Funciona concurrente y asíncronamente, desacoplando la entrada de audio de los resultados. Correlaciona audio, resultados y operaciones con la línea de tiempo de audio de la sesión. Algunos de los resultados son volátiles, si así deseamos, y el resto son definitivos y no cambiarán. Y mostré cómo las piezas encajan en un caso de uso de una sola función. Shantini demostrará cómo se puede expandir el trabajo de esa función a diferentes vistas, modelos y modelos de vista. Te mostrará varios métodos y propiedades de las clases SpeechAnalyzer y Transcriber que puedes usar para ocuparte de algunas necesidades comunes. También podrás leer sobre esto en la documentación. Ahora, describiremos algunas ventajas del nuevo modelo de conversión de voz a texto de la clase SpeechTranscriber. SpeechTranscriber funciona con un nuevo modelo diseñado por Apple para admitir un amplio espectro de casos de uso. Queríamos un modelo que admitiera casos de uso conversacionales y de larga duración en los que los oradores no están cerca del micrófono, como al grabar una reunión. También queríamos habilitar transcripciones en vivo que exijan baja latencia sin sacrificar la precisión ni la legibilidad, y mantener la privacidad del discurso. El nuevo modelo en el dispositivo logra todo eso. Colaboramos con socios internos para diseñar una gran experiencia para los desarrolladores. Y ahora puedes admitir los mismos casos de uso en tus propias apps. Con SpeechTranscriber, obtienes un modelo de conversión de voz a texto que no tienes que adquirir ni administrar por tu cuenta. Solo instala los activos relevantes del modelo con la nueva API AssetInventory. Podrás descargarlos cuando los necesites. El modelo se guarda en el almacenamiento del sistema y no aumenta el tamaño de descarga o almacenamiento de tu app. Tampoco aumenta el tamaño de la memoria en tiempo de ejecución. Opera fuera del espacio de memoria de tu app, por lo que no tienes que preocuparte por exceder el límite de tamaño. Mejoramos constantemente el modelo y el sistema instala automáticamente las actualizaciones cuando estén disponibles. SpeechTranscriber puede transcribir estos idiomas y otros por venir, y está disponible para todas las plataformas excepto watchOS, con ciertos requisitos de hardware. Si necesitas un idioma o dispositivo no compatible, ofrecemos otra clase de transcriptor: DictationTranscriber. Admite los mismos idiomas, modelo de conversión de voz a texto y dispositivos que SFSpeechRecognizer en el dispositivo iOS 10. Pero NO necesitas decirles a tus usuarios que vayan a Configuración y activen Siri o el dictado del teclado para cualquier idioma en particular. Esta es la introducción a la nueva API y modelo. Bastante abstracto, pero ya lo volveremos concreto. Vayamos con Shantini, te mostrará cómo integrar SpeechAnalyzer en tu app. ¡Gracias por ese gran resumen, Donovan! Quizás ya viste las funcionalidades que agregamos a Notas en iOS 18 para grabar y transcribir llamadas telefónicas, audio en tiempo real y grabado. Además, las integramos con Apple Intelligence para obtener resúmenes útiles y eficientes. Colaboramos con el equipo de Speech para conseguir que SpeechAnalyzer y SpeechTranscriber nos permitan ofrecer una funcionalidad Notas de alta calidad. SpeechTranscriber es una gran opción porque es rápida, precisa incluso a grandes distancias y en el dispositivo. Una de nuestras metas adicionales fue permitirte a ti, el desarrollador, crear funcionalidades como las de Notas y personalizarlas a las necesidades de tus usuarios. Me encantaría empezar con eso. Veamos una app que estoy creando con transcripción en tiempo real. Mi app es para niños, y graba y transcribe cuentos para dormir que luego puedes reproducir. Aquí están los resultados de la transcripción en tiempo real.
Cuando reproduces el audio, se resalta el segmento de texto correspondiente, para que puedan seguir la historia. Veamos la configuración del proyecto.
En el código de muestra, tengo una clase Recorder y una SpokenWordTranscriber. Las hice a ambas observables.
Hice este modelo de historia para encapsular la transcripción y otros detalles relevantes para mostrar. Aquí tengo la vista de transcripción, con la transcripción y reproducción en tiempo real, y botones de grabación y reproducción. También administra el estado de grabación y reproducción. Primero veamos la transcripción. La podemos ajustar en tres pasos: Configurar SpeechTranscriber, garantizar la presencia del modelo y manejar los resultados. Inicializamos SpeechTranscriber con un objeto de configuración regional y las opciones que necesitamos. El código de idioma de la configuración regional corresponde al idioma de la transcripción. Como se mencionó, los resultados volátiles son conjeturas inmediatas y los resultados finales son las mejores conjeturas. Aquí se usan ambos, los resultados volátiles en una opacidad más clara y se sustituyen por los finales cuando llegan. Para configurarlo en nuestro SpeechTranscriber, configuraremos estos tipos de opciones. Agrego la opción audioTimeRange para que obtengamos información de tiempo.
Nos permite sincronizar la reproducción del texto con el audio.
También hay ajustes preestablecidos que ofrecen diferentes opciones.
Ahora vamos a configurar el objeto SpeechAnalyzer con nuestro módulo SpeechTranscriber.
Esto nos permite obtener el formato de audio que necesitamos usar.
Y podemos garantizar que el modelo de conversión de voz a texto esté implementado.
Por último, queremos guardar las referencias a la entrada AsyncStream e iniciar el analizador.
Ahora que terminamos de configurar SpeechTranscriber, veamos cómo obtenemos los modelos.
En el método ensureModel, agregaremos controles para comprobar si SpeechTranscriber admite la transcripción del idioma que queremos.
También comprobaremos si el idioma está descargado e instalado.
Si el idioma es compatible pero no se descarga, podemos continuar y enviar una solicitud a AssetInventory para descargar el soporte.
La transcripción se realiza totalmente en el dispositivo, pero los modelos deben obtenerse.
La solicitud de descarga incluye un objeto “progress” que puedes usar para que tu usuario sepa lo que está pasando.
Tu app puede tener soporte para una cantidad limitada de idiomas a la vez. Si excede el límite, puedes solicitarle a AssetInventory que desasigne uno o más de ellos para liberar un lugar.
Ya tenemos nuestros modelos, pasemos a la parte divertida: los resultados.
Junto a nuestro código, estoy creando una tarea y guardando una referencia.
Y estoy creando dos variables para rastrear los resultados volátiles y finales.
SpeechTranscriber devuelve resultados a través de AsyncStream. Cada objeto de resultado tiene algunos campos diferentes.
El primero que queremos es “text”, que lo representa un AttributedString. Este es el resultado de la transcripción de un segmento de audio. Cada vez que obtenemos un resultado en el flujo, verificaremos si es volátil o final con la propiedad “isFinal”.
Si es volátil, lo guardaremos en volatileTranscript.
Cuando obtenemos un resultado final, borramos el volatileTranscript y agregamos el resultado a finalizedTranscript.
Si no eliminamos los resultados volátiles, podríamos terminar con duplicados.
Cuando obtenemos un resultado final, también lo escribiré en el modelo de historia para usarlo después.
También configuro algún formato condicional con las API AttributedString de SwiftUI.
Así podremos visualizar los resultados de la transcripción a medida que pasan de volátiles a finales.
Si te preguntas cómo obtendré los datos de tiempo de la transcripción, son parte de la cadena atribuida.
Cada ejecución tiene un atributo “audioTimeRange” representado como CMTimeRange. Lo usaré en mi código de vista para resaltar el segmento correcto. Ahora veamos cómo configurar la entrada de audio.
En mi función de grabar, que invoco cuando se presiona “Grabar”, solicito permiso de audio e iniciar AVAudioSession. También garantizamos que la app está configurada para usar el micrófono en la configuración del proyecto. Luego invoco la función setUpTranscriber que creamos antes.
Por último, manejo la entrada de mi transmisión de audio. Veamos cómo lo configuré. Están pasando varias cosas aquí: Configuramos AVAudioEngine para devolver una transmisión asíncrona y pasar los búferes entrantes a la transmisión.
También grabamos el audio en disco.
Finalmente, iniciamos audioEngine.
En mi función de grabar, estoy pasando la entrada AsyncStream al transcriptor.
Las fuentes de audio tienen varios formatos de salida y rangos de muestra. SpeechTranscriber nos dio bestAvailableAudioFormat que podemos usar.
Paso los búferes de audio por un paso de conversión para asegurarme de que coincide con bestAvailableAudioFormat.
Luego enrutaré la transmisión asíncrona al objeto inputBuilder desde SpeechTranscriber. Cuando dejamos de grabar, queremos hacer algunas cosas. Detuve el motor de audio y el transcriptor. Es importante cancelar tus tareas e invocar “finalize” en tu flujo de analizador. Esto garantiza que se concreten todos los resultados volátiles. Veamos cómo se conecta todo esto a nuestra vista.
Mi TranscriptView tiene un enlace a la historia actual y uno a nuestro SpokenWordTranscriber. Si grabamos, aparece una concatenación de la transcripción final con la transcripción volátil que vemos desde nuestra clase SpokenWordTranscriber. Para la reproducción, mostramos la transcripción final del modelo de datos. Agregué un método para dividir las frases y reducir el desorden visual.
Una función clave que mencioné fue resaltar cada palabra mientras se reproduce. Uso algunos métodos auxiliares para calcular si cada ejecución debe resaltarse según su atributo audioTimeRange y el tiempo de reproducción actual.
La precisión de SpeechTranscriber es estupenda por muchas razones, entre ellas la de usar Apple Intelligence para realizar transformaciones útiles en la salida.
Aquí uso la nueva API FoundationModels para cuando termine mi historia. La API facilita la creación de títulos ingeniosos, así no tengo que pensar en uno. Si quieres saber más sobre las API FoundationModels, revisa la sesión “Conoce la estructura FoundationModels”.
¡Veamos nuestra funcionalidad en acción! Toco el botón + para crear una nueva historia.
Luego empiezo a grabar. “Había una vez en la mística tierra de Magenta, una niña llamada Delilah que vivía en un castillo en la colina. Delilah pasaba sus días explorando el bosque y cuidando a los animales”.
Cuando el usuario termina, puede reproducirlo y cada palabra se resalta al ritmo del audio.
Había una vez, en la mística tierra de Magenta, una niña llamada Delilah que vivía en un castillo en la colina.
Delilah pasaba sus días explorando el bosque y cuidando a los animales. Gracias a SpeechAnalyzer y SpeechTranscriber, pudimos crear una app completa en muy poco tiempo. Consulta la documentación de la estructura Speech si quieres revisar la app de ejemplo que creamos. ¡Y esto es SpeechAnalyzer! Sabemos que crearás funcionalidades increíbles. ¡Gracias por acompañarnos!
-
-
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.