Forma de onda adaptável para o seu serviço de áudio



Quando eu precisava configurar um arquivo de áudio para um único site de transmissão, além do painel de administração, também precisava de um reprodutor de áudio. A transmissão durou 40 minutos mais duas pausas musicais. Usar o Waveform em formatos tão longos é especialmente conveniente; portanto, como muitos serviços de música, decidi usar essa solução no design do player.

Com o planejado futuro redesenho do site e, possivelmente, futuros aplicativos móveis, a forma de onda raster aqui simplesmente se apoiou na barreira. Não é adaptável, é extremamente intensivo em recursos para redesenhar se estiver na varredura.

O conhecido SOUNDCLOUD resolveu esse problema em telas pequenas movendo toda a forma de onda em relação ao centro estático. Mas eu não quero isso.

A transmissão de rádio foi feita através do painel de administração e imediatamente fiz mais cópias compactadas dos arquivos de áudio através do ffmpeg. Seria tolice desistir de suas capacidades e gerar uma forma de onda.

Algoritmo de ações:


1. Geração de forma de onda em um tamanho mínimo para armazenamento
2. Tradução em vetor (JSON)
3. Desenhando um jogador para esta matriz
4. Implementação da adaptabilidade: redução uniforme da matriz e retorno à etapa 3

Geração de forma de onda



No momento da implementação dessa abordagem, os camaradas da BBC ainda não haviam divulgado a saída em JSON em sua utilidade , tanto quanto me lembro. E, no momento, eu recomendo que você reconstrua a utilidade deles para remover a saída inútil de números negativos e extras. Antigo sobre canais de testemunha e outras bobagens.
Enquanto isso, continue:

Se usarmos o design do meu player (ele é reduzido em largura aqui), veremos que existem 2 pixels por tira (mais 1 separador de pixels). Isso significa que 600px fornecerá 1200px de largura.



Suponho que no futuro seja extremamente improvável que seja necessária uma apresentação maior do arquivo de áudio. Bem, se você não puxar o design por toda a largura do monitor 4K, pense nisso, mas eu paro em 600x60px.

E agora mais perto do 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 - conectar filtros

aformat - trabalhe com som

channel_layouts

-mono - modo mono

-compand é um compressor e expansor. Nesse modo, os sons baixos e altos serão equalizados em volume, o que permite obter uma forma de onda sem picos e sobrecargas nas gravações baixas e altas. A forma de onda, por assim dizer, é sempre esticada ao máximo.

-showwavespic = s = 600x120 - s leva o tamanho da imagem.

-crop = in_w: in_h / 2: 0: 0 - corta a imagem recebida. Como regra, a resposta de frequência de saída é espelhada em torno do eixo x. Portanto, borrifamos, deixando apenas a ponta do iceberg.

-c: v png -pix_fmt monob -frames: v 1 - formato de imagem de saída, paleta de cores bw e apenas o primeiro quadro (não precisamos de animação). png8 é ótimo para qualidade (sem perdas no nosso caso) / local.

> / dev / null 2> / dev / null e envia dados de saída e de trabalho para o abismo. E '&' permite que o php não espere o console terminar de trabalhar, mas continue.

Na saída, obtemos esta imagem:


Tamanho do arquivo final 2.4kb

O engraçado é que há alguns anos atrás, em vez de branco, havia uma cor vermelha. Os desenvolvedores, aparentemente, alteraram os valores padrão.

Converter forma de onda em vetor


A imagem resultante é a amplitude em Y e o tempo em X. É fundamental convertê-la em uma matriz JSON unidimensional. Onde os valores atuarão como valores de amplitude, e o tempo é simplesmente seu índice ordinal.

Eu decidi fazer a tradução em tempo real, sem armazenar o resultado em cache, isso é feito muito rapidamente.
Medimos o número de pixels ao longo de Y, de cima para o primeiro, e passamos para o próximo pixel ao longo 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); 

A matriz resultante consiste em 600 valores.

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

Renderização de jogador por JSON


Para uma barra de progresso do trabalho conveniente, usei progressor.js da Elliot Bentley. Ele fez isso para um serviço de transcrição de áudio.

github.com/ejb/progressor.js 2,76 KB

Vamos dar uma olhada no nosso jogador novamente.



A barra de progresso consiste em duas camadas: um fundo com barras cinza e verde.

Abaixo as imagens são desenhadas com a função getGraph.

Seu significado é desenhar colunas da espessura e cor desejadas com separadores de colunas.

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

Aqui está um exemplo de trabalho sem adaptabilidade

4. Implementação da adaptabilidade


Agora precisamos reduzir a matriz JSON no cliente para o tamanho desejado e aqui você tem a adaptabilidade.

Planejar um


O primeiro método que vem à mente é remover cada segundo, terceiro, quarto ... em um ciclo, para que você não possa reduzir a matriz em menos da metade e a precisão do pixel não pode ser alcançada aqui.

Modificar a forma de onda excluindo valores de matriz é um beco sem saída. Ao fazer isso, você verá o quanto a forma de onda se torna impessoalmente rasgada, porque você joga extremos e não mede a média dos vizinhos em altura.

Precisamos de algoritmos de reamostragem. Existe uma implementação do algoritmo em js:

largestTriangleThreeBuckets

Funciona bem, apenas solicita uma entrada como essa matriz, cujos índices receberão as coordenadas XY. Temos uma matriz unidimensional, então tive que me divertir um pouco e refazer a função. Essa coisa funciona assim:



E aqui você pode tocar com adaptável como KDPV.

Defina o modo de exibição em que o quadro html estará à direita. Então você pode alterar a largura desta janela.

Plano B - Puff


No entanto, eu ainda não gostaria de carregar o lado do cliente. Por exemplo, eu quero 1000 pontos-5000, mas toda a largura da tela. Se eu tiver mais pontos, como isso se comportará no celular? Por um lado, isso não é absolutamente nenhum problema, não é tão aparentemente caro, a julgar pelas demos do algoritmo, ele mastiga 5000 pontos facilmente. Mas, por outro lado, deve-se dar o que se pede. Questão de design.

Elementar, se você tiver Node.Js, poderá transferir esse código para o servidor. E se você tem php, pode encontrar uma implementação desse algoritmo em php, mas ... por que, pensei.

Onde estão os algoritmos de reamostragem? Na mesma lib nativa GD que usamos para gerar JSON. Simplesmente passamos o parâmetro do cliente em pixels da largura necessária e redimensionamos nossa forma de onda antes de converter para JSON.

Portanto, expandirei o código escrito no início.

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

Depois disso, você não pode se preocupar se precisar alterar o design, a largura do player, expandir para um aplicativo móvel. Tudo parece bastante flexível e muito inteligente.

O código está aqui

.
Ovo de pascoa

Provavelmente foi um dia ensolarado. A janela do nosso quarto dava para dois velhos andares de tijolos de 9 andares, dos quais me lembro quando adolescente, sei que um anel de bonde se abre atrás deles, um pouco mais adiante - o antigo hospital, logo atrás da escola, e o prédio atual com o escritório onde tento cavar em suas memórias, este é um antigo hospital inacabado, agora um prédio puramente comercial. Lembro-me de que na minha infância as forças especiais treinadas aqui eram exibidas na TV, atacando vigorosamente uma estrutura de concreto, coberta de tudo o que estava à sua volta. E agora, ao que parece, estou chocando energicamente o corrimão brilhante, descendo as escadas e admirando a forma de distorções deste edifício no reflexo do complexo residencial mais próximo. (Nas proximidades, ao longo da linha do bonde, a parede do antigo cemitério é aberta. E nela está uma inscrição em tinta verde “Enquanto Boris está no poder” e “Labor Russia”. Deus sabe quem e quando eles foram feitos, mas depois de algumas décadas eles ainda leram mas permaneço completamente invisível. Não vejo mais, desde o legado dos anos 90, um monumento mais antigo da cidade.)

No andar de cima, ele está vazio, como acontece vazio em um pacote com trigo sarraceno que foi iniciado: há muito tudo embaixo e apertado: alguns bandidos de geo-inteligência especial, escritório 2gis, depois seoshniki regular e acima quase não há grãos. Você acha que algo deve crescer pelo chão de algo aqui, mas ao longo dos 5 anos apenas o lavador de janelas olhou do transcendental e do contador imanente com olhos loucos, que batem em todas as portas do chão em busca de alguém , que explicará como assinar um pagamento por meio de um plug-in maluco para serviços bancários na Internet devido a outra atualização do navegador.

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


All Articles