Rendering waveform in PHP - How to produce a more compressed render?

好久不见. 提交于 2019-12-05 01:05:58

With no math skills (and probably useful to have a speedy display):

You have 256 possible values. Create an array that contains the "dynamic" value for each of these values:

$dynamic = array(
   0 => 0,
   1 => 2,
   ...
);

That done, you can easily get the dynamic value:

$v = (int) ($dynamic[(int) $data / 255] * $height);

You might lose some precision, but it's probably useful.


Natural dynamic values are generated by the math sine and cosine functions, in PHP this sin­Docs (and others linked there).

You can use a loop and that function to prefill the array as well and re-use the array so you have pre-computed values:

$sine = function($v)
{
    return sin($v * 0.5 * M_PI);
};

$dynamic = array();
$base = 255;
for ($i = 0; $i <= $base; $i++)
{
    $dynamic[$i] = $i/$base;
}

$dynamic = array_map($sine, $dynamic);

I use a variable function here, so you can write multiple and can easily test which one matches your needs.

You need something similar to gamma correction.

For input values x in the range 0.0 -> 1.0, take y = pow(x, n) when n should be in the range 0.2 - 0.7 (ish). Just pick a number that gives the desired curve.

As your values are in the range 0 -> 255 you will need to divide by 255.0, apply the pow function, and then multiply by 255 again, e.g.

$y = 255 * pow($x / 255.0, 0.4);

The pow formula satisfies the criteria that 0 and 1 map to themselves, and smaller values are "amplified" more than larger values.

Here's a graph showing gamma curves for n = 1 / 1.6, 1 / 2, 1 / 2.4 and 1 / 2.8, vs the sin curve (in red):

The lower the value of n, the more "compression" is applied to the low end, so the light blue line is the one with n = 1 / 2.8.

Note how the sin curve is almost linear in the range 0 to 0.5, so provides almost no low end compression at all.

If as I suspect your values are actually centered around 128, then you need to modify the formula somewhat:

$v = ($x - 128.0) / 128.0;
$y = 128 + 127 * sign($v) * pow(abs($v), 0.4);

although I see that the PHP developers have not included a sign function in the PHP library.

Simple downsampling is not going to give you a correct render, as it will leave the original signal with only low frequencies, whilst all frequencies contribute to amplitudes. So you need to build the peak data (min and max for a certain range of values) from the original waveform to visualize it. You shouldn't apply any non-linear functions to your data, as the waveform representation is linear (unlike gamma-compressed images).

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!