Advertisement
  1. Code
  2. iOS 10

Usando la API de Reconocimiento de Voz en iOS 10

Scroll to top
Read Time: 14 min

Spanish (Español) translation by Rafael Chavarría (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

Introducción

Siri ha sido una característica nuclear de iOS desde que fue presentada en 2011. Ahora, iOS 10 trae nuevas características para permitir a los desarrolladores interactuar con Siri. En particular, dos nuevos frameworks están ahora disponibles: Speech y SiriKit.

Hoy, vamos a echar un vistazo al framework Speech, que nos permite traducir fácilmente audio a texto. Aprenderás cómo construir una aplicación de la vida real que usa la API de reconocimiento de voz para revisar el estado de un vuelo.

Si quieres aprender más sobre SiriKit, lo cubrí en mi tutorial Crea Extensiones SiriKit en iOS 10. Para más sobre otras nuevas características para desarrolladores en iOS 10, revisa el curso de Markus Mühlberger, justo aquí en Envato Tuts+.

Uso

El reconocimiento de voz es el proceso de traducir audio en vivo o pre-grabado a texto transcrito. Desde que Siri fue presentada en iOS 5, ha habido un botón de micrófono en el teclado del sistema que permite a los usuarios dictar fácilmente. Esta característica puede ser usada con cualquier entrada de texto UIKit, y no requiere que escribas código adicional más allá de lo que escribirías para soportar una entrada de texto estándar. Es realmente rápido y fácil de usar, pero viene con unas cuántas limitaciones:

  • El teclado siempre está presente cuando se dicta.
  • El lenguaje no puede ser personalizado por la app misma.
  • La app no puede ser notificada cuando el dictado comienza y termina.
Dictation in the iOS keyboardDictation in the iOS keyboardDictation in the iOS keyboard

Para permitir a los desarrolladores construir aplicaciones más personalizables y poderosas con la misma tecnología de dictado que Siri, Apple creó el framework Speech. Este permite a cada dispositivo que ejecute iOS 10 para traducir audio a texto en más de 50 lenguajes y dialectos.

Esta nueva API es mucho más poderosa porque no solo proporciona un simple sistema de transcripción, sino que también proporciona interpretaciones alternativas de lo que cada usuario pudiera haber dicho. Puedes controlar cuando la app detiene un dictado, puedes mostrar los resultados mientras tu usuario habla, y el motor de reconocimiento de voz se adaptará automáticamente a las preferencias del usuario (lenguaje, vocabulario, nombres, etc.).

Una característica interesante es el soporte para transcribir audio pre-grabado. Si estás construyendo una aplicación de mensajería instantánea, por ejemplo, podrías usar esta funcionalidad para transcribir el texto a nuevos mensajes de audio.

Configuración

Primero que todo, necesitarás pedir al usuario permiso para transmitir su voz a Apple para análisis.

Dependiendo del dispositivo y el lenguaje que será reconocido, iOS podría debería decidir transparentemente transcribir el audio en el dispositivo mismo o, si el reconocimiento de voz local no está disponible en el dispositivo, iOS usará los servidores de Apple para hacer el trabajo.

Es por esto que una conexión activa a internet usualmente es requerida para reconocimiento de voz. Te mostraré como revisar la disponibilidad del servicio muy pronto.

Hay tres pasos para usar reconocimiento de voz:

  • Explica: dile a tu usuario por qué quieres acceder su voz.
  • Autoriza: pide autorización explícitamente para acceder su voz.
  • Solicita: carga un audio pre-grabado desde el disco usando SFSpeechURLRecognitionRequest, o transmite audio en vivo usando SFSpeechAudioBufferRecognitionRequest u procesa la transcripción.

Si quieres saber más sobre el framework Speech, mira la Sesión 509 de WWDC 2016. También puedes leer la documentación oficial.

Ejemplo

Ahora te mostraré cómo puedes construir una aplicación de la vida real que saca ventaja de la API de reconocimiento de voz. Vamos a construir una pequeña aplicación de seguimiento de vuelo en la cuál el usuario puede simplemente decir un número de vuelo, y la app mostrará el estado actual del vuelo. Si, ¡vamos a construir un pequeño asistente como Siri para revisar el estado de cualquier vuelo!

En el repositorio de GitHub del tutorial, he provisto un proyecto esqueleto que contiene una UI básica que nos ayudará para este tutorial. Descarga y abre el proyecto en Xcode 8.2 o superior. Comenzar con UI existente nos permitirá enfocarnos en la API de reconocimiento de voz.

Echa un vistazo a las clases en el proyecto. UIViewController+Style.swift contiene la mayoría del código responsable de actualizar la UI. El origen de datos de ejemplo de los vuelos mostrados en la tabla es declarado en FlightsDataSource.swift.

Si ejecutas el proyecto, este debería lucir como sigue:

The initial example projectThe initial example projectThe initial example project

Después de que el usuario presiona el botón de micrófono, queremos iniciar el reconocimiento de voz para transcribir el número de vuelo. Así que si el usuario dice "LX40", nos gustaría mostrar la información en relación a la puerta y el estado actual del vuelo. Para hacer esto, llamaremos a una función para buscar automáticamente el vuelo en una fuente de datos y mostrar el estado del vuelo.

Primero vamos a explorar cómo transcribir desde audio pre-grabado. Después, aprenderemos cómo implementar el más interesante reconocimiento de voz en vivo.

Comencemos configurando el proyecto. abre el archivo Info.plist y agrega una nueva fila con la explicación que será mostrada al usuario cuando se le pida permiso para acceder a su voz. La fila recién agregada está resaltada en azul en la siguiente imagen.

The Infoplist file with the newly added keyThe Infoplist file with the newly added keyThe Infoplist file with the newly added key

Una vez que está hecho, abre ViewController.swift. No te fijes en el código que ya está en esta clase; solo se está encargando de actualizar la UI por nosotros.

El primer paso con cualquier framework que quieres usar es importarlo en la parte superior del archivo.

1
import Speech

Para mostrar el diálogo de permiso al usuario, agrega este código en el método viewDidLoad(animated:):

1
switch SFSpeechRecognizer.authorizationStatus() {
2
    case .notDetermined:
3
        askSpeechPermission()
4
    case .authorized:
5
        self.status = .ready
6
    case .denied, .restricted:
7
        self.status = .unavailable
8
}

La variable status se encarga de cambiar la UI para advertir al usuario de que el reconocimiento de voz no está disponible en caso de que algo salga mal. Vamos a asignar un nuevo estado a la misma variable cada vez que quisiéramos cambiar la UI.

Si la app no ha pedido permiso al usuario aún, el estado de autorización será notDetermined, y llamaremos al método askSpeechPermission para pedirlo como está definido en el siguiente paso.

Deberías siempre fallar agraciadamente si una característica específica no está disponible. También es muy importante comunicar al usuario cuando estás grabando su voz. Nunca intentes reconocer su voz sin primero actualizar la UI y hacer a tu usuario consciente de ello.

Aquí está la implementación de la función para pedir permiso al usuario.

1
func askSpeechPermission() {
2
    SFSpeechRecognizer.requestAuthorization { status in
3
        OperationQueue.main.addOperation {
4
            switch status {
5
            case .authorized:
6
                self.status = .ready
7
            default:
8
                self.status = .unavailable
9
            }
10
        }
11
    }
12
}

Invocamos al método requestAuthorization para mostrar la petición de privacidad de reconocimiento de voz que agregamos al Info.plist. Después cambiamos al hilo principal en caso de que el cierre sea invocado en un hilo diferente--queremos actualizar la UI solo desde el hilo principal. Asignamos el nuevo status para actualizar el botón de micrófono para avisar al usuario la disponibilidad (o no) del reconocimiento de voz.

Reconocimiento de Audio Pre-Grabado

Antes de escribir el código para reconocer audio pre-grabado, necesitamos encontrar la URL para el archivo de audio. En el navegador de proyecto, revisa que tengas un archivo llamado LX40.m4a. Grabé este archivo yo mismo con la aplicación Voice Memos en mi iPhone diciendo "LX40". Podemos revisar fácilmente si tenemos una transcripción correcta de audio.

Almacena la URL de archivo de audio en una propiedad:

1
var preRecordedAudioURL: URL = { 
2
    return Bundle.main.url(forResource: "LX40", withExtension: "m4a")! 
3
}()

Es tiempo de ver finalmente el poder y simplicidad del framework Speech. Este es el código que hace todo el reconocimiento de voz por nosotros:

1
func recognizeFile(url: URL) {
2
    guard let recognizer = SFSpeechRecognizer(), recognizer.isAvailable else {
3
        return
4
    }
5
6
    let request = SFSpeechURLRecognitionRequest(url: url)
7
    recognizer.recognitionTask(with: request) { result, error in
8
        guard let recognizer = SFSpeechRecognizer(), recognizer.isAvailable else {
9
            return self.status = .unavailable
10
        }
11
        if let result = result {
12
            self.flightTextView.text = result.bestTranscription.formattedString
13
            if result.isFinal {
14
                self.searchFlight(number: result.bestTranscription.formattedString)
15
            }
16
        } else if let error = error {
17
            print(error)
18
        }
19
    }
20
}

Aquí está lo que está haciendo el método:

  • Inicializa una instancia de SFSpeechRecognizer y revisa que el reconocimiento de voz está disponible con una declaración de guardia. Si no está disponible, simplemente establecemos el estado a unavailable y devolvemos. (El inicializador por defecto usa el locale por defecto del usuario, pero puedes también usar el inicializador SFSpeechRecognizer(locale:) para proporcionar un locale diferente.)
  • Si el reconocimiento de voz está disponible, crea una instancia SFSpeechURLRecognitionRequest pasando la URL del audio pre-grabado.
  • Comienza el reconocimiento de voz invocando el método recognitionTask(with:) con la petición previamente creada.

El cierre será llamado múltiples veces con dos parámetros: un resultado y un objeto de error.

El recognizer está de hecho reproduciendo el archivo e intentando reconocer el texto incrementalmente. Por esta razón, el cierre es llamado varias veces. Cada vez que reconoce una letra o palabra o hace algunas correcciones, el cierre es invocado con objetos actualizados.

El objeto result tiene la propiedad isFinal establecida a true cuando el archivo de audio fue completamente analizado. En este caso, comenzamos una búsqueda en nuestra fuente de información de vuelo para ver si podemos encontrar un vuelo con el número de vuelo reconocido. La función searchFlight se encargará de mostrar el resultado.

La última cosa que nos falta es invocar la función recognizeFile(url:) cuando el botón de micrófono es presionado:

1
@IBAction func microphonePressed(_ sender: Any) {
2
    recognizeFile(url: preRecordedAudioURL)
3
}

Ejecuta la aplicación en tu dispositivo corriendo iOS 10, presiona el botón de micrófono, y verás el resultado. El audio "LX40" es reconocido de manera incremental, y el estado del vuelo es mostrado!

Consejo: El número de vuelo es mostrado en un UITextView. Cómo habrás notado, si habilitas el detector de datos del Número de Vuelo en el UITextView, ¡puedes presionar sobre este y el estado actual del vuelo se mostrará!

El código completo de ejemplo hasta este punto puede ser visto en la rama de audio-pre-grabado en GitHub.

Reconocimiento de Audio en Vivo

Veamos ahora cómo implementar reconocimiento de voz en vivo. Va a ser un poco más complicado comparado con lo que hicimos. Puedes nuevamente descargar el mismo esqueleto de proyecto y continuar.

Necesitamos una nueva llave en el  archivo Info.plist para explicar al usuario por qué necesitamos acceder al micrófono. Agrega una nueva fila para tu Info.plist como se muestra en la imagen.

New row explaining why we need to access the microphoneNew row explaining why we need to access the microphoneNew row explaining why we need to access the microphone

No necesitamos pedir permiso de manera manual al usuario porque iOS hará eso por nosotros tan pronto intentemos acceder cualquier API relacionada con el micrófono.

Podemos re-usar el mismo código que usamos en la sección previa (recuerda import_Speech) para pedir la autorización. El método viewDidLoad (animated:) es implementado exactamente como antes.

1
switch SFSpeechRecognizer.authorizationStatus() {
2
case .notDetermined:
3
    askSpeechPermission()
4
case .authorized:
5
    self.status = .ready
6
case .denied, .restricted:
7
    self.status = .unavailable
8
}

También, el método para pedir permiso al usuario es el mismo.

1
func askSpeechPermission() {
2
    SFSpeechRecognizer.requestAuthorization { status in
3
        OperationQueue.main.addOperation {
4
            switch status {
5
            case .authorized:
6
                self.status = .ready
7
            default:
8
                self.status = .unavailable
9
            }
10
        }
11
    }
12
}

La implementación de startRecording va a ser un poco diferente. Agreguemos primero unas cuantas variables de instancia que serán útiles cuando administremos la sesión de audio y tarea de reconocimiento de voz.

1
let audioEngine = AVAudioEngine() 
2
let speechRecognizer: SFSpeechRecognizer? = SFSpeechRecognizer() 
3
let request = SFSpeechAudioBufferRecognitionRequest() 
4
var recognitionTask: SFSpeechRecognitionTask?

Echemos un vistazo a cada variable de manera separada:

  • AVAudioEngine es usado para procesar un flujo de audio. Crearemos un nodo de audio y lo adjuntaremos a este motor para que podamos actualizarnos cuando el micrófono recibe algunas señales de audio.
  • SFSpeechRecognizer es la misma clase que hemos visto en la parte previa del tutorial, y se encarga de reconocer el habla. Dado que el inicializador puede fallar y devolver nil, los declaramos como opcional para evitar un fallo en tiempo de ejecución.
  • SFSpeechAudioBufferRecognitionRequest es un buffer usado para reconocer voz en vivo. Dado que no tenemos el archivo de audio completo como antes, necesitamos un buffer para asignar la voz mientras el usuario habla.
  • SFSpeechRecognitionTask administra la tarea actual de reconocimiento de voz y puede ser usado para detenerlo o cancelarlo.

Una vez que hemos declarado todas las variables requeridas, implementemos startRecording.

1
func startRecording() {
2
    // Setup audio engine and speech recognizer

3
    guard let node = audioEngine.inputNode else { return }
4
    let recordingFormat = node.outputFormat(forBus: 0)
5
    node.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { buffer, _ in
6
        self.request.append(buffer)
7
    }
8
9
    // Prepare and start recording

10
    audioEngine.prepare()
11
    do {
12
        try audioEngine.start()
13
        self.status = .recognizing
14
    } catch {
15
        return print(error)
16
    }
17
18
    // Analyze the speech

19
    recognitionTask = speechRecognizer?.recognitionTask(with: request, resultHandler: { result, error in
20
        if let result = result {
21
            self.flightTextView.text = result.bestTranscription.formattedString
22
            self.searchFlight(number: result.bestTranscription.formattedString)
23
        } else if let error = error {
24
            print(error)
25
        }
26
    })
27
}

Este es el código principal para nuestra característica. Lo explicaré paso a paso:

  • Primero obtenemos el inputNode del audioEngine. Un dispositivo posiblemente puede tener múltiples entradas de audio, y aquí seleccionamos la primera.
  • Le decimos al nodo de entrada que queremos monitorear el flujo de audio. El bloque que proporcionamos será invocado en cada flujo de audio recibido de 1024 bytes. Inmediatamente anexamos el buffer de audio al request para que puede comenzar el proceso de reconocimiento.
  • Preparamos el motor de audio para comenzar a grabar. Si la grabación comienza exitósamente, establecemos el estado a .recognizing de manera que actualicemos el botón de icono para hacer saber al usuario que su voz está siendo reconocida.
  • Asignemos el objeto devuelto de speechRecognizer.recognitionTask(with:resultHandler:) a la variable recognitionTask. Si el reconocimiento es exitoso, buscamos el vuelo en nuestra fuente de datos y actualizamos la UI.

La función para cancelar la grabación es tan simple como detener el motor de audio, remover la escucha del nodo de entrada, y cancelar la tarea de reconocimiento.

1
func cancelRecording() {
2
    audioEngine.stop()
3
    if let node = audioEngine.inputNode {
4
        node.removeTap(onBus: 0)
5
    }
6
    recognitionTask?.cancel()
7
}

Ahora solo necesitamos comenzar y detener la grabación. Modifica el método microphonePressed como sigue:

1
@IBAction func microphonePressed() {
2
    switch status {
3
    case .ready:
4
        startRecording()
5
        status = .recognizing
6
    case .recognizing:
7
        cancelRecording()
8
        status = .ready
9
    default:
10
        break
11
    }
12
}

Dependiendo del status actual, comenzamos o detenemos el reconocimiento de voz.

Construye y ejecuta la app para ver el resultado. Intenta deletrear cualquiera de los números de vuelo listados y deberías ver su estado aparecer.

Una vez más, el código de ejemplo puede ser visto en la rama live-audio en GitHub.

Mejores Prácticas

El reconocimiento de voz es una API muy importante que Apple proporcionó a desarrolladores iOS apuntando a iOS 10. Es completamente gratuito de usar, pero ten en mente que no es de uso ilimitado. Está limitado a cerca de un minuto por cada tarea de reconocimiento de voz, y tu app podría  también ser sofocada por los servidores de Apple si requiere mucho cómputo. Por estas razones, tiene un impacto alto sobre el tráfico de la red y uso de poder.

Asegúrate de que tus usuarios son instruidos propiamente sobre cómo usar el reconocimiento de voz, y ser tan transparente como sea posible cuando estás grabando su voz.

Recapitulación

En este tutorial, has visto cómo usar reconocimiento de voz rápido, preciso y flexible en iOS 10. Úsalo a tu favor para dar a tus usuarios una nueva manera de interactuar con tu app y mejorar su accesibilidad al mismo tiempo.

Si quieres aprender más sobre integrar Siri en tu app, o si quieres averiguar sobre algunas otras características interesantes de desarrollador de iOS 10, revisa el curso de Markus Mühlberger.

También, revisa algunos de nuestros otros tutoriales gratuitos sobre características de iOS 10.


Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.