From 80964d99dd2b63fda9276903d72537091e04ba17 Mon Sep 17 00:00:00 2001 From: dj3c1t Date: Sat, 10 May 2014 00:30:03 +0200 Subject: [PATCH] generation de waveforms si sox dispo --- app/controllers/sources/waveforms.php | 203 ++++++++++++++++++++ app/data/modules/share/sml_data_waveforms.php | 262 ++++++++++++++++++++++++++ 2 files changed, 465 insertions(+) create mode 100644 app/controllers/sources/waveforms.php create mode 100644 app/data/modules/share/sml_data_waveforms.php diff --git a/app/controllers/sources/waveforms.php b/app/controllers/sources/waveforms.php new file mode 100644 index 0000000..eadba06 --- /dev/null +++ b/app/controllers/sources/waveforms.php @@ -0,0 +1,203 @@ + + + + genere la waveforme de la source d'id + + 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 diff --git a/app/data/modules/share/sml_data_waveforms.php b/app/data/modules/share/sml_data_waveforms.php new file mode 100644 index 0000000..d492a46 --- /dev/null +++ b/app/data/modules/share/sml_data_waveforms.php @@ -0,0 +1,262 @@ +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 -- 2.1.4