音频服务的自适应波形



当需要为单个广播站点设置音频档案时,除了管理面板外,我还需要音频播放器。 广播持续了40分钟,外加两次音乐暂停。 以如此长的格式使用Waveform特别方便,因此,与许多音乐服务一样,我决定在播放器的设计中使用此解决方案。

随着计划中站点的未来重新设计以及可能的未来移动应用的重新设计,此处的光栅波形仅靠在楔形上。 它不是自适应的,如果在栅格中,则重新设计非常耗费资源。

众所周知的SOUNDCLOUD通过在整个屏幕上相对于静态中心移动整个波形来解决此问题。 但是我不要

广播是通过管理面板完成的,我立即通过ffmpeg制作了音频文件的更多压缩副本。 放弃其功能并生成波形是愚蠢的。

动作算法:


1.以最小尺寸生成波形以进行存储
2.转换为向量(JSON)
3.为此数组绘制一个播放器
4.实施适应性:统一减少阵列并返回步骤3

波形产生



据我所知,在实施这种方法时,BBC同志尚未在其实用程序中以JSON发布输出。 目前,我建议您重建它们的实用程序,以删除无用的负数和多余的输出。 关于咬人和其他胡说八道的陈词滥调。
同时,继续:

如果我们采用播放器的设计(此处宽度减小),我们将看到每个条带有2个像素(加上1个像素分隔符)。 这意味着600px将为我们提供1200px的宽度。



我认为,将来极不可能需要较大的音频文件表示形式。 好吧,如果您不将设计拉到4K显示器的整个宽度上,则应该考虑一下,但我会停在600x60px。

现在更接近代码:

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-连接过滤器

格式 -处理声音

channel_layouts

-mono-单声道模式

-compand是压缩程序和扩展程序。 在此模式下,安静声音和响亮声音的音量均等,这使您可以在安静和响亮的录音中获得没有峰值和过载的波形。 实际上,波形总是一直延伸到最大。

-showwavespic = s = 600x120 -s占用图像的大小。

-crop = in_w:in_h / 2:0:0-裁剪接收到的图像。 通常,输出频率响应围绕x轴镜像。 因此,我们洒了,只剩下冰山一角。

-c:v png -pix_fmt monob -frames:v 1-输出图像格式,bw调色板和仅第一帧(我们不需要动画)。 png8非常适合质量(在我们的情况下无损)/位置。

> / dev / null 2> / dev / null并将输出和工作数据发送到深渊。 并且“&”允许php不等待控制台完成工作,而是继续进行操作。

在输出中,我们得到以下图像:


最终文件的大小2.4kb

有趣的是,几年前不是白色,而是红色。 开发人员显然更改了默认值。

将波形转换为矢量


生成的图像是以Y为单位的振幅,以X为单位的时间。将其转换为一维JSON数组是基本的。 这些值将用作振幅值,而时间只是其序号。

我决定即时进行翻译,而无需缓存结果,因此很快就可以完成。
我们从上到下测量沿Y的像素数,然后沿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); 

结果数组由600个值组成。

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

播放器通过JSON渲染


为了方便工作进度栏,我从Elliot Bentley获得了progressor.js。 他将其用于音频转录服务。

github.com/ejb/progressor.js 2.76 KB

让我们再次看看我们的播放器。



进度栏由两层组成:具有灰色栏和绿色的背景。

在图像下方使用getGraph函数绘制。

其含义是使用列分隔符绘制所需厚度和颜色的列。

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

这是一个没有适应性的工作示例

4.实施适应性


现在,我们需要将客户端上的JSON数组减小到所需的大小,并且这里具有适应性。

计划一个


我想到的第一种方法是每一个周期删除第二,第三,第四...,因此您不能将阵列缩小不到一半,并且此处无法达到像素精度。

通过删除数组值来修改波形是死路一条。 当您执行此操作时,您会看到波形被非人为地撕裂了多少,这是因为您抛出了极端,并且没有求平均邻居的身高。

我们需要重采样算法。 在js上有算法的实现:

最大三角三斗

它工作得很好,只要求输入这样的数组,在索引处它会接收XY坐标,我们有一个一维数组,所以我不得不增加一些乐趣并重做该函数。 这个东西是这样的:



在这里,您可以使用自适应作为KDPV。

设置html框架在右侧的查看模式。 然后,您可以更改此窗口的宽度。

计划B-吹


但是,我仍然不想加载客户端。 例如,我要1000点-5000,但要整个屏幕宽度。 如果我有更多分数,那么这东西在手机上的表现如何? 一方面,这绝对没有问题,看上去也不是那么昂贵,根据算法的演示来看,它很容易获得5000分。 但是,另一方面,一个人应该给予尽可能多的要求。 设计问题。

初级,如果您有Node.Js,则可以将此代码传输到服务器。 而且,如果您有php,则可以在php中找到该算法的实现,但是...我想。

重采样算法在哪里? 在用于生成JSON的同一本机库GD中。 我们只需从客户端以所需宽度的像素为单位传递参数,然后在转换为JSON之前调整波形大小。

因此,我将扩展开始时编写的代码。

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

之后,您不必担心是否需要更改设计,播放器的宽度,扩展到移动应用程序中。 一切看起来都很灵活而且非常聪明。

代码在这里

复活节彩蛋。

可能是晴天。 我们房间的窗户俯瞰着两层9层的旧砖地板,我记得我还是十几岁的时候,我知道一个电车环在它们后面开了,再往前走-老医院,它就在学校后面,以及我试图挖掘的办公室所在的当前建筑物在他的回忆录中,这是一家前未完工的医院,现在是一座纯办公楼。 我记得在我童年时在这里训练过的特种部队是如何在电视上放映的,他们猛烈地冲向一个混凝土结构,周围的一切都长满了。 现在,事实证明,我正在大力震撼闪亮的栏杆,走下楼梯,并欣赏最近的住宅区所反射出的那栋建筑物的变形形式。 (在附近,沿着电车路线,那座古老的大公墓的墙壁打开了。上面刻着绿色的铭文:“鲍里斯当政”和“劳动俄罗斯”。上帝知道他们是谁,何时制造的,但几十年后,他们仍然阅读但仍然完全看不见。从90年代的遗产中,我再也看不到这座城市中更古老的纪念碑了。)

在我们的顶层是空的,因为刚开始装在一个装有荞麦的包装中是空的:下面有很多东西而且很紧:特殊的地理智能,2gis办公室,常规的seoshniki造成了一些骗局,而上面的几乎没有谷物。 您是否认为应该从这里的地板上生长出某种东西,但是在过去的5年中,只有洗窗器从先验者和无知的会计师那里疯狂地注视着,他们敲开地板上的所有门来寻找某人,他将解释由于另一个浏览器更新,如何通过疯狂的Internet银行插件来签署付款。

Source: https://habr.com/ru/post/zh-CN412629/


All Articles