O autor do artigo é o programador polonês Tomek Rekavek, que está desenvolvendo o projeto Jackrabbit Oak como parte da Apache Software Foundation for Adobe. O artigo foi publicado no blog pessoal do autor em 24 de fevereiro de 2016.O “Radio-3” polonês (a chamada “Troika”) é famoso pela boa música e pelos apresentadores inteligentes. Por outro lado, sofre da presença de blocos de anúncios altos e irritantes nas transmissões, que geralmente anunciam algum tipo de eletrônica ou medicamento. Eu ouço Troika quase constantemente no trabalho e em casa, então me perguntei: como remover anúncios? Eu acho que consegui encontrar uma solução.
Processamento de sinal digital
Meu objetivo é criar um aplicativo que mude os anúncios. O bloco comercial
começa e
termina com jingles; portanto, o programa deve reconhecer esses sons específicos e desativar o som entre eles.
Eu sei que essa área da matemática / ciência da computação é chamada
processamento de sinal digital , mas o DSP sempre me pareceu mágico. Bem, uma ótima oportunidade para aprender algo novo. Passei um dia ou dois tentando descobrir qual mecanismo usar para analisar o fluxo de áudio. E no final, encontrei o que precisava: é
correlação cruzada ou correlação cruzada (correlação cruzada).
Octave
Geralmente, todo mundo se refere à implementação do MATLAB. Mas o MATLAB é um aplicativo caro que simplifica a execução de operações matemáticas complexas, incluindo o DSP. Felizmente, existe uma alternativa gratuita chamada
Octave . Parece que no Octave é fácil executar correlação cruzada em dois arquivos de áudio. Só é necessário executar os seguintes comandos:
pkg load signal jingle = wavread('jingle.wav')(:,1); audio = wavread ('audio.wav')(:,1); [R, lag] = xcorr(jingle, audio); plot(R);
Você obtém o seguinte gráfico:

É claramente visível um pico que descreve a posição de
jingle.wav
em
audio.wav
. O que me surpreendeu foi a simplicidade do método:
xcorr()
faz todo o trabalho, o restante do código é apenas para ler arquivos e exibir o resultado.
Eu queria implementar o mesmo algoritmo em Java e, em seguida, terei uma ferramenta que:
- lê o fluxo de áudio da entrada padrão (por exemplo, de ffmpeg),
- analisa-o em busca de jingles,
- imprime o mesmo fluxo em stdout e / ou o desativa.
O uso de stdin e stdout permitirá conectar o novo
analisador a outros aplicativos responsáveis pela transmissão de áudio e pela reprodução do resultado.
Lendo arquivos de som
A primeira coisa que um programa Java deve ler é um jingle (salvo como um arquivo
.wav
) em uma matriz. Existem algumas informações adicionais no arquivo, como cabeçalhos, metadados e muito mais, mas precisamos apenas de som. Um formato adequado é chamado PCM, é apenas uma lista de números que representam sons. Converter WAV em PCM pode ffmpeg:
ffmpeg -i input.wav -f s16le -acodec pcm_s16le output.raw
Aqui, cada amostra é salva como um número de 16 bits com ordem de bytes reversa (little endian). Em Java, esse número é chamado
short
, e você pode usar a classe
ByteBuffer
para converter automaticamente o fluxo de entrada em uma lista de valores
short
:
ByteBuffer buf = ByteBuffer.allocate(4); buf.order(ByteOrder.LITTLE_ENDIAN); buf.put(bytes); short leftChannel = buf.readShort();
Xcorr engenharia reversa
Para implementar a função
xcorr()
em Java, estudei o
código fonte do Octave. Sem alterar o resultado final, consegui substituir a chamada xcorr () pelas seguintes linhas - elas precisam ser reescritas em 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 assustador, mas a maioria das funções são operações triviais com matrizes. A correlação cruzada é baseada na aplicação da
transformada rápida de Fourier em uma amostra de som.
Transformação rápida de Fourier
Como uma pessoa que não tem experiência com DSP, apenas vejo a FFT como uma função que pega uma matriz com a descrição de uma amostra de som - e retorna uma matriz com números complexos que representam frequências. Essa abordagem minimalista funcionou bem: lancei a implementação da FFT a partir do pacote
JTransforms e obtive os mesmos resultados que no Octave. Eu acho que isso é parcialmente um
culto à carga , mas droga, funciona!
Executando o xcorr em um thread
O algoritmo acima pressupõe que o
audio
é a matriz na qual estamos procurando
jingle
. Isso não é totalmente adequado para transmissão, onde temos um fluxo contínuo de som. Para executar a análise, criei um buffer cíclico um pouco mais do que a duração do jingle a ser reconhecido. O fluxo de entrada preenche o buffer e, assim que estiver cheio, o teste de correlação cruzada é executado. Se nada for encontrado, a parte mais antiga do buffer será descartada - e novamente esperamos que seja preenchido.
Eu experimentei um pouco o comprimento do buffer e obtive os melhores resultados com um tamanho de buffer de 1,5 vezes o tamanho do jingle.
Juntando tudo
Obter um fluxo no formato PCM é fácil. Isso pode ser feito usando o
ffmpeg
acima. O comando abaixo redireciona o fluxo para a entrada
java
padrão e, em seguida, gera o
Got jingle 0
ou o
Got jingle 1
quando o padrão correspondente é encontrado no fluxo.
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
Versão autônoma
Também preparei uma versão simples e simples do analisador, que se conecta ao fluxo da Troika (sem
ffmpeg
externo) e reproduz o resultado usando
javax.sound
. Tudo se encaixa em um único arquivo JAR e contém uma interface básica com os botões Estrela e Parar. Pode ser baixado
aqui . Se você não gosta de executar JARs de outras pessoas na sua máquina (o que é absolutamente correto), todas as fontes estão no
GitHub .
Tudo parece
funcionar como deveria :)
Trabalho adicional
O objetivo final é desativar os anúncios no nível de um amplificador de hardware, recebendo um sinal de FM "real" e não algum fluxo da Internet. Isso é descrito no
próximo artigo .
Atualização (junho de 2018)
Discussão em Hacker NewsDiscussão sobre WykopDiscussão no Reddit