Adaptive Wellenform für Ihren Audiodienst



Als ich neben dem Admin-Panel ein Audio-Archiv für eine einzelne Broadcast-Site einrichten musste, brauchte ich auch einen Audio-Player. Die Sendung dauerte 40 Minuten plus zwei musikalische Pausen. Die Verwendung von Waveform in so langen Formaten ist besonders praktisch. Daher habe ich mich wie bei vielen Musikdiensten für diese Lösung beim Design des Players entschieden.

Mit der geplanten zukünftigen Neugestaltung der Site und möglicherweise zukünftigen mobilen Anwendungen lag die Rasterwellenform hier einfach am Keil. Es ist nicht anpassungsfähig, es ist äußerst ressourcenintensiv, wenn es sich im Raster befindet.

Die bekannte SOUNDCLOUD löste dieses Problem auf kleinen Bildschirmen, indem sie die gesamte Wellenform relativ zum statischen Zentrum bewegte. Aber das will ich nicht.

Die Radiosendung wurde über das Admin-Panel durchgeführt, und ich habe sofort mehr komprimierte Kopien der Audiodateien über ffmpeg erstellt. Es wäre dumm, seine Fähigkeiten aufzugeben und eine Wellenform zu erzeugen.

Aktionsalgorithmus:


1. Erzeugung einer Wellenform in einer Mindestgröße für die Speicherung
2. Übersetzung in Vektor (JSON)
3. Zeichnen Sie einen Player für dieses Array
4. Implementierung der Anpassungsfähigkeit: Gleichmäßige Reduzierung des Arrays und Rückkehr zu Schritt 3

Wellenformgenerierung



Zum Zeitpunkt der Implementierung dieses Ansatzes hatten die BBC-Genossen, soweit ich mich erinnere, die Ausgabe in JSON noch nicht in ihrem Dienstprogramm veröffentlicht. Und im Moment würde ich empfehlen, dass Sie ihr Dienstprogramm neu erstellen, um die nutzlose Ausgabe von negativen Zahlen und zusätzlichen zu entfernen. Alt über Kanäle der Bissigkeit und anderen Unsinns.
In der Zwischenzeit fahren Sie fort:

Wenn wir mein Player-Design verwenden (es ist hier in der Breite reduziert), werden wir sehen, dass es 2 Pixel pro Streifen gibt (plus 1 Pixel-Trennzeichen). Dies bedeutet, dass 600 Pixel eine Breite von 1200 Pixel ergeben.



Ich nehme an, dass es in Zukunft äußerst unwahrscheinlich sein wird, dass eine größere Präsentation der Audiodatei erforderlich ist. Wenn Sie das Design nicht über die gesamte Breite des 4K-Monitors ziehen, sollten Sie darüber nachdenken, aber ich höre bei 600 x 60 Pixel auf.

Und jetzt näher am Code:

shell_exec("ffmpeg -y -i '$name.mp3' -filter_complex 'aformat=channel_layouts=mono,compand,showwavespic=s=600x120,crop=in_w:in_h/2:0:0' -c:v png -pix_fmt monob -frames:v 1 '$png_path.png' > /dev/null 2>/dev/null &"); 

-filter_complex - Filter verbinden

aformat - arbeite mit Ton

channel_layouts

-mono - Mono-Modus

-compand ist ein Kompressor und Expander. In diesem Modus werden sowohl leise als auch laute Töne in der Lautstärke ausgeglichen, wodurch Sie eine Wellenform ohne Spitzen und Überlastungen sowohl bei leisen als auch bei lauten Aufnahmen erhalten. Die Wellenform wird sozusagen immer maximal gedehnt.

-showwavespic = s = 600x120 - s nimmt die Größe des Bildes an.

-crop = in_w: in_h / 2: 0: 0 - schneidet das empfangene Bild zu. In der Regel wird der Ausgangsfrequenzgang um die x-Achse gespiegelt. Deshalb streuen wir und lassen nur die Spitze des Eisbergs.

-c: v png -pix_fmt monob -frames: v 1 - Ausgabebildformat, bw-Farbpalette und nur das erste Frame (keine Animation erforderlich). png8 ist großartig für Qualität (in unserem Fall verlustfrei) / Ort.

> / dev / null 2> / dev / null & Sende Ausgabe- und Arbeitsdaten an den Abgrund. Und '&' ermöglicht es PHP, nicht auf die Arbeit der Konsole zu warten, sondern fortzufahren.

Am Ausgang erhalten wir dieses Bild:


Größe der endgültigen Datei 2,4 KB

Das Lustige ist, dass es vor ein paar Jahren anstelle von Weiß eine rote Farbe gab. Die Entwickler haben anscheinend die Standardwerte geändert.

Wellenform in Vektor konvertieren


Das resultierende Bild ist die Amplitude in Y und die Zeit in X. Es ist elementar, es in ein eindimensionales JSON-Array zu übersetzen. Wo die Werte als Amplitudenwerte wirken und die Zeit einfach ihr Ordnungsindex ist.

Ich habe mich entschlossen, die Übersetzung im laufenden Betrieb durchzuführen, ohne das Ergebnis zwischenzuspeichern. Sie ist sehr schnell erledigt.
Wir messen die Anzahl der Pixel entlang Y von oben nach oben und fahren mit dem nächsten Pixel entlang X fort.

 $a = imagecreatefrompng("test.png"); $i = 0; $h = '60'; // horizontal movener while ( $i < 600 ) { // vertical movener $y = $h-1; $c = 0; while ( $c < $h ) { //echo imagecolorat($aa, $i, $c ); // test color if(imagecolorat($a, $i, $c ) == "255") { $arr[$i] = $c; break; } else { $arr[$i] = $y; } $c++; } $i++; }; echo json_encode($arr); 

Das resultierende Array besteht aus 600 Werten.

[46,28,34,35,34,35,26,33,39,29,29,30,30,30,33,33,28...]

Player-Rendering von JSON


Für einen bequemen Arbeitsfortschrittsbalken habe ich progressor.js von Elliot Bentley genommen. Er machte es für einen Audio-Transkriptionsdienst.

github.com/ejb/progressor.js 2.76 KB

Werfen wir noch einmal einen Blick auf unseren Spieler.



Der Fortschrittsbalken besteht aus zwei Ebenen: einem Hintergrund mit grauen Balken und grün.

Unten werden die Bilder mit der Funktion getGraph gezeichnet.

Seine Bedeutung ist es, Säulen mit der gewünschten Dicke und Farbe mit Säulentrennzeichen zu zeichnen.

 var c = document.createElement("canvas"); c.width = width; c.height = height; var ctx = c.getContext("2d"); function getGraph(fillStyle1,fillStyle2,fillStyle3) { if (fillStyle3) { //console.log(fillStyle1); var grd = ctx.createLinearGradient(0,120,0,0); grd.addColorStop(0.5,fillStyle1); grd.addColorStop(1,fillStyle2); fillStyle1 = grd; fillStyle2 = fillStyle3; } json.forEach(function(item, i, arr) { ctx.fillStyle = fillStyle1; ctx.fillRect(i * 3, height, 2, item - height); ctx.fillStyle = fillStyle2; var next = json[i + 1]; if( item <= next ) { h2 = next; } else { h2 = item; } ctx.fillRect(i * 3 + 2, height, 1, h2 - height); }); return c.toDataURL(); } 

Hier ist ein Arbeitsbeispiel ohne Anpassungsfähigkeit

4. Umsetzung der Anpassungsfähigkeit


Jetzt müssen wir das JSON-Array auf dem Client auf die gewünschte Größe reduzieren und hier haben Sie die Anpassungsfähigkeit.

Planen Sie a


Die allererste Methode, die mir in den Sinn kommt, besteht darin, jede Sekunde, jedes dritte, vierte ... in einem Zyklus zu entfernen, sodass Sie das Array nicht um weniger als die Hälfte reduzieren können und hier keine Pixelgenauigkeit erreicht werden kann.

Das Ändern der Wellenform durch Löschen von Array-Werten ist eine Sackgasse. Wenn Sie dies tun, werden Sie sehen, wie stark die Wellenform unpersönlich zerrissen wird, weil Sie Extreme werfen und die Höhe der Nachbarn nicht mitteln.

Wir brauchen Resampling-Algorithmen. Es gibt eine Implementierung des Algorithmus auf js:

größteTriangleThreeBuckets

Es funktioniert gut, fragt nur nach einer Eingabe eines solchen Arrays, an dessen Indizes es die XY-Koordinaten empfängt. Wir haben ein eindimensionales Array, also musste ich ein wenig Spaß machen und die Funktion wiederholen. Das Ding funktioniert so:



Und hier können Sie mit adaptiven als KDPV berühren.

Stellen Sie den Ansichtsmodus ein, in dem sich der HTML-Frame rechts befindet. Dann können Sie die Breite dieses Fensters ändern.

Plan B - Puff


Ich möchte die Clientseite jedoch immer noch nicht laden. Zum Beispiel möchte ich 1000 Punkte-5000, aber die gesamte Breite des Bildschirms. Wenn ich mehr Punkte habe, wie verhält sich dieses Ding auf einem Handy? Einerseits ist dies absolut kein Problem, es scheint nicht so teuer zu sein, gemessen an den Demos des Algorithmus, es kaut leicht 5000 Punkte. Andererseits sollte man so viel geben, wie man verlangt. Frage des Designs.

Elementar, wenn Sie Node.Js haben, können Sie diesen Code auf den Server übertragen. Und wenn Sie PHP haben, können Sie eine Implementierung dieses Algorithmus in PHP finden, aber ... warum, dachte ich.

Wo sind die Resampling-Algorithmen? In derselben nativen lib GD, mit der wir JSON generiert haben. Wir übergeben den Parameter einfach vom Client in Pixel der erforderlichen Breite und ändern die Größe unserer Wellenform, bevor wir sie in JSON konvertieren.

Daher werde ich den am Anfang geschriebenen Code erweitern.

 $h = 60; $width_new = 600; $a = imagecreatefrompng("$id.png"); $width_old = imagesx($a); $aa = imagecreatetruecolor($width_new, $h); imagecopyresized($aa, $a, 0, 0, 0, 0, $width_new, $h, $width_old, $h); imagetruecolortopalette($aa, false, 2); $i = 0; // horizontal movener while ( $i < $width_new ) { // vertical movener $y = $h-1; $c = 0; while ( $c < $h ){ //echo imagecolorat($aa, $i, $c ); // search what color is needed if(imagecolorat($aa, $i, $c ) == "1"){ $arr[$i] = $c; break; } else { $arr[$i] = $y; } $c++; } $i++; }; echo json_encode($arr); 

Danach können Sie sich keine Sorgen mehr machen, wenn Sie das Design und die Breite des Players ändern und zu einer mobilen Anwendung erweitern müssen. Alles sieht sehr flexibel und sehr schlau aus.

Der Code ist hier

.
Osterei.

Es war wahrscheinlich ein sonniger Tag. Das Fenster unseres Zimmers blickte auf zwei alte 9-stöckige Ziegelböden, an die ich mich als Teenager erinnere. Ich weiß, dass sich ein Stück weiter hinter ihnen ein Straßenbahnring öffnet - das alte Krankenhaus direkt hinter der Schule und das aktuelle Gebäude mit dem Büro, in dem ich zu graben versuche In seinen Memoiren ist dies ein ehemaliges unfertiges Krankenhaus, heute ein reines Bürogebäude. Ich erinnere mich, wie in meiner Kindheit Spezialkräfte, die hier ausgebildet wurden, im Fernsehen gezeigt wurden und heftig eine Betonkonstruktion stürmten, die mit allem um sie herum bewachsen war. Und jetzt, wie sich herausstellt, schockiere ich das glänzende Geländer energisch, gehe die Treppe hinunter und bewundere die Form der Verzerrungen dieses Gebäudes im Spiegel des nächsten Wohnkomplexes. (In der Nähe, entlang der Straßenbahnlinie, öffnet sich die Mauer des alten großen Friedhofs. Darauf befindet sich eine Inschrift in grüner Farbe „Während Boris an der Macht ist“ und „Labour Russia“. Gott weiß, wer und wann sie hergestellt wurden, aber nach ein paar Jahrzehnten lesen sie immer noch aber bleiben Sie völlig unsichtbar. Ich habe aus dem Erbe der 90er Jahre kein älteres Denkmal in der Stadt mehr gesehen.)

In unserer obersten Etage ist es leer, wie es in einer Packung mit Buchweizen, die gestartet wurde, leer ist: Es gibt viel von allem unten und dicht: einige Gauner von spezieller Geo-Intelligenz, 2gis-Büro, dann reguläres Seoshniki und oben gibt es fast keine Körner. Denken Sie, dass hier etwas durch die Böden von etwas wachsen sollte, aber im Laufe der 5 Jahre sah nur die Fensterputzmaschine vom Transzendentalen und vom immanenten Buchhalter mit verrückten Augen aus, der auf der Suche nach jemandem an alle Türen des Bodens klopfte , der erklärt, wie man eine Zahlung über ein verrücktes Plug-In für Internetbanking aufgrund eines anderen Browser-Updates signiert.

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


All Articles