<?php
/*
 * Etch A Sketch!
 * Copyright (c) 2012, Alex Duchesne <alex@alexou.net>.
 * This file is subject to the ISC license. In short you can do 
 * what ever you want, so long that you keep my name up there.
 * 
 */

set_time_limit(0);

$WavSamplingRate 48000;
$WavChannels 2// Stereo, X et Y.
$WavBytes 1// Amplitude = 0 - 255
$BytePack = [=> 'C'=> 'v'=> 'V'];
$Multiplicator 2;

/**
 * Create amplitude map (x(right)-y(left)) from picture resource
 *
 * @param img $frame: A picture resource. The script will use only black pixels (RGB 0,0,0)
 * @return array
 */
function create_wav_samples_from_picture($frame$BgColor 255) {
    global 
$WavBytes$BytePack$Multiplicator;
    
    
$ResMax pow(2$WavBytes 8) / $Multiplicator;

    
$FrameX imagesx($frame);
    
$FrameY imagesy($frame);
    
    if (
$FrameX != $FrameY || $FrameX != $ResMax || $FrameY != $ResMax) {
        throw new 
exception("The image is too large. Expected resolution: ({$ResMax}x{$ResMax})...");
    }
    
    
$samples = [];
    
    for (
$x 0$x $FrameX$x++) { // FrameX - 1
        
for ($y 0$y $FrameY$y++) {
            if (
imagecolorat ($frame$x$y) !== $BgColor) {
                
$samples[] = pack($BytePack[$WavBytes], $x $Multiplicator); // L
                
$samples[] = pack($BytePack[$WavBytes], ($ResMax $y) * $Multiplicator); // R
            
}
        }
    }
    
    return 
$samples;
}


/**
 * Create a RIFF WAV from our amplitude map.
 * The code tries to respect carefully the WAV PCM 8bits stereo 48kHz spec.
 *  
 * @param string $file: File path for output wav.
 * @param string $samples: A string containing audio information (arrays are too memory intensive)
 * @return boolean
 */
function create_wav_from_samples($file$samples) {
    global 
$WavSamplingRate$WavChannels$WavBytes;
    
    if (
file_exists($file)) {
        
//throw new exception("Le fichier {$file} existe déjà!");
    
}
    
    if (!(
$file fopen($file'w'))) {
        throw new 
exception("Impossible d'ouvrir le fichier {$file} en écriture");
    }
    
    
$fmt "fmt ".
            
pack("V"16).
            
pack("v"1).
            
pack("v"$WavChannels).
            
pack("V"$WavSamplingRate).
            
pack("V"$WavSamplingRate $WavChannels $WavBytes).
            
pack("v"$WavChannels $WavBytes).
            
pack("v"$WavBytes*8);
            
    
$sound "data".
             
pack("V"strlen($samples)).
             
$samples;
    
    
$header "RIFF".
              
pack("V"+ (strlen($fmt)) + (strlen($sound))).
              
"WAVE";
    
    return 
fwrite($file$header.$fmt.$sound) && fclose($file);
}


/**
 * Will loop the frames enough to cover $miliseconds (or a bit more).
 *
 * @param string $frames: Frames.
 * @param int $miliseconds:    
 * @return array
 */
function loop_frames($frames$miliseconds) {
    global 
$WavSamplingRate$WavChannels;
    
    if (
is_array($frames)) {
        
$frames implode($frames);
    }
    
    
$wavSamples $frames;
    
    while(
strlen($wavSamples) < $WavSamplingRate $WavChannels $miliseconds 1000) {
        
$wavSamples .= $frames;
    }
    
    return 
$wavSamples;
}


$sound '';
/*
foreach(glob('test/*.png') as $file) {
    $sound .= loop_frames(implode(create_wav_samples_from_picture(imagecreatefrompng($file))), 2750);
}

foreach(array_reverse(glob('test/*.png')) as $file) {
    $wavSamples = array_merge($wavSamples, loop_frames(create_wav_samples_from_picture(imagecreatefrompng($file)), 750));
}
*/


$fp fopen('/dev/dsp''w');

$fmt "fmt ".
        
pack("V"16).
        
pack("v"1).
        
pack("v"$WavChannels).
        
pack("V"$WavSamplingRate).
        
pack("V"$WavSamplingRate $WavChannels $WavBytes).
        
pack("v"$WavChannels $WavBytes).
        
pack("v"$WavBytes*8);
        
$sound "data".
         
pack("V"4294967295).
         
$sound;

$header "RIFF".
          
pack("V"+ (strlen($fmt)) + (strlen($sound))).
          
"WAVE";
    
fwrite($fp$header.$fmt.$sound);



/* Use this with an image editor feature auto save or a simple bmp drawing program */
while (1) {
    if (
$frame imagecreatefrompng('oscillo-test.png')) {
        
$samples create_wav_samples_from_picture($frameimagecolorat ($frame1,1));
        if (
$samples) {
            
$loop loop_frames($samples250);
            
fwrite($fp$loop);
            echo 
'Playing for '.(strlen($loop) / ($WavSamplingRate $WavChannels)).'s. Actual frame length: '.(count($samples) / ($WavSamplingRate $WavChannels))."s\n";
            
usleep(150000);
            continue;
        }
    }
    echo 
"Waiting\n";
    
usleep(750000);
}

exit();


/* Or use a gif */
include 'GifFrameExtractor.php';  // https://github.com/Sybio/GifFrameExtractor/blob/master/src/GifFrameExtractor/GifFrameExtractor.php

$gifFilePath 'oscillo-test.gif';

if (
false && GifFrameExtractor::isAnimatedGif($gifFilePath)) { // check this is an animated GIF
    
$gfe = new GifFrameExtractor();
    
$gfe->extract($gifFilePath);
    
    foreach (
$gfe->getFrames() as $i => $frame) {
        
$sound1 .= loop_frames(implode(create_wav_samples_from_picture($frame['image'], imagecolorat ($frame['image'], 1,1))), $frame['duration']*10);
    
//    imagepng($frame['image'], 'a/'.$i.'.png');
    
}
} else {
    
$frame imagecreatefrompng($gifFilePath);
    
$sound1 .= loop_frames(implode(create_wav_samples_from_picture($frameimagecolorat ($frame1,1))), 500);
}

create_wav_from_samples('/dev/dsp'$sound1);