Forma de onda adaptativa para su servicio de audio



Cuando necesitaba configurar un archivo de audio para un solo sitio de transmisión, además del panel de administración, también necesitaba un reproductor de audio. La transmisión duró 40 minutos más dos pausas musicales. Usar Waveform en formatos tan largos es especialmente conveniente, por lo tanto, como muchos servicios de música, decidí usar esta solución en el diseño del reproductor.

Con el rediseño futuro planificado del sitio y, posiblemente, las futuras aplicaciones móviles, la forma de onda ráster aquí simplemente descansaba contra la cuña. No es adaptativo, es extremadamente intensivo en recursos para rediseñar si está en la trama.

La conocida SOUNDCLOUD resolvió este problema en pantallas pequeñas moviendo toda la forma de onda en relación con el centro estático. Pero no quiero eso.

La transmisión de radio se realizó a través del panel de administración, e inmediatamente hice más copias comprimidas de los archivos de audio a través de ffmpeg. Sería una tontería renunciar a sus capacidades y generar una forma de onda.

Algoritmo de acciones:


1. Generación de forma de onda en un tamaño mínimo para almacenamiento
2. Traducción al vector (JSON)
3. Dibujando un jugador para esta matriz
4. Implementación de adaptabilidad: reducción uniforme de la matriz y regreso al paso 3

Generación de forma de onda



En el momento de la implementación de este enfoque, los camaradas de la BBC aún no habían publicado la salida en JSON en su utilidad , por lo que recuerdo. Y en este momento, recomendaría que reconstruya su utilidad para eliminar la salida inútil de números negativos y extra. Viejo sobre canales de bitness y otras tonterías.
Mientras tanto, continúe:

Si tomamos el diseño de mi reproductor (aquí se reduce su ancho), veremos que hay 2 píxeles por tira (más un separador de 1 píxel). Esto significa que 600px nos dará 1200px de ancho.



Supongo que en el futuro será extremadamente improbable que se necesite una presentación más grande del archivo de audio. Bueno, si no tira el diseño sobre el ancho completo del monitor 4K, debería pensarlo, pero me detengo en 600x60px.

Y ahora más cerca del código:

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 - conecta filtros

aformat - trabaja con sonido

channel_layouts

-mono - modo mono

-compand es un compresor y expansor. En este modo, los sonidos tanto silenciosos como fuertes se igualarán en volumen, lo que le permite obtener una forma de onda sin picos y sobrecargas en grabaciones silenciosas y fuertes. La forma de onda, por así decirlo, siempre se estira al máximo.

-showwavespic = s = 600x120 - s toma el tamaño de la imagen.

-crop = in_w: in_h / 2: 0: 0 - recorta la imagen recibida. Como regla general, la respuesta de frecuencia de salida se refleja alrededor del eje x. Por lo tanto, rociamos, dejando solo la punta del iceberg.

-c: v png -pix_fmt monob -frames: v 1 - formato de imagen de salida, paleta de colores bw y solo el primer fotograma (no necesitamos animación). png8 es excelente para la calidad (sin pérdidas en nuestro caso) / lugar.

> / dev / null 2> / dev / null & enviar salida y datos de trabajo al abismo. Y '&' permite que php no espere a que la consola termine de funcionar, sino que continúe.

En la salida, obtenemos esta imagen:


Tamaño del archivo final 2.4kb

Lo curioso es que hace un par de años en lugar de blanco había un color rojo. Los desarrolladores, aparentemente, cambiaron los valores predeterminados.

Convertir forma de onda a vector


La imagen resultante es la amplitud en Y y el tiempo en X. Es elemental traducirla en una matriz JSON unidimensional. Donde los valores actuarán como valores de amplitud, y el tiempo es simplemente su índice ordinal.

Decidí hacer la traducción sobre la marcha, sin almacenar en caché el resultado, se hace muy rápidamente.
Medimos el número de píxeles a lo largo de Y desde la parte superior a la primera, y pasamos al siguiente píxel a lo largo de X.

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

La matriz resultante consta de 600 valores.

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

Representación del jugador por JSON


Para una barra de progreso de trabajo conveniente, tomé el archivo progressor.js de Elliot Bentley. Lo hizo para un servicio de transcripción de audio.

github.com/ejb/progressor.js 2.76 KB

Echemos un vistazo a nuestro jugador nuevamente.



La barra de progreso consta de dos capas: un fondo con barras grises y verde.

Debajo, las imágenes se dibujan con la función getGraph.

Su significado es dibujar columnas del grosor y color deseados con separadores de columnas.

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

Aquí hay un ejemplo de trabajo sin adaptabilidad.

4. Implementación de adaptabilidad


Ahora necesitamos reducir la matriz JSON en el cliente al tamaño deseado y aquí tiene la adaptabilidad.

Planificar un


El primer método que viene a la mente es eliminar cada segundo, tercero, cuarto ... en un ciclo, por lo que no puede reducir la matriz en menos de la mitad, y la precisión de píxeles no se puede lograr aquí.

Modificar la forma de onda eliminando valores de matriz es un callejón sin salida. Cuando haga esto, verá cuánto se desgarra impersonalmente la forma de onda, porque arroja extremos y no promedia la altura de los vecinos.

Necesitamos algoritmos de remuestreo. Hay una implementación del algoritmo en js:

largerTriangleThreeBuckets

Funciona bien, solo pide una entrada como una matriz, en cuyos índices recibirá las coordenadas XY. Tenemos una matriz unidimensional, así que tuve que burlarme un poco y rehacer la función. Esto funciona así:



Y aquí puedes tocar con adaptativos como KDPV.

Establezca el modo de vista donde el marco html estará a la derecha. Luego puede cambiar el ancho de esta ventana.

Plan B - Puff


Sin embargo, todavía no me gustaría cargar el lado del cliente. Por ejemplo, quiero 1000 puntos-5000, pero todo el ancho de la pantalla. Si tengo más puntos, ¿cómo se comportará esto en un móvil? Por un lado, esto no es absolutamente ningún problema, no es tan caro, a juzgar por las demostraciones del algoritmo, mastica 5000 puntos fácilmente. Pero, por otro lado, uno debe dar tanto como pide. Cuestión de diseño.

Elemental, si tiene Node.Js, puede transferir este código al servidor. Y si tienes php, puedes encontrar una implementación de este algoritmo en php pero ... por qué, pensé.

¿Dónde están los algoritmos de remuestreo? En la misma lib nativa GD que usamos para generar JSON. Simplemente pasamos el parámetro del cliente en píxeles del ancho requerido y redimensionamos nuestra forma de onda antes de convertir a JSON.

Por lo tanto, expandiré el código escrito al principio.

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

Después de eso, no puede preocuparse si necesita cambiar el diseño, el ancho del reproductor, expandirse a una aplicación móvil. Todo parece bastante flexible y muy inteligente.

El codigo esta aqui

.
Huevo de pascua

Probablemente fue un día soleado. La ventana de nuestra habitación daba a dos viejos pisos de ladrillo de 9 pisos, que recuerdo cuando era adolescente, sé que un anillo de tranvía se abre detrás de ellos, un poco más allá: el antiguo hospital, justo detrás de la escuela, y el edificio actual con la oficina donde intento cavar. En sus memorias, este es un antiguo hospital inacabado, ahora un edificio puramente de oficinas. Recuerdo cómo en mi infancia, las fuerzas especiales entrenadas aquí, se mostraban en televisión, asaltando vigorosamente una estructura de hormigón, cubierta de todo lo que la rodeaba. Y ahora, resulta que estoy enérgicamente impactando la reja brillante, bajando las escaleras y admirando la forma de distorsiones de este edificio en el reflejo del complejo residencial más cercano. (Cerca, a lo largo de la línea del tranvía, se abre la pared del antiguo cementerio grande. Y en ella hay una inscripción en pintura verde "Mientras Boris está en el poder" y "Labor Rusia". Dios sabe quién y cuándo se hicieron, pero después de un par de décadas todavía leen pero permanecen completamente invisibles. No he visto más del legado de los 90 un monumento más antiguo en la ciudad).

En nuestro piso superior está vacío, como sucede vacío en un paquete con trigo sarraceno que se inició: hay mucho de todo debajo y apretado: algunos ladrones de geo-inteligencia especial, oficina de 2gis, luego seoshniki regular, y arriba casi no hay granos. ¿Crees que algo debería crecer a través de los pisos de algo aquí, pero durante los 5 años solo el limpiador de ventanas miró desde lo trascendental y desde el contador inmanente con ojos locos, que tocó todas las puertas en el piso en busca de alguien? , quien explicará cómo firmar un pago a través de un complemento loco para la banca por Internet debido a otra actualización del navegador.

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


All Articles