sox: encodage en 16 bits pour les waveforms
[mw_sourceml] / app / data / modules / share / sml_data_waveforms.php
1 <?php
2
3   class sml_data_waveforms extends mw_data{
4
5     var $SOX_EXISTS;
6
7     function init_waveform_dirs(){
8       $env = $this->env();
9       $dir = $env->path("content")."tmp";
10       if(!is_dir($dir)) @mkdir($dir);
11       if(!is_dir($dir)) return false;
12       $dir = $env->path("content")."waveforms";
13       if(!is_dir($dir)) @mkdir($dir);
14       if(!is_dir($dir)) return false;
15       return true;
16     }
17
18     function audio_to_png($params = array()){
19       $env = $this->env();
20       $audio_file = $params["audio_file"];
21       if(!$audio_file) return false;
22       if(!file_exists($audio_file)) return false;
23
24       $png_file = $params["png_file"];
25       if(!$png_file) return false;
26
27       $width = isset($params["width"]) ? $params["width"] : "650";
28       $height = isset($params["height"]) ? $params["height"] : "100";
29       $background = isset($params["background"]) ? $params["background"] : "";
30       $foreground = isset($params["foreground"]) ? $params["foreground"] : "#000000";
31       $draw_flat = isset($params["draw_flat"]) ? $params["draw_flat"] : true;
32       $DETAIL = isset($params["DETAIL"]) ? $params["DETAIL"] : 5;
33
34       if(!is_dir($env->path("content")."waveforms")){
35         @mkdir($env->path("content")."waveforms");
36       }
37       if(!is_dir($env->path("content")."waveforms")) return false;
38       if(!is_dir($env->path("content")."tmp")){
39         @mkdir($env->path("content")."tmp");
40       }
41       if(!is_dir($env->path("content")."tmp")) return false;
42
43       if(
44         !$this->audio_to_wav(
45           $audio_file,
46           $wav_file = $env->path("content")."tmp/".substr(md5(time()), 0, 10).".wav"
47         )
48       ) return false;
49
50       $OK = true;
51       if(
52         !(
53           $img = $this->wav_to_img(
54             $wav_file,
55             array(
56               "width" => $width,
57               "height" => $height,
58               "background" => $background,
59               "foreground" => $foreground,
60               "draw_flat" => $draw_flat,
61               "DETAIL" => $DETAIL
62             )
63           )
64         )
65       ){
66         $OK = false;
67       }
68       unlink($wav_file);
69       if(!$OK) return false;
70
71       // want it resized?
72       if ($width) {
73         // resample the image to the proportions defined in the form
74         $rimg = imagecreatetruecolor($width, $height);
75         // save alpha from original image
76         imagesavealpha($rimg, true);
77         imagealphablending($rimg, false);
78         // copy to resized
79         imagecopyresampled($rimg, $img, 0, 0, 0, 0, $width, $height, imagesx($img), imagesy($img));
80         $OK = @imagepng($rimg, $png_file);
81         imagedestroy($rimg);
82       } else {
83         $OK = @imagepng($img, $png_file);
84       }
85       imagedestroy($img);
86       if(!$OK) return false;
87       return true;
88     }
89
90     // ----------------------------------------------------------------------------
91     //                                                                  utilitaires
92     //
93
94     function sox_command($audio_file, $wav_file){
95       return "sox -V \"".$audio_file."\" -b 16 -r 8000 -c 1 \"".$wav_file."\"";
96     }
97
98     function sox_exists(){
99       if(isset($this->SOX_EXISTS)){
100         return $this->SOX_EXISTS;
101       }
102       $command = "sox";
103       $whereIsCommand = (PHP_OS == 'WINNT') ? 'where' : 'which';
104       if(
105         (
106           $process = proc_open(
107             "$whereIsCommand $command",
108             array(
109               0 => array("pipe", "r"), //STDIN
110               1 => array("pipe", "w"), //STDOUT
111               2 => array("pipe", "w"), //STDERR
112             ),
113             $pipes
114           )
115         ) === false
116       ){
117         $this->SOX_EXISTS = false;
118       }
119       else{
120         $stdout = stream_get_contents($pipes[1]);
121         $stderr = stream_get_contents($pipes[2]);
122         fclose($pipes[1]);
123         fclose($pipes[2]);
124         proc_close($process);
125         $this->SOX_EXISTS = $stdout != '';
126       }
127       return $this->SOX_EXISTS;
128     }
129
130     function audio_to_wav($audio_file, $wav_file){
131       if(!$this->sox_exists()){
132         return false;
133       }
134       system($this->sox_command($audio_file, $wav_file));
135       return file_exists($wav_file);
136     }
137
138     function wav_to_img($wav_file, $params = array()){
139
140       $width = isset($params["width"]) ? $params["width"] : "650";
141       $height = isset($params["height"]) ? $params["height"] : "100";
142       $background = isset($params["background"]) ? $params["background"] : "";
143       $foreground = isset($params["foreground"]) ? $params["foreground"] : "#000000";
144       $draw_flat = isset($params["draw_flat"]) ? $params["draw_flat"] : true;
145
146       // $DETAIL : how much detail we want. Larger number means less detail
147       // (basically, how many bytes/frames to skip processing)
148       // the lower the number means longer processing time
149       $DETAIL = isset($params["DETAIL"]) ? $params["DETAIL"] : 5;
150
151       list($r, $g, $b) = $this->html2rgb($foreground);
152
153       if(!($handle = fopen($wav_file, "r"))) return false;
154
155       // wav file header retrieval
156       $heading[] = fread($handle, 4);
157       $heading[] = bin2hex(fread($handle, 4));
158       $heading[] = fread($handle, 4);
159       $heading[] = fread($handle, 4);
160       $heading[] = bin2hex(fread($handle, 4));
161       $heading[] = bin2hex(fread($handle, 2));
162       $heading[] = bin2hex(fread($handle, 2));
163       $heading[] = bin2hex(fread($handle, 4));
164       $heading[] = bin2hex(fread($handle, 4));
165       $heading[] = bin2hex(fread($handle, 2));
166       $heading[] = bin2hex(fread($handle, 2));
167       $heading[] = fread($handle, 4);
168       $heading[] = bin2hex(fread($handle, 4));
169
170       // wav bitrate
171       $peek = hexdec(substr($heading[10], 0, 2));
172       $byte = $peek / 8;
173
174       // checking whether a mono or stereo wav
175       $channel = hexdec(substr($heading[6], 0, 2));
176
177       $ratio = ($channel == 2 ? 40 : 80);
178
179       // start putting together the initial canvas
180       // $data_size = (size_of_file - header_bytes_read) / skipped_bytes + 1
181       $data_size = floor((filesize($wav_file) - 44) / ($ratio + $byte) + 1);
182       $data_point = 0;
183
184       if($img = imagecreatetruecolor($data_size / $DETAIL, $height)){
185         if($background == ""){
186           imagesavealpha($img, true);
187           $transparentColor = imagecolorallocatealpha($img, 0, 0, 0, 127);
188           imagefill($img, 0, 0, $transparentColor);
189         }
190         else{
191           list($br, $bg, $bb) = $this->html2rgb($background);
192           imagefilledrectangle($img, 0, 0, (int) ($data_size / $DETAIL), $height, imagecolorallocate($img, $br, $bg, $bb));
193         }
194       }
195
196       if($img) while(!feof($handle) && $data_point < $data_size){
197         if ($data_point++ % $DETAIL == 0){
198           $bytes = array();
199           for ($i = 0; $i < $byte; $i++) $bytes[$i] = fgetc($handle);
200           switch($byte){
201             // get value for 8-bit wav
202             case 1:
203               $data = $this->findValues($bytes[0], $bytes[1]);
204               break;
205             // get value for 16-bit wav
206             case 2:
207               if(ord($bytes[1]) & 128)
208                 $temp = 0;
209               else
210                 $temp = 128;
211               $temp = chr((ord($bytes[1]) & 127) + $temp);
212               $data = floor($this->findValues($bytes[0], $temp) / 256);
213               break;
214           }
215
216           // skip bytes for memory optimization
217           fseek($handle, $ratio, SEEK_CUR);
218           // draw this data point
219           // relative value based on height of image being generated
220           // data values can range between 0 and 255
221           $v = (int) ($data / 255 * $height);
222           // don't print flat values on the canvas if not necessary
223           if (!($v / $height == 0.5 && !$draw_flat))
224             // draw the line on the image using the $v value and centering it vertically on the canvas
225             imageline(
226               $img,
227               // x1
228               (int) ($data_point / $DETAIL),
229               // y1: height of the image minus $v as a percentage of the height for the wave amplitude
230               $height - $v,
231               // x2
232               (int) ($data_point / $DETAIL),
233               // y2: same as y1, but from the bottom of the image
234               $height - ($height - $v),
235               imagecolorallocate($img, $r, $g, $b)
236             );
237
238         } else {
239           // skip this one due to lack of detail
240           fseek($handle, $ratio + $byte, SEEK_CUR);
241         }
242       }
243       fclose($handle);
244       return $img;
245     }
246
247     function findValues($byte1, $byte2){
248       return hexdec(bin2hex($byte1)) + (hexdec(bin2hex($byte2)) * 256);
249     }
250
251     function html2rgb($input){
252       $input = $input[0] == "#" ? substr($input, 1, 6) : substr($input, 0, 6);
253       return array(
254        hexdec(substr($input, 0, 2)),
255        hexdec(substr($input, 2, 2)),
256        hexdec(substr($input, 4, 2))
257       );
258     }
259
260   }
261
262 ?>