4 min read

Leer y analizar metadatos de archivos de audio

Entrada corta.

El problema

Me interesa leer los metadatos de todos los archivos de audio en una carpeta. En este caso son audios de entrevistas que están en tres formatos diferente, .mp3, .m4a y .wav. Después de leerlos querría cargar esa información de manera sistemática en un data frame que tendrá una fila por cada archivo y una columna para el nombre del archivo, otra para la duración y una tercera para la fecha de creación.

Dentro de la paquetería de R no encontré ningúna librería que haga el trabajo. Sin embargo hay un programa que funciona en Linux (también en otras plataformas, aunque no probé) y que se encarga de leer metadatos de archivos de medios. Se llama mediainfo y tiene la opción de generar su output en formato XML, es decir, un formato que podría leer desde R.1

system y stdout

R tiene la función system, que se encarga de conectar a la consola de R con la consola de nuestro sistema operativo. Normalmente la usamos para pasar directamente desde un script instrucciones a la consola (crear o borrar un archivo). Sin embargo tiene otra característica interesante, también puede ejecutar un programa en BASH y capturar la salida de ese programa como un objeto de R. Esa “salida” está en un formato conocido llamado Standar Output o stdout. Una de las maravillas de UNIX.

La solución

Los nombres de archivo están censurados por cuestiones de privacidad.

library(rvest)
library(dplyr) # para mutate
library(lubridate) #Para manipular fechas.

# Lista con todos los archivos de audio
archivos <- list.files("~/audio_test", "*.mp3|*.m4a|*.wav", 
                       full.names = TRUE)

# Collapso a lista a una cadena de caracteres. 
# mediainfo acepta múltiples nombres de archivo separados por un espacio

archivos <- paste(archivos, collapse = " ")

# Leo los metadatos con una llamada externa a mediainfo, le especificio que la salida sea en XML
metadatos_crudos <- system(paste("mediainfo --output=XML", archivos), 
                           intern = TRUE) #R captura el stdout como un vector de cadenas de caracteres.

metadatos <- paste0(metadatos_crudos, collapse = "") %>%  #Colapso el vector de caracteres a una sola cadena. 
  read_html()  #Legible por read_html

# Navego las etiquetas XML y capturo la información que me interesa. 
# read_html::xml_structure() imprime la lista de etiquetas. 

audios <- data.frame(

   archivo = html_nodes(metadatos, "media") %>% 
     html_attr("ref"),
   
   formato = html_nodes(metadatos, xpath = '//*[@type="General"]') %>%  
     html_nodes("format") %>% 
     html_text(),
   
    duracion = html_nodes(metadatos, xpath = '//*[@type="General"]') %>% 
     html_nodes("duration") %>% 
     html_text() %>% 
     as.numeric(), #En segundos
   
   fecha = html_nodes(metadatos, "file_modified_date") %>%
     html_text() %>% 
     ymd_hms() %>% 
     date(), #Sale la fecha limpia
   
   stringsAsFactors = FALSE
   )

audios <- audios %>% 
  mutate(archivo = "Información privada")

knitr::kable(audios, caption = "Audios de entrevistas")
Table 1: Audios de entrevistas
archivo formato duracion fecha
Información privada MPEG-4 4108.794 2017-11-23
Información privada MPEG-4 5986.754 2017-11-23
Información privada MPEG-4 3100.582 2017-11-23
Información privada MPEG-4 4710.214 2017-10-03
Información privada MPEG Audio 2270.798 2017-10-26
Información privada MPEG-4 2776.704 2017-10-09
Información privada MPEG-4 5813.300 2017-10-03
Información privada MPEG-4 5989.586 2017-09-05
Información privada MPEG-4 5442.918 2017-09-05
Información privada MPEG-4 4904.000 2017-10-09
Información privada MPEG-4 1427.795 2017-11-13
Información privada MPEG-4 373.028 2017-11-26
Información privada MPEG-4 5595.288 2017-11-26
Información privada MPEG-4 2453.824 2017-10-15
Información privada MPEG-4 5047.168 2017-10-15
Información privada MPEG-4 1730.176 2017-10-15
Información privada MPEG-4 2045.696 2017-10-15
Información privada MPEG-4 3163.328 2017-10-15
Información privada MPEG-4 5408.064 2017-10-15
Información privada MPEG Audio 3245.453 2017-10-26
Información privada MPEG Audio 3895.797 2017-10-26
Información privada MPEG Audio 1404.055 2017-10-26
Información privada MPEG Audio 373.577 2017-10-26
Información privada MPEG Audio 2493.466 2017-10-26
Información privada MPEG Audio 1118.406 2017-10-26
Información privada MPEG-4 2414.595 2017-10-03
Información privada MPEG-4 2101.428 2017-10-03
Información privada MPEG-4 1472.143 2017-10-03
Información privada MPEG-4 1345.712 2017-10-03
Información privada MPEG-4 2334.208 2017-10-03
Información privada MPEG-4 2524.705 2017-10-03
Información privada MPEG-4 3032.317 2017-10-03
Información privada MPEG-4 3850.912 2017-10-03
Información privada MPEG-4 2638.855 2017-10-03
Información privada Wave 6712.293 2017-10-26
Información privada MPEG Audio 3350.752 2017-10-31
Información privada MPEG Audio 5097.639 2017-10-26
Información privada MPEG-4 391.808 2017-10-10
Información privada MPEG-4 1210.240 2017-10-10
Información privada MPEG-4 1392.704 2017-10-10
Información privada MPEG-4 122.304 2017-10-10

  1. No sin alguna dificultad. Para algunas operaciones hay utilizar la sintaxis xpath que es poco intuitiva.