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; } public 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 // public function sox_command($audio_file, $wav_file){ return "sox -V \"".$audio_file."\" -b 16 -r 8000 -c 1 \"".$wav_file."\""; } public 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; } public 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); } public 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; } public function findValues($byte1, $byte2){ return hexdec(bin2hex($byte1)) + (hexdec(bin2hex($byte2)) * 256); } public 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)) ); } }