Adblock für Radio

Der Autor des Artikels ist der polnische Programmierer Tomek Rekavek, der das Jackrabbit Oak- Projekt im Rahmen der Apache Software Foundation für Adobe entwickelt. Der Artikel wurde am 24. Februar 2016 im persönlichen Blog des Autors veröffentlicht.

Das polnische „Radio-3“ (die sogenannte „Troika“) ist berühmt für gute Musik und intelligente Moderatoren. Auf der anderen Seite leidet es unter dem Vorhandensein von lauten und nervigen Anzeigenblöcken in Sendungen, die normalerweise für irgendeine Art von Elektronik oder Medizin werben. Ich höre Troika fast ständig bei der Arbeit und zu Hause und habe mich gefragt: Wie entferne ich Anzeigen? Ich glaube, ich habe es geschafft, eine Lösung zu finden.

Digitale Signalverarbeitung


Mein Ziel ist es, eine Anwendung zu erstellen, die Anzeigen stummschaltet. Der kommerzielle Block beginnt und endet mit Jingles, daher sollte das Programm diese spezifischen Sounds erkennen und den Sound zwischen ihnen ausschalten.

Ich weiß, dass dieser Bereich der Mathematik / Informatik als digitale Signalverarbeitung bezeichnet wird , aber DSP schien mir immer magisch. Nun, eine großartige Gelegenheit, etwas Neues zu lernen. Ich habe ein oder zwei Tage damit verbracht, herauszufinden, mit welchem ​​Mechanismus der Audiostream analysiert werden soll. Und am Ende habe ich gefunden, was ich brauchte: Es ist Kreuzkorrelation oder Kreuzkorrelation (Kreuzkorrelation).

Oktave


Normalerweise bezieht sich jeder auf die Implementierung von MATLAB. MATLAB ist jedoch eine teure Anwendung, die die Ausführung komplexer mathematischer Operationen, einschließlich DSP, vereinfacht. Glücklicherweise gibt es eine kostenlose Alternative namens Octave . Es scheint, dass es in Octave einfach ist, eine Kreuzkorrelation für zwei Audiodateien durchzuführen. Es müssen nur die folgenden Befehle ausgeführt werden:

pkg load signal jingle = wavread('jingle.wav')(:,1); audio = wavread ('audio.wav')(:,1); [R, lag] = xcorr(jingle, audio); plot(R); 

Sie erhalten folgende Tabelle:



Ein Peak ist deutlich sichtbar, der die Position von jingle.wav in audio.wav . Was mich überrascht hat, war die Einfachheit der Methode: xcorr() erledigt die ganze Arbeit, der Rest des Codes dient nur zum Lesen von Dateien und zum Anzeigen des Ergebnisses.

Ich wollte den gleichen Algorithmus in Java implementieren, und dann werde ich ein Tool haben, das:

  1. liest den Audiostream vom Standardeingang (z. B. von ffmpeg),
  2. analysiert es auf der Suche nach Jingles,
  3. druckt denselben Stream auf stdout und / oder deaktiviert ihn.

Mit stdin und stdout können Sie den neuen Analysator an andere Anwendungen anschließen, die für die Audioübertragung und Wiedergabe des Ergebnisses verantwortlich sind.

Audiodateien lesen


Das erste, was ein Java-Programm lesen sollte, ist ein Jingle (als .wav Datei gespeichert) in einem Array. Die Datei enthält einige zusätzliche Informationen wie Header, Metadaten und mehr, aber wir benötigen nur Sound. Ein geeignetes Format heißt PCM, es ist nur eine Liste von Zahlen, die Töne darstellen. WAV in PCM konvertieren kann ffmpeg:

 ffmpeg -i input.wav -f s16le -acodec pcm_s16le output.raw 

Hier wird jeder Abtastwert als 16-Bit-Zahl mit umgekehrter Bytereihenfolge (Little Endian) gespeichert. In Java wird diese Nummer als short der ByteBuffer Klasse können Sie den ByteBuffer automatisch in eine Liste mit short Werten konvertieren:

 ByteBuffer buf = ByteBuffer.allocate(4); buf.order(ByteOrder.LITTLE_ENDIAN); buf.put(bytes); short leftChannel = buf.readShort(); // stereo stream short rightChannel = buf.readShort(); 

Xcorr Reverse Engineering


Um die Funktion xcorr() in Java zu implementieren, habe ich den Octave- Quellcode untersucht . Ohne das Endergebnis zu ändern, konnte ich den Aufruf von xcorr () durch die folgenden Zeilen ersetzen - sie müssen in Java neu geschrieben werden:

 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)); 

Es sieht beängstigend aus, aber die meisten Funktionen sind triviale Operationen mit Arrays. Die Kreuzkorrelation basiert auf der Anwendung der schnellen Fourier-Transformation auf ein Klangbeispiel.

Schnelle Fourier-Transformation


Als Person, die keine Erfahrung mit DSP hat, sehe ich FFT nur als eine Funktion, die ein Array mit einer Beschreibung eines Klangbeispiels verwendet - und ein Array mit komplexen Zahlen zurückgibt, die Frequenzen darstellen. Dieser minimalistische Ansatz hat gut funktioniert: Ich habe die FFT-Implementierung aus dem JTransforms- Paket gestartet und die gleichen Ergebnisse wie in Octave erzielt. Ich denke, das ist teilweise ein Frachtkult , aber verdammt, es funktioniert!

Xcorr in einem Thread ausführen


Der obige Algorithmus geht davon aus, dass audio das Array ist, in dem wir nach jingle suchen. Dies ist nicht ganz für den Rundfunk geeignet, bei dem wir einen kontinuierlichen Tonstrom haben. Um die Analyse auszuführen, habe ich einen zyklischen Puffer erstellt, der etwas länger ist als die Dauer des zu erkennenden Jingles. Der eingehende Stream füllt den Puffer und sobald er voll ist, wird der Kreuzkorrelationstest ausgeführt. Wenn nichts gefunden wird, wird der älteste Teil des Puffers verworfen - und wir erwarten erneut, dass er gefüllt wird.

Ich habe ein wenig mit der Länge des Puffers experimentiert und die besten Ergebnisse mit einer Puffergröße erzielt, die das 1,5-fache der Größe des Jingles beträgt.

Alles zusammenfügen


Das Abrufen eines Streams im PCM-Format ist einfach. Dies kann mit dem obigen ffmpeg . Der folgende Befehl leitet den Stream zur Standard- java Eingabe um und gibt dann Got jingle 0 oder Got jingle 1 wenn das entsprechende Muster im Stream gefunden wird.

 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 

Standalone-Version


Ich habe auch eine einfache Standalone-Version des Analysators vorbereitet, die selbst eine Verbindung zum Troika-Stream (ohne externes ffmpeg ) herstellt und das Ergebnis mit javax.sound . Alles passt in eine einzelne JAR-Datei und enthält eine grundlegende Benutzeroberfläche mit den Tasten Star und Stop. Es kann hier heruntergeladen werden . Wenn Sie nicht gerne die JARs anderer Personen auf Ihrem Computer ausführen (was absolut korrekt ist), befinden sich alle Quellen auf GitHub .



Alles scheint so zu funktionieren wie es sollte :)

Weitere Arbeit


Das ultimative Ziel besteht darin, Anzeigen auf der Ebene eines Hardwareverstärkers zu deaktivieren und ein „echtes“ FM-Signal und keinen Internet-Stream zu empfangen. Dies wird im nächsten Artikel beschrieben .

Update (Juni 2018)


Diskussion bei Hacker News
Diskussion über Wykop
Diskussion über Reddit

Source: https://habr.com/ru/post/de415469/


All Articles