<?php defined('SYSPATH') OR die('No direct access allowed.');
class Wave_Core {
public $width = 1906;
public $height = 100;
public $foreground = "#cccccc";
public $background = "";
public $draw_flat = false;
public $stereo = true;
public $path = null;
public $img = false;
public $converted_wav = null;
// 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
public $detail = 5;
public static function instance(){
return new self;
}
/**
* Input song data
*
* @path string image path
* @stereo_support bool
*
* @return instance
*/
public function input_file($path, $stereo_support){
$this->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))
);
}
}