path = $path; $this->stereo = $stereo_support; return $this; } /** * Convert MP3 file to Wav * * @return void */ public function convert(){ $tmpname = "temp/".Helper::uuid1(); //make file copy copy($this->path, $tmpname."_o.mp3"); // convert mp3 to wav using lame decoder // First, resample the original mp3 using as mono (-m m), 16 bit (-b 16), and 8 KHz (--resample 8) // Secondly, convert that resampled mp3 into a wav // We don't necessarily need high quality audio to produce a waveform, doing this process reduces the WAV // to it's simplest form and makes processing significantly faster exec("lame {$tmpname}_o.mp3 -m m -S -f -b 16 --resample 8 {$tmpname}.mp3 && lame -S --decode {$tmpname}.mp3 {$tmpname}.wav"); $this->converted_wav = "{$tmpname}.wav"; // delete temporary files unlink("{$tmpname}_o.mp3"); unlink("{$tmpname}.mp3"); } /** * Convert file * * @return instance */ public function generate_wave(){ //convert to wav $this->convert(); // generate foreground color list($r, $g, $b) = $this->html2rgb($this->foreground); /** * Below as posted by "zvoneM" on * http://forums.devshed.com/php-development-5/reading-16-bit-wav-file-318740.html * as findValues() defined above * Translated from Croation to English - July 11, 2011 */ $handle = fopen($this->converted_wav, "r"); // 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($this->converted_wav) - 44) / ($ratio + $byte) + 1); $data_point = 0; // now that we have the data_size for a single channel (they both will be the same) // we can initialize our image canvas if (!$this->img) { // create original image width based on amount of detail // each waveform to be processed with be $height high, but will be condensed // and resized later (if specified) $this->img = imagecreatetruecolor($data_size / $this->detail, $this->height * sizeof($this->converted_wav)); // fill background of image if ($this->background == "") { // transparent background specified imagesavealpha($this->img, true); $transparentColor = imagecolorallocatealpha($this->img, 0, 0, 0, 127); imagefill($this->img, 0, 0, $transparentColor); } else { list($br, $bg, $bb) = $this->html2rgb($this->background); imagefilledrectangle($this->img, 0, 0, (int) ($data_size / $this->detail), $this->height * sizeof($this->converted_wav), imagecolorallocate($this->img, $br, $bg, $bb)); } } imagesetthickness($this->img, 3); while(!feof($handle) && $data_point < $data_size){ if ($data_point++ % $this->detail == 0) { $bytes = array(); // get number of bytes depending on bitrate 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 * $this->height); if($v < 20) $v = 20; // don't print flat values on the canvas if not necessary if (!($v / $this->height == 0.5 && !$this->draw_flat)) // draw the line on the image using the $v value and centering it vertically on the canvas imageline( $this->img, // x1 (int) ($data_point / $this->detail), // y1: height of the image minus $v as a percentage of the height for the wave amplitude $this->height - $v, // x2 (int) ($data_point / $this->detail), // y2: same as y1, but from the bottom of the image $this->height, imagecolorallocate($this->img, 56, 56, 56) ); } else { // skip this one due to lack of detail fseek($handle, $ratio + $byte, SEEK_CUR); } } // close and cleanup fclose($handle); // delete the processed wav file unlink($this->converted_wav); return $this; } /** * Save wave file * * @return void */ public function save($path){ // resample the image to the proportions defined in the form $rimg = imagecreatetruecolor($this->width, $this->height); // save alpha from original image imagesavealpha($rimg, true); imagealphablending($rimg, false); // copy to resized imagecopyresampled($rimg, $this->img, 0, 0, 0, 0, $this->width, $this->height, imagesx($this->img), imagesy($this->img)); //save imagepng($rimg, $path); imagedestroy($rimg); imagedestroy($this->img); } /** * */ private function findValues($byte1, $byte2){ $byte1 = hexdec(bin2hex($byte1)); $byte2 = hexdec(bin2hex($byte2)); return ($byte1 + ($byte2*256)); } /** * Convert HEX to RGB * * @return array */ private 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)) ); } }