El autor del artículo es el programador polaco Tomek Rekavek, quien está desarrollando el proyecto Jackrabbit Oak como parte de la Apache Software Foundation para Adobe. El artículo fue publicado en el blog personal del autor el 24 de febrero de 2016.La "Radio-3" polaca (la llamada "Troika") es famosa por su buena música y sus presentadores inteligentes. Por otro lado, sufre la presencia de bloques de anuncios ruidosos y molestos en las transmisiones, que generalmente anuncian algún tipo de electrónica o medicina. Escucho a Troika casi constantemente en el trabajo y en casa, así que me preguntaba: ¿cómo eliminar anuncios? Creo que logré encontrar una solución.
Procesamiento de señal digital
Mi objetivo es crear una aplicación que silencie los anuncios. El bloqueo comercial
comienza y
termina con jingles, por lo que el programa debe reconocer estos sonidos específicos y desactivar el sonido entre ellos.
Sé que esta área de las matemáticas / informática se llama
procesamiento de señal digital , pero DSP siempre me pareció mágico. Bueno, una gran oportunidad para aprender algo nuevo. Pasé un día o dos tratando de averiguar qué mecanismo usar para analizar la transmisión de audio. Y al final, encontré lo que necesitaba: es
correlación cruzada o correlación cruzada (correlación cruzada).
Octava
Por lo general, todos se refieren a la implementación de MATLAB. Pero MATLAB es una aplicación costosa que simplifica la ejecución de operaciones matemáticas complejas, incluido DSP. Afortunadamente, hay una alternativa gratuita llamada
Octave . Parece que en Octave es fácil ejecutar la correlación cruzada en dos archivos de audio. Solo es necesario ejecutar los siguientes comandos:
pkg load signal jingle = wavread('jingle.wav')(:,1); audio = wavread ('audio.wav')(:,1); [R, lag] = xcorr(jingle, audio); plot(R);
Obtiene la siguiente tabla:

Es claramente visible un pico que describe la posición de
jingle.wav
en
audio.wav
. Lo que me sorprendió fue la simplicidad del método:
xcorr()
hace todo el trabajo, el resto del código es solo para leer archivos y mostrar el resultado.
Quería implementar el mismo algoritmo en Java, y luego tendré una herramienta que:
- lee la transmisión de audio desde la entrada estándar (por ejemplo, desde ffmpeg),
- lo analiza en busca de jingles,
- imprime el mismo flujo en stdout y / o lo deshabilita.
El uso de stdin y stdout le permitirá conectar el nuevo
analizador a otras aplicaciones responsables de la transmisión de audio y la reproducción del resultado.
Leer archivos de sonido
Lo primero que debe leer un programa Java es un jingle (guardado como un archivo
.wav
) en una matriz. Hay información adicional en el archivo, como encabezados, metadatos y más, pero solo necesitamos sonido. Un formato adecuado se llama PCM, es solo una lista de números que representan sonidos. Convertir WAV a PCM puede ffmpeg:
ffmpeg -i input.wav -f s16le -acodec pcm_s16le output.raw
Aquí, cada muestra se guarda como un número de 16 bits con orden de bytes inverso (little endian). En Java, este número se llama
short
, y puede usar la clase
ByteBuffer
para convertir automáticamente la secuencia de entrada a una lista de valores
short
:
ByteBuffer buf = ByteBuffer.allocate(4); buf.order(ByteOrder.LITTLE_ENDIAN); buf.put(bytes); short leftChannel = buf.readShort();
Ingeniería inversa Xcorr
Para implementar la función
xcorr()
en Java, estudié el
código fuente de Octave. Sin cambiar el resultado final, pude reemplazar la llamada xcorr () con las siguientes líneas: deben reescribirse en Java:
N = length(audio); M = 2 ^ nextpow2(2 * N - 1); pre = fft(postpad(prepad(jingle(:), length(jingle) + N - 1), M)); post = fft(postpad(audio(:), M)); cor = ifft(pre .* conj(post)); R = real(cor(1:2 * N));
Parece aterrador, pero la mayoría de las funciones son operaciones triviales con matrices. La correlación cruzada se basa en la aplicación de la
transformada rápida de Fourier en una muestra de sonido.
Transformada rápida de Fourier
Como una persona que no tiene experiencia con DSP, solo veo FFT como una función que toma una matriz con una descripción de una muestra de sonido, y devuelve una matriz con números complejos que representan frecuencias. Este enfoque minimalista funcionó bien:
lancé la implementación de FFT desde el paquete
JTransforms y obtuve los mismos resultados que en Octave. Creo que esto es en parte un
culto a la carga , pero maldita sea, ¡funciona!
Ejecutar xcorr en un hilo
El algoritmo anterior supone que el
audio
es la matriz en la que estamos buscando
jingle
. Esto no es del todo adecuado para la transmisión, donde tenemos un flujo continuo de sonido. Para ejecutar el análisis, creé un buffer cíclico un poco más largo que la duración del jingle para ser reconocido. El flujo entrante llena el búfer y, tan pronto como está lleno, se ejecuta la prueba de correlación cruzada. Si no se encuentra nada, la parte más antigua del búfer se descarta, y nuevamente esperamos que se llene.
Experimenté un poco con la longitud del búfer y obtuve los mejores resultados con un tamaño de búfer de 1,5 veces el tamaño del jingle.
Poniendo todo junto
Obtener una transmisión en formato PCM es fácil. Esto se puede hacer usando el
ffmpeg
anterior. El siguiente comando redirige el flujo a la entrada estándar de
java
y luego genera
Got jingle 0
u
Got jingle 1
cuando se encuentra el patrón correspondiente en el flujo.
ffmpeg -loglevel -8 \ -i http://stream3.polskieradio.pl:8904/\;stream \ -f s16le -acodec pcm_s16le - \ | java -jar target/analyzer-1.0.0-SNAPSHOT-jar-with-dependencies.jar \ 2 \ src/test/resources/commercial-start-44.1k.raw 500 \ src/test/resources/commercial-end-44.1k.raw 700
Versión independiente
También preparé una versión simple e independiente del analizador, que se conecta a la corriente de Troika (sin
ffmpeg
externo) y reproduce el resultado usando
javax.sound
. Todo encaja en un solo archivo JAR y contiene una interfaz de usuario básica con los botones Star y Stop. Se puede descargar
aquí . Si no le gusta ejecutar JAR de otras personas en su máquina (lo cual es absolutamente correcto), entonces todas las fuentes están en
GitHub .
Todo parece
funcionar como debería :)
Trabajo adicional
El objetivo final es deshabilitar los anuncios a nivel de un amplificador de hardware, recibir una señal FM "real" y no alguna transmisión de Internet. Esto se describe en el
siguiente artículo .
Actualización (junio de 2018)
Discusión en Hacker NewsDiscusión sobre WykopDiscusión sobre Reddit