acrcloud android music recognition

Reconocimiento de música en Android con ACRCloud

¿Alguna vez has pensado crear tu propia aplicación de reconocimiento de música como Shazam o Soundhound? Pues ahora puedes, gracias a un servicio de reconocimiento de música online llamado ACRCloud.

En mi caso te voy a enseñar a hacerlo en Android con lenguaje Kotlin, pero también podrías integrarlo en iOS o en cualquier otra plataforma gracias a su API Web.

Lo mejor es que te puedes registrar en el plan gratuito para hacer tus primeras pruebas. Tiene un límite de 100 peticiones al día, suficiente para poder poner en marcha tu proyecto.

Registro y creación del proyecto

El primer paso que debemos seguir es registrarnos en su web de modo que podamos acceder a la consola.

Una vez aquí, veremos los distintos servicios que ofrecen.

acrcloud console

En nuestro caso nos interesa la parte de «Projects > Audio & Video recognition».

Aquí deberemos crear un nuevo proyecto pulsando «Create Project». Le asignamos un nombre y elegimos «Recorded audio», ya que vamos a reconocer la música que grabemos por el micrófono de nuestro dispositivo. Como «bucket» debemos elegir «ACRCloud Music». Opcionalmente podemos marcar «3rd party integration» si queremos que en los resultados de la música reconocida nos aporte enlaces de YouTube y Spotify, entre otros.

Una vez creado nos dará un «Access Key» y un «Access Secret». Ambos los utilizaremos en nuestro proyecto Android.

Descargar e integrar el SDK

El siguiente paso será descargar el SDK para Android desde el siguiente repositorio en GitHub.

Como veis, tenemos varios recursos: un proyecto de ejemplo en Java, un fichero de ejemplo en Kotlin, el código fuente de las librerías nativas, documentación y las propias librerías. Esto último es lo que nos interesa ahora mismo.

La carpeta «libs» está separada fundamentalmente en un SDK en Java (el fichero .jar) y código nativo en forma de librerías (ficheros .so) para las distintas arquitecturas.

Pues bien, vamos a crear un proyecto nuevo en blanco desde Android Studio.

El fichero .jar lo podemos mover ya a la carpeta «app/libs».

acrcloud-lib

Para las liberías nativas, primero deberemos crear una carpeta llamada jniLibs dentro de app/src/main/. Después copiamos dentro las carpetas de las arquitecturas que queramos, quedando la siguiente estructura:

jnilibs-acrcloud

La parte de código Android

Ya tenemos preparado el SDK. Ahora a desarrollar nuestra aplicación.

Lo primero que debemos saber es que vamos a necesitar permisos de micrófono para grabar el audio y de Internet para comunicarnos con el Servidor de ACRCloud. De modo que en el archivo manifest añadimos lo siguiente:

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

Como la grabación de audio es un permiso «peligroso», debemos solicitar acceso en tiempo de ejecución. Así que vamos a crear un botón en activity_main.xml para habilitar este permiso:

<Button
    android:id="@+id/permission"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Enable mic" />

Podemos mostrarlo u ocultarlo en función de si disponemos o no de este permiso. Además, solicitar el permiso al pulsar el botón. Una forma rápida y sencilla de implementarlo sería con estas dos funciones:

fun checkPermission() {
    if (ContextCompat.checkSelfPermission(applicationContext, Manifest.permission.RECORD_AUDIO) != 0) {
        permission.visibility = View.VISIBLE
        permission.setOnClickListener {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO), 100)
        }
    } else {
        permission.visibility = View.GONE
        hasPermission()
    }
}
 
fun hasPermission() {}
 
override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == 100) {
        checkPermission()
    }
}

Ahora, en caso de que sí tenga permiso de micrófono habilitado podemos comenzar a reconocer el audio. Así que volvemos al layout y creamos un botón y un textView donde almacenaremos el resultado.

<TextView
    android:id="@+id/result"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="21sp"
    android:textStyle="bold"
    android:layout_margin="8dp"/>
 
<Button
    android:id="@+id/recognize"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Recognize" />

Ahora podemos iniciar la configuración de ACRCloud copiando el siguiente código a nuestra Activity principal:

    private var mClient: ACRCloudClient? = null
 
    fun initAcrcloud() {
        val config = ACRCloudConfig()
 
        config.acrcloudListener = this
        config.context = this
 
        // Please create project in "http://console.acrcloud.cn/service/avr".
        // Please create project in "http://console.acrcloud.cn/service/avr".
        config.host = "XXXXXX"
        config.accessKey = "XXXXXX"
        config.accessSecret = "XXXXXX"
 
        config.recorderConfig.rate = 8000
        config.recorderConfig.channels = 1
 
        mClient = ACRCloudClient()
        if (BuildConfig.DEBUG) {
            ACRCloudLogger.setLog(true)
        }
        mClient!!.initWithConfig(config)
    }

De momento vamos a dejar en texto plano las variables host, accessKey y accessSecret, pero deberías plantearte ocultar estos campos en producción. Mira algunos ejemplos para ocultar tus claves.

Para iniciar el reconocimiento de la canción al pulsar el botón podrías hacer algo así:

    fun hasPermission() {
        recognize.setOnClickListener {
            startRecognition()
        }
    }
 
    fun startRecognition() {
        mClient?.let {
            if (it.startRecognize()) {
                result.text = "Recognizing..."
            } else {
                result.text = "Init error"
            }
        } ?: run {
            result.text = "Client not ready"
        }
    }

Ahora solo nos queda recibir el resultado. Para ello nuestra activity debe implementar la interfaz «IACRCloudListener». En el método «onResult()» recibiremos el callback de los resultados del reconocimiento. De modo que quedaría algo así:

class MainActivity : AppCompatActivity(), IACRCloudListener {
    override fun onResult(acrResult: ACRCloudResult?) {
        acrResult?.let {
            Log.d("ACR", "acr cloud result received: ${it.result}")
            handleResult(it.result)
        }
    }
 
    override fun onVolumeChanged(vol: Double) {
        Log.d("ACR", "volume changed $vol")
    }
 
    fun handleResult(acrResult: String) {}

Si ejecutamos el programa veremos por consola que el resultado es un string en formato JSON. Es por ello que podemos realizar la transformación para poder leer los datos que queramos. Podemos ver un ejemplo del resultado del JSON en la siguiente página de documentación de ACRCloud.

Supongamos que solo nos interesa el título y el artista principal del primer resultado, que es además el más relevante. Para ello haríamos lo siguiente:

    fun handleResult(acrResult: String) {
        var res = ""
        try {
            val json = JSONObject(acrResult)
            val status: JSONObject = json.getJSONObject("status")
            val code = status.getInt("code")
            if (code == 0) {
                val metadata: JSONObject = json.getJSONObject("metadata")
                if (metadata.has("music")) {
                    val musics = metadata.getJSONArray("music")
                    val tt = musics[0] as JSONObject
                    val title = tt.getString("title")
                    val artistt = tt.getJSONArray("artists")
                    val art = artistt[0] as JSONObject
                    val artist = art.getString("name")
 
                    res = "$title ($artist)"
                }
            } else {
                // TODO: Handle error
                res = acrResult
            }
        } catch (e: JSONException) {
            res = "Error parsing metadata"
            Log.e("ACR", "JSONException", e)
        }
 
        result.text = res
    }

Solo nos queda liberar el cliente de ACRCloud al salir de la aplicación, en el método onDestroy:

    override fun onDestroy() {
        super.onDestroy()
        mClient?.let { 
            it.release()
            mClient = null
        }
    }

¡Y eso es todo! Con esto cuando pulsemos el botón nuestra app se pondrá a escuchar. Una vez tengamos el resultado del reconocimiento disponible se mostrará en el campo de texto.

acrcloud android result

Ahora solo nos quedaría mejorar nuestra app con un diseño único, algún indicador de progreso cuando está reconociendo la música, gestionar correctamente los errores de reconocimiento, la opción de cancelar un reconocimiento iniciado… Lo que se te ocurra para crear una app fantástica e inigualable.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.