Thomas Huijzer

Slikveld 22
3311VT Dordrecht

+31 (0)6 - 52 18 06 31
thomas@thuijzer.nl
PGP key
Of contact me via Signal

IBAN: NL93INGB0006752359
KvK: 57865841
btw: NL181253240B02

user@thuijzer.nl:/home/blog/geluidsgolven_van_een_audiobestand_afbeelden_met_php/

Geluidsgolven van een audiobestand afbeelden met PHP

In dit artikel laat ik zien hoe je met PHP een afbeelding kan genereren die de geluidsgolven (waveforms) van een audiobestand laat zien.

Audiobestanden inlezen met PHP, dat klinkt misschien onmogelijk. Maar er zijn methoden om dit voor elkaar te krijgen.
Via de ogg://-wrapper bijvoorbeeld, is het mogelijk om met PHP OGG/Vorbis bestanden te openen. Maar deze functionaliteit moet geïnstalleerd worden en ondersteunt alleen OGG/Vorbis. Je zou alle bestanden dus eerst om moeten zetten naar OGG/Vorbis.

Raw

Er is een ander bestandsformaat dat erg goed te gebruiken is met PHP: raw. Een raw-bestand bevat ruwe audio in de vorm van een reeks samples (amplitude op een bepaald moment in tijd). Omdat raw-bestanden geen headers hebben zijn ze met PHP erg goed te doorlopen. Alleen moet je dan wel precies weten wat de indeling van het bestand is. Dat wil zeggen, hoeveel bits de samples zijn en hoe de stereo-informatie is opgeslagen.

Om van audiobestanden een raw-bestand te maken gebruik ik Ffmpeg om bestanden om te zetten: ffmpeg -i input.mp3 -ac 1 -ar 1000 -f u8 -acodec pcm_u8 output.raw -ac 1 maakt van de output een mono bestand (1 audio channel).
-ar 1000 stelt de sample rate in op 1000 Hz. Veel meer heb je ook niet nodig, want dat is toch niet erg nuttig voor een afbeelding.
-f u8 -acodec pcm_u8 stelt het formaat en het gebruik van de codec in; een sample reeks van unsigned 8 bit (1 byte) waarden.

Code

Het raw-bestand lezen we nu in met PHP en we maken hier een mooi plaatje van:

<?php

$file = 'output.raw';

$bps = 1; // bytes per sample
$chunk = 100; // chunk size for fread
$samples = filesize($file) / $bps;
$sampleIndex = 0;

$width = 400;
$height = 200;

$pixels = array();
for($i = 0; $i < $width * $height; $i ++) {
    $pixels[$i] = 1;
}

$handle = fopen($file, "rb");
while(!feof($handle)) {
    $data = fread($handle, $bps * $chunk);
    $unpacked = unpack('C*', $data);
    foreach($unpacked as $sample) {
        $x = floor($sampleIndex * $width / $samples);
        $ys = floor(($height - 1) - $sample * ($height - 1) / 255);
        $ye = $ys > $height * 0.5 ? $ys : $height * 0.5;
        if($ys > $height * 0.5) {
            $ys = $height * 0.5;
        }
        for($y = $ys; $y < $ye; $y ++) {
            $i = $x + $y * $width;
            $pixels[$i] *= 0.9;
        }
        $i = $x + $y * $width;
        $pixels[$i] *= 0.9;
        $sampleIndex ++;
    }
}
fclose($handle);

$img = imagecreatetruecolor($width, $height);
foreach($pixels as $i => $pixel) {
    $y = floor($i / $width);
    $x = $i - $y * $width;
    $r = $pixel * 255;
    $g = $pixel * 255;
    $b = $pixel * 255;
    imagesetpixel($img, $x, $y, imagecolorallocate($img, $r, $g, $b));
}
header('content-type: image/png');
imagepng($img);
imagedestroy($img);

Opmerkingen

Geheugengebruik

Omdat raw-bestanden nogal groot kunnen zijn is het niet aan te raden om deze bestanden in één keer in te lezen. Daarom lees ik steeds 'chunks' in met fread.

GD lib

Tekenen met GD lib in PHP is behoorlijk langzaam. Daarom heb ik er voor gekozen om een array met pixels bij te houden en die uiteindelijk in één keer te tekenen.
De pixels-array is eigenlijk een lijst met 'intensiteit' per pixel. Standaard staat deze op 1 (maximaal) en loopt naar beneden ($pixels[$i] *= 0.9;) wanneer een sample over de pixel valt.
Hierdoor ontstaat er altijd een waarde tussen de 0 en 1 en hoeft er niet bijgehouden te worden wat de maximale waarde is.

Resultaat

geluidsgolven

Reacties


Plaats een reactie