generation de waveforms si sox dispo sourceml.1.2.0
authordj3c1t <dj3c1t@free.fr>
Fri, 9 May 2014 22:30:03 +0000 (00:30 +0200)
committerdj3c1t <dj3c1t@free.fr>
Fri, 9 May 2014 22:30:03 +0000 (00:30 +0200)
app/controllers/sources/waveforms.php [new file with mode: 0644]
app/data/modules/share/sml_data_waveforms.php [new file with mode: 0644]

diff --git a/app/controllers/sources/waveforms.php b/app/controllers/sources/waveforms.php
new file mode 100644 (file)
index 0000000..eadba06
--- /dev/null
@@ -0,0 +1,203 @@
+<?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
diff --git a/app/data/modules/share/sml_data_waveforms.php b/app/data/modules/share/sml_data_waveforms.php
new file mode 100644 (file)
index 0000000..d492a46
--- /dev/null
@@ -0,0 +1,262 @@
+<?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