generation de waveforms si sox dispo
[mw_sourceml] / app / data / modules / share / sml_data_waveforms.php
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