--- /dev/null
+<?php
+
+ class mw_sources_waveforms extends mw_controller{
+
+ /*
+
+ des actions disponibles en ligne de commande pour génerer des waveforms
+ si sox est executable sur le systeme via php
+
+ ### action make
+ ---------------
+
+ php cli.php sources/waveforms/make from=http://domaine.archive.tld archive_root=/fs/path/to/archive/ id=<id_source>
+
+
+ genere la waveforme de la source d'id <id_source>
+
+ options :
+
+ waveform_fg=RRVVBB couleur (notation hexa sans #) de la waveforme
+
+ waveform_bg=RRVVBB couleur (notation hexa sans #) du fond (par defaut transparent)
+
+
+ ### action make_all
+ -------------------
+
+ php cli.php sources/waveforms/make_all from=http://domaine.archive.tld archive_root=/fs/path/to/archive/
+
+
+ genere toutes les waveformes
+
+ options :
+
+ update=1 force la generation même si une image existe deja
+
+ waveform_fg=RRVVBB couleur (notation hexa sans #) de la waveforme
+
+ waveform_bg=RRVVBB couleur (notation hexa sans #) du fond (par defaut transparent)
+
+ */
+
+ function validate(){
+ if(PHP_SAPI != "cli"){
+ return "utilisation en ligne de commande uniquement";
+ }
+ return true;
+ }
+
+ function make(){
+ $env = $this->env();
+ $data = $env->data();
+ $id_source = $_GET[$env->param("id")];
+ $base_url = $_GET[$env->param("from")];
+ $document_root = $_GET[$env->param("archive_root")];
+ $foreground = isset($_GET[$env->param("waveform_fg")]) ? $_GET[$env->param("waveform_fg")] : "383838";
+ $background = isset($_GET[$env->param("waveform_bg")]) ? $_GET[$env->param("waveform_bg")] : null;
+ if(!$data->init_waveform_dirs()){
+ debug("impossible de creer les dossiers pour les waveforms");
+ exit;
+ }
+ if(($source = $data->source($id_source, true)) === false){
+ debug("impossible de lire les informations de la source");
+ exit;
+ }
+ $audio_files = array();
+ $no_files = array();
+ if(isset($source["documents"])){
+ $documents = array(
+ "flac" => false,
+ "wav" => false,
+ "ogg" => false,
+ "mp3" => false
+ );
+ foreach($source["documents"] as $document){
+ $file_name = false;
+ if(substr(urldecode($document["url"]), 0, strlen($base_url)) == $base_url){
+ $file_name = $document_root.substr(urldecode($document["url"]), strlen($base_url));
+ if(!file_exists($file_name)){
+ $file_name = false;
+ }
+ }
+ if($file_name){
+ if(strtolower(substr($document["url"], -5)) === ".flac"){
+ $documents["flac"] = $file_name;
+ }
+ if(strtolower(substr($document["url"], -4)) === ".wav"){
+ $documents["wav"] = $file_name;
+ }
+ if(strtolower(substr($document["url"], -4)) === ".ogg"){
+ $documents["ogg"] = $file_name;
+ }
+ if(strtolower(substr($document["url"], -4)) === ".mp3"){
+ $documents["mp3"] = $file_name;
+ }
+ }
+ }
+ if($documents["flac"]) $audio_files[$source["id"]] = $documents["flac"];
+ elseif($documents["wav"]) $audio_files[$source["id"]] = $documents["wav"];
+ elseif($documents["ogg"]) $audio_files[$source["id"]] = $documents["ogg"];
+ elseif($documents["mp3"]) $audio_files[$source["id"]] = $documents["mp3"];
+ else $no_files[$source["id"]] = $source["titre"];
+ }
+ foreach($audio_files as $id_source => $audio_file){
+ if(
+ !$data->audio_to_png(
+ array(
+ "audio_file" => $audio_file,
+ "png_file" => $env->path("content")."waveforms/".$id_source.".png",
+ "foreground" => "#".$foreground,
+ "background" => $background ? "#".$background : ""
+ )
+ )
+ ){
+ if(!$data->sox_exists()){
+ debug("impossible d'executer la commande sox sur le systeme");
+ }
+ else{
+ debug("impossible de generer le png pour la source id ".$id_source);
+ }
+ }
+ }
+ }
+
+ function make_all(){
+ $env = $this->env();
+ $data = $env->data();
+ $base_url = $_GET[$env->param("from")];
+ $document_root = $_GET[$env->param("archive_root")];
+ $UPDATE = isset($_GET[$env->param("update")]) ? $_GET[$env->param("update")] : false;
+ $foreground = isset($_GET[$env->param("waveform_fg")]) ? $_GET[$env->param("waveform_fg")] : "383838";
+ $background = isset($_GET[$env->param("waveform_bg")]) ? $_GET[$env->param("waveform_bg")] : null;
+ if(!$data->init_waveform_dirs()){
+ debug("impossible de creer les dossiers pour les waveforms");
+ exit;
+ }
+ $sources = $data->sources(array());
+ $audio_files = array();
+ $no_files = array();
+ foreach($sources["list"] as $source){
+ $png_file = $env->path("content")."waveforms/".$source["id"].".png";
+ if(file_exists($png_file) && !$UPDATE) continue;
+ if(isset($source["documents"])){
+ $documents = array(
+ "flac" => false,
+ "wav" => false,
+ "ogg" => false,
+ "mp3" => false
+ );
+ foreach($source["documents"] as $document){
+ $file_name = false;
+ if(substr(urldecode($document["url"]), 0, strlen($base_url)) == $base_url){
+ $file_name = $document_root.substr(urldecode($document["url"]), strlen($base_url));
+ if(!file_exists($file_name)){
+ $file_name = false;
+ }
+ }
+ if($file_name){
+ if(strtolower(substr($document["url"], -5)) === ".flac"){
+ $documents["flac"] = $file_name;
+ }
+ if(strtolower(substr($document["url"], -4)) === ".wav"){
+ $documents["wav"] = $file_name;
+ }
+ if(strtolower(substr($document["url"], -4)) === ".ogg"){
+ $documents["ogg"] = $file_name;
+ }
+ if(strtolower(substr($document["url"], -4)) === ".mp3"){
+ $documents["mp3"] = $file_name;
+ }
+ }
+ }
+ if($documents["flac"]) $audio_files[$source["id"]] = $documents["flac"];
+ elseif($documents["wav"]) $audio_files[$source["id"]] = $documents["wav"];
+ elseif($documents["ogg"]) $audio_files[$source["id"]] = $documents["ogg"];
+ elseif($documents["mp3"]) $audio_files[$source["id"]] = $documents["mp3"];
+ else $no_files[$source["id"]] = $source["titre"];
+ }
+ }
+ foreach($audio_files as $id_source => $audio_file){
+ if(
+ !$data->audio_to_png(
+ array(
+ "audio_file" => $audio_file,
+ "png_file" => $env->path("content")."waveforms/".$id_source.".png",
+ "foreground" => "#".$foreground,
+ "background" => $background ? "#".$background : ""
+ )
+ )
+ ){
+ if(!$data->sox_exists()){
+ debug("impossible d'executer la commande sox sur le systeme");
+ }
+ else{
+ debug("impossible de generer le png pour la source id ".$id_source);
+ }
+ }
+ }
+ }
+
+ }
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+ class sml_data_waveforms extends mw_data{
+
+ var $SOX_EXISTS;
+
+ function init_waveform_dirs(){
+ $env = $this->env();
+ $dir = $env->path("content")."tmp";
+ if(!is_dir($dir)) @mkdir($dir);
+ if(!is_dir($dir)) return false;
+ $dir = $env->path("content")."waveforms";
+ if(!is_dir($dir)) @mkdir($dir);
+ if(!is_dir($dir)) return false;
+ return true;
+ }
+
+ function audio_to_png($params = array()){
+ $env = $this->env();
+ $audio_file = $params["audio_file"];
+ if(!$audio_file) return false;
+ if(!file_exists($audio_file)) return false;
+
+ $png_file = $params["png_file"];
+ if(!$png_file) return false;
+
+ $width = isset($params["width"]) ? $params["width"] : "650";
+ $height = isset($params["height"]) ? $params["height"] : "100";
+ $background = isset($params["background"]) ? $params["background"] : "";
+ $foreground = isset($params["foreground"]) ? $params["foreground"] : "#000000";
+ $draw_flat = isset($params["draw_flat"]) ? $params["draw_flat"] : true;
+ $DETAIL = isset($params["DETAIL"]) ? $params["DETAIL"] : 5;
+
+ if(!is_dir($env->path("content")."waveforms")){
+ @mkdir($env->path("content")."waveforms");
+ }
+ if(!is_dir($env->path("content")."waveforms")) return false;
+ if(!is_dir($env->path("content")."tmp")){
+ @mkdir($env->path("content")."tmp");
+ }
+ if(!is_dir($env->path("content")."tmp")) return false;
+
+ if(
+ !$this->audio_to_wav(
+ $audio_file,
+ $wav_file = $env->path("content")."tmp/".substr(md5(time()), 0, 10).".wav"
+ )
+ ) return false;
+
+ $OK = true;
+ if(
+ !(
+ $img = $this->wav_to_img(
+ $wav_file,
+ array(
+ "width" => $width,
+ "height" => $height,
+ "background" => $background,
+ "foreground" => $foreground,
+ "draw_flat" => $draw_flat,
+ "DETAIL" => $DETAIL
+ )
+ )
+ )
+ ){
+ $OK = false;
+ }
+ unlink($wav_file);
+ if(!$OK) return false;
+
+ // want it resized?
+ if ($width) {
+ // resample the image to the proportions defined in the form
+ $rimg = imagecreatetruecolor($width, $height);
+ // save alpha from original image
+ imagesavealpha($rimg, true);
+ imagealphablending($rimg, false);
+ // copy to resized
+ imagecopyresampled($rimg, $img, 0, 0, 0, 0, $width, $height, imagesx($img), imagesy($img));
+ $OK = @imagepng($rimg, $png_file);
+ imagedestroy($rimg);
+ } else {
+ $OK = @imagepng($img, $png_file);
+ }
+ imagedestroy($img);
+ if(!$OK) return false;
+ return true;
+ }
+
+ // ----------------------------------------------------------------------------
+ // utilitaires
+ //
+
+ function sox_command($audio_file, $wav_file){
+ return "sox -V \"".$audio_file."\" -r 8000 -c 1 \"".$wav_file."\"";
+ }
+
+ function sox_exists(){
+ if(isset($this->SOX_EXISTS)){
+ return $this->SOX_EXISTS;
+ }
+ $command = "sox";
+ $whereIsCommand = (PHP_OS == 'WINNT') ? 'where' : 'which';
+ if(
+ (
+ $process = proc_open(
+ "$whereIsCommand $command",
+ array(
+ 0 => array("pipe", "r"), //STDIN
+ 1 => array("pipe", "w"), //STDOUT
+ 2 => array("pipe", "w"), //STDERR
+ ),
+ $pipes
+ )
+ ) === false
+ ){
+ $this->SOX_EXISTS = false;
+ }
+ else{
+ $stdout = stream_get_contents($pipes[1]);
+ $stderr = stream_get_contents($pipes[2]);
+ fclose($pipes[1]);
+ fclose($pipes[2]);
+ proc_close($process);
+ $this->SOX_EXISTS = $stdout != '';
+ }
+ return $this->SOX_EXISTS;
+ }
+
+ function audio_to_wav($audio_file, $wav_file){
+ if(!$this->sox_exists()){
+ return false;
+ }
+ system($this->sox_command($audio_file, $wav_file));
+ return file_exists($wav_file);
+ }
+
+ function wav_to_img($wav_file, $params = array()){
+
+ $width = isset($params["width"]) ? $params["width"] : "650";
+ $height = isset($params["height"]) ? $params["height"] : "100";
+ $background = isset($params["background"]) ? $params["background"] : "";
+ $foreground = isset($params["foreground"]) ? $params["foreground"] : "#000000";
+ $draw_flat = isset($params["draw_flat"]) ? $params["draw_flat"] : true;
+
+ // $DETAIL : how much detail we want. Larger number means less detail
+ // (basically, how many bytes/frames to skip processing)
+ // the lower the number means longer processing time
+ $DETAIL = isset($params["DETAIL"]) ? $params["DETAIL"] : 5;
+
+ list($r, $g, $b) = $this->html2rgb($foreground);
+
+ if(!($handle = fopen($wav_file, "r"))) return false;
+
+ // wav file header retrieval
+ $heading[] = fread($handle, 4);
+ $heading[] = bin2hex(fread($handle, 4));
+ $heading[] = fread($handle, 4);
+ $heading[] = fread($handle, 4);
+ $heading[] = bin2hex(fread($handle, 4));
+ $heading[] = bin2hex(fread($handle, 2));
+ $heading[] = bin2hex(fread($handle, 2));
+ $heading[] = bin2hex(fread($handle, 4));
+ $heading[] = bin2hex(fread($handle, 4));
+ $heading[] = bin2hex(fread($handle, 2));
+ $heading[] = bin2hex(fread($handle, 2));
+ $heading[] = fread($handle, 4);
+ $heading[] = bin2hex(fread($handle, 4));
+
+ // wav bitrate
+ $peek = hexdec(substr($heading[10], 0, 2));
+ $byte = $peek / 8;
+
+ // checking whether a mono or stereo wav
+ $channel = hexdec(substr($heading[6], 0, 2));
+
+ $ratio = ($channel == 2 ? 40 : 80);
+
+ // start putting together the initial canvas
+ // $data_size = (size_of_file - header_bytes_read) / skipped_bytes + 1
+ $data_size = floor((filesize($wav_file) - 44) / ($ratio + $byte) + 1);
+ $data_point = 0;
+
+ if($img = imagecreatetruecolor($data_size / $DETAIL, $height)){
+ if($background == ""){
+ imagesavealpha($img, true);
+ $transparentColor = imagecolorallocatealpha($img, 0, 0, 0, 127);
+ imagefill($img, 0, 0, $transparentColor);
+ }
+ else{
+ list($br, $bg, $bb) = $this->html2rgb($background);
+ imagefilledrectangle($img, 0, 0, (int) ($data_size / $DETAIL), $height, imagecolorallocate($img, $br, $bg, $bb));
+ }
+ }
+
+ if($img) while(!feof($handle) && $data_point < $data_size){
+ if ($data_point++ % $DETAIL == 0){
+ $bytes = array();
+ for ($i = 0; $i < $byte; $i++) $bytes[$i] = fgetc($handle);
+ switch($byte){
+ // get value for 8-bit wav
+ case 1:
+ $data = $this->findValues($bytes[0], $bytes[1]);
+ break;
+ // get value for 16-bit wav
+ case 2:
+ if(ord($bytes[1]) & 128)
+ $temp = 0;
+ else
+ $temp = 128;
+ $temp = chr((ord($bytes[1]) & 127) + $temp);
+ $data = floor($this->findValues($bytes[0], $temp) / 256);
+ break;
+ }
+
+ // skip bytes for memory optimization
+ fseek($handle, $ratio, SEEK_CUR);
+ // draw this data point
+ // relative value based on height of image being generated
+ // data values can range between 0 and 255
+ $v = (int) ($data / 255 * $height);
+ // don't print flat values on the canvas if not necessary
+ if (!($v / $height == 0.5 && !$draw_flat))
+ // draw the line on the image using the $v value and centering it vertically on the canvas
+ imageline(
+ $img,
+ // x1
+ (int) ($data_point / $DETAIL),
+ // y1: height of the image minus $v as a percentage of the height for the wave amplitude
+ $height - $v,
+ // x2
+ (int) ($data_point / $DETAIL),
+ // y2: same as y1, but from the bottom of the image
+ $height - ($height - $v),
+ imagecolorallocate($img, $r, $g, $b)
+ );
+
+ } else {
+ // skip this one due to lack of detail
+ fseek($handle, $ratio + $byte, SEEK_CUR);
+ }
+ }
+ fclose($handle);
+ return $img;
+ }
+
+ function findValues($byte1, $byte2){
+ return hexdec(bin2hex($byte1)) + (hexdec(bin2hex($byte2)) * 256);
+ }
+
+ function html2rgb($input){
+ $input = $input[0] == "#" ? substr($input, 1, 6) : substr($input, 0, 6);
+ return array(
+ hexdec(substr($input, 0, 2)),
+ hexdec(substr($input, 2, 2)),
+ hexdec(substr($input, 4, 2))
+ );
+ }
+
+ }
+
+?>
\ No newline at end of file