<?php
require_once('gdext.inc.php');
/**
* PHP Chord
* Chord class for PHP 5
*
* Version: 1.0
* Release: 2010-07-25
* Author: Tony Pottier
* Website: http://www.tonypottier.info
* Contact: hide@address.com
*
* Don't hesitate to contact me for any suggestion, feature request
* and/or bug fixes.
*
* This work is published under the Creative Commons
* Attribution-NonCommercial 3.0 license
* http://creativecommons.org/licenses/by-nc/3.0/
*
* To obtain a commercial license please contact me.
*
*/
class chord
{
private $data;
private $barre;
private $width;
private $height;
private $margin;
private $bgColor;
private $bgPicture;
private $gridColor;
private $symbolColor;
private $maxFrets;
private $circleSize;
private $startingFret;
private $nutSize;
private $showUnstrummedStrings;
private $showZeros;
private $upperSymbolSize;
private $fontStartingFret;
private $fontStartingFretSize;
private $title;
private $fontTitle;
private $fontTitleSize;
/**
* Build a new chord object
*
* @param array $data
*/
function __construct($data = array())
{
if(is_array($data))
{
$this->data = $data;
$this->width = 90;
$this->height = 120;
$this->margin = array('top'=>5,'right'=>7,'bottom'=>5,'left'=>7);
$this->bgColor = array(255,255,255);
$this->gridColor = array(0,0,0);
$this->symbolColor = array(0,0,0);
$this->maxFrets = 5;
$this->fontStartingFret = 1;
$this->fontStartingFretSize = 10.0;
$this->fontTitle = 5;
$this->fontTitleSize = 16.0;
$this->bgPicture = '';
$this->circleSize = 0.5; //proportional to fret spacing size
$this->barreSize = 0.30; //proportional to fret spacing size
$this->nutSize = 0.15; //proportional to fret spacing size
$this->startingFret = 0;
$this->showUnstrummedStrings = true;
$this->showZeros = true;
$this->upperSymbolSize = 0.5; //proportional to string spacing size
}
else
{
throw new Exception('You must provide an array');
}
}
/**
* Draw the chord according to given parameters
*
*/
function draw()
{
header('Content-type: image/png');
$img = imagecreatetruecolor($this->width, $this->height);
if(!$img)
throw new Exception('Cannot Initialize new GD image stream');
// Values computed and used for the drawing
//count number of strings and compute space between strings
$nbStrings = count($this->data);
$realWidth = $this->width - $this->margin['left'] - $this->margin['right'];
$stringSpacing = ($realWidth / ($nbStrings-1));
$upperSymbolSize = intval(round($stringSpacing * $this->upperSymbolSize)); //space needed to draw the "o" or "x"
$totalTopMargin = $this->margin['top'] + $upperSymbolSize;
$titleMargin = 0;
//if theres a title, compute additionnal top margin induced
if(!empty($this->title))
{
if(is_int($this->fontTitle))
{
$titleMargin = imagefontheight($this->fontTitle);
}
else
{
$box = imagettfbboxextended($this->fontTitleSize, 0, $this->fontTitle, $this->title);
$titleMargin = $box['height'];
}
}
$totalTopMargin += $titleMargin;
$realHeight = $this->height - $totalTopMargin - $this->margin['bottom'];
$fretsSpacing = $realHeight / ($this->maxFrets);
//create colors
$bgColor = imagecolorallocate($img, $this->bgColor[0], $this->bgColor[1], $this->bgColor[2]);
$gridColor = imagecolorallocate($img, $this->gridColor[0], $this->gridColor[1], $this->gridColor[2]);
$symbolColor = imagecolorallocate($img, $this->symbolColor[0], $this->symbolColor[1], $this->symbolColor[2]);
//clear image with background color
imagefilledrectangle($img, 0, 0, $this->width, $this->height, $bgColor);
//if there is a background picture, set it
if(!empty($this->bgPicture))
{
$bg = file_get_contents($this->bgPicture);
if($bg)
{
$bgPic = imagecreatefromstring($bg);
if($bgPic)
{
imagecopy($img, $bgPic, 0, 0, 0, 0, $this->width, $this->height);
imagedestroy($bgPic);
}
}
}
//if there's a title, draw it
if(!empty($this->title))
{
if(is_int($this->fontTitle))
{
$font_width = imagefontwidth($this->fontTitle);
imagestring($img, $this->fontTitle, ($this->width - $font_width) / 2.0, $this->margin['top'], $this->title, $symbolColor);
}
else
{
$box = imagettfbboxextended($this->fontTitleSize, 0, $this->fontTitle, $this->title);
$font_width = $box['width'];
imagettftext($img, $this->fontTitleSize, 0, ($this->width - $font_width) / 2.0, $this->margin['top'] + $box['top'] - 4, $symbolColor, $this->fontTitle, $this->title);
}
}
//draw the strings
$xoffset = 0;
for($i=0;$i<$nbStrings;$i++)
{
imageline($img, $this->margin['left']+$xoffset, $totalTopMargin, $this->margin['left']+$xoffset, $this->height - $this->margin['bottom'], $gridColor);
$xoffset += $stringSpacing;
}
//draw the frets
$yoffset = 0;
for($i=0;$i<=$this->maxFrets;$i++)
{
if($i==0 && $this->startingFret == 0)
{
//if the starting fret is 0 then we draw the nut
imagefilledrectangle($img, $this->margin['left'], $totalTopMargin + $yoffset, $this->width - $this->margin['right'], $totalTopMargin + ($fretsSpacing*$this->nutSize) , $gridColor);
}
else
{
imageline($img, $this->margin['left'], $totalTopMargin + $yoffset, $this->width - $this->margin['right'], $totalTopMargin + $yoffset, $gridColor);
}
$yoffset += $fretsSpacing;
}
//if the starting fret isn't 0 add a mention with a 2 pixel margin
if($this->startingFret != 0)
{
$str = sprintf('%dfr.', $this->startingFret);
if(is_int($this->fontStartingFret))
{
$font_height = imagefontheight($this->fontStartingFret);
imagestring($img, $this->fontStartingFret, $this->width - $this->margin['right'] + 2, $totalTopMargin - ($font_height / 2.0), $str, $symbolColor);
}
else
{
$box = imagettfbboxextended($this->fontStartingFretSize, 0, $this->fontStartingFret, $str);
imagettftext($img, $this->fontStartingFretSize, 0, $this->width - $this->margin['right'] + 2, $totalTopMargin - ($box['height'] / 2.0) + $box['top'], $symbolColor, $this->fontStartingFret, $str);
}
}
//draw chord
$xoffset = 0;
for($i=0;$i<$nbStrings;$i++)
{
if($this->data[$i] === 'x')
{
if($this->showUnstrummedStrings)
{
imagecross($img, $this->margin['left'] + $xoffset - $upperSymbolSize/2.0, $this->margin['top'] + $titleMargin - 1, $this->margin['left']+$xoffset+$upperSymbolSize/2.0, $this->margin['top'] + $titleMargin+$upperSymbolSize, $symbolColor);
}
}
else
{
$stringPos = intval($this->data[$i]);
if($stringPos)
{
$yoffset = ($fretsSpacing / 2) + $fretsSpacing*($stringPos-1-$this->startingFret);
//imagefilledellipse($img, $this->margin['left']+$xoffset, $this->margin['top'] + $yoffset, $fretsSpacing*$this->circleSize, $fretsSpacing*$this->circleSize, $gridColor);
imagesmoothcircle($img, $this->margin['left']+$xoffset, $totalTopMargin + $yoffset, intval(round($fretsSpacing*$this->circleSize/2.0)), array( 'R' => $this->symbolColor[0], 'G' => $this->symbolColor[1], 'B' => $this->symbolColor[2] ));
}
else
{
if($this->showZeros)
{
imageellipse($img, $this->margin['left']+$xoffset, $this->margin['top'] + $upperSymbolSize / 2.0 - 1 + $titleMargin, $upperSymbolSize, $upperSymbolSize, $symbolColor);
}
}
}
$xoffset += $stringSpacing;
}
//draw barre chords
if(count($this->barre))
{
$x1 = 0;
$x2 = 0;
$yoffset = 0;
foreach($this->barre as $fret=>$bar)
{
$yoffset = ($fretsSpacing / 2) + $fretsSpacing*($fret-1-$this->startingFret);
$x1 = ($bar[0]-1) * $stringSpacing;
$x2 = ($bar[1]-1) * $stringSpacing;
imagesmoothcircle($img, $this->margin['left']+$x1, $totalTopMargin + $yoffset, intval(round($fretsSpacing*$this->barreSize/2.0)), array( 'R' => $this->symbolColor[0], 'G' => $this->symbolColor[1], 'B' => $this->symbolColor[2] ));
imagesmoothcircle($img, $this->margin['left']+$x2, $totalTopMargin + $yoffset, intval(round($fretsSpacing*$this->barreSize/2.0)), array( 'R' => $this->symbolColor[0], 'G' => $this->symbolColor[1], 'B' => $this->symbolColor[2] ));
imagefilledrectangle($img, $this->margin['left']+$x1, $totalTopMargin + $yoffset - intval(round($fretsSpacing*$this->barreSize/2.0)), $this->margin['left']+$x2, $totalTopMargin + $yoffset + intval(round($fretsSpacing*$this->barreSize/2.0)), $symbolColor);
}
}
imagepng($img);
imagedestroy($img);
}
//The code below is made of uninteresting getters and setters
//Some basic checks are made and the functions try to be as flexible as possible:
//ie you can enter a numeric string for setWidth for instance,
//but if you really feed the setters something it cannot understand, an
//exception will be thrown
/**
* Set image width
*
* @param int $v
*/
function setWidth($v) {$v = intval($v); if($v > 0) {$this->width=$v;} else {throw new Exception('Strictly positive integers only');}}
/**
* Get image width
*
* @return int
*/
function getWidth() {return $this->width;}
/**
* Set image height
*
* @param int $v
*/
function setHeight($v) {$v = intval($v); if($v > 0) {$this->height=$v;} else {throw new Exception('Strictly positive integers only');}}
/**
* Get image width
*
* @return int
*/
function getHeight() {return $this->height;}
/**
* Provide a 4 value array using the CSS like order top, right, bottom, left
*
* @param array $v
*/
function setMargin($v)
{
if(is_array($v) && count($v) >= 4) { $this->margin = array('top'=>intval($v[0]),'right'=>intval($v[1]),'bottom'=>intval($v[2]),'left'=>intval($v[3])); }
else {throw new Exception('Please provide an array');}
}
/**
* Get margin
*
* @return array
*/
function getMargin() { return $this->margin;}
function setMarginTop($v) {$v = intval($v); if($v > 0) {$this->margin['top']=$v;} else {throw new Exception('Strictly positive integers only');}}
function getMarginTop() {return $this->margin['top'];}
function setMarginRight($v) {$v = intval($v); if($v > 0) {$this->margin['right']=$v;} else {throw new Exception('Strictly positive integers only');}}
function getMarginRight() {return $this->margin['right'];}
function setMarginBottom($v) {$v = intval($v); if($v > 0) {$this->margin['bottom']=$v;} else {throw new Exception('Strictly positive integers only');}}
function getMarginBottom() {return $this->margin['bottom'];}
function setMarginLeft($v) {$v = intval($v); if($v > 0) {$this->margin['left']=$v;} else {throw new Exception('Strictly positive integers only');}}
function getMarginLeft() {return $this->margin['left'];}
/**
* Set background color.
*
* @param int $r
* @param int $g
* @param int $b
*/
function setBgColor($r,$g,$b){$r=intval($r); $g=intval($g); $b=intval($b); if($r>=0&&$g>=0&&$b>=0){$this->bgColor=array($r,$g,$b);}else{throw new Exception('positive integers only');}}
/**
* Get background color as an ordered array(r,g,b)
*
* @return array
*/
function getBgColor(){return $this->bgColor;}
/**
* Set symbol colors
*
* @param int $r
* @param int $g
* @param int $b
*/
function setSymbolColor($r,$g,$b){$r=intval($r); $g=intval($g); $b=intval($b); if($r>=0&&$g>=0&&$b>=0){$this->symbolColor=array($r,$g,$b);}else{throw new Exception('positive integers only');}}
/**
* Get symbol color as an ordered array(r,g,b)
*
* @return array
*/
function getSymbolColor(){return $this->symbolColor;}
/**
* Set grid color.
*
* @param int $r
* @param int $g
* @param int $b
*/
function setGridColor($r,$g,$b){$r=intval($r); $g=intval($g); $b=intval($b); if($r>=0&&$g>=0&&$b>=0){$this->gridColor=array($r,$g,$b);}else{throw new Exception('positive integers only');}}
/**
* Get background color as an ordered array(r,g,b)
*
* @return array
*/
function getGridColor(){return $this->gridColor;}
/**
* Maximum number of frets to display. Default is 5.
*
* @param int $v
*/
function setMaxFrets($v) {$v = intval($v); if($v > 0) {$this->maxFrets=$v;} else {throw new Exception('Strictly positive integers only');}}
function getMaxFrets(){return $this->maxFrets;}
/**
* The starting fret to display is 0 by default.
*
* @param bool $v
*/
function setStartingFret($v) {$v = intval($v); if($v >= 0) {$this->startingFret=$v;} else {throw new Exception('positive integers only');}}
function getStartingFret() {return $this->startingFret;}
/**
* Will display a cross on top by default for unstrummed strings
*
* @param bool $v
*/
function setShowUnstrummedStrings($v){$this->showUnstrummedStrings = (bool)$v;}
function getShowUnstrummedString(){return $this->showUnstrummedStrings;}
/**
* Will display a "o" by default for unfretted strings
*/
function setShowZeros($v){$this->showZeros = (bool)$v;}
function getShowZeros(){return $this->showZeros;}
/**
* Fretted string symbol size, related to the space between frets
*/
function setCircleSize($v){if(is_numeric($v)){$this->circleSize = $v;}else{throw new Exception('Floating point numbers only');}}
function getCircleSize(){return $this->circleSize;}
/**
* Size of the nut, related to the space between frets
*/
function setNutSize($v){if(is_numeric($v)){$this->nutSize = $v;}else{throw new Exception('Floating point numbers only');}}
function getNutSize(){return $this->nutSize;}
/**
* Size of the upper symbols ("x" and "o"), related to the space between frets
*/
function setUpperSymbolSize($v){if(is_numeric($v)){$this->upperSymbolSize = $v;}else{throw new Exception('Floating point numbers only');}}
function getUpperSymbolSize(){return $this->upperSymbolSize;}
function setBgPicture($v){$this->bgPicture = $v;}
function getBgPicture(){return $this->bgPicture;}
/**
* Remove all barre chords
*/
function clearBarreChords()
{$this->barre = array();}
/**
* Add a replace a barre chord on fret $fret, from string $fromString
* to string $toString
*
* @param int $fret
* @param int $fromString
* @param int $toString
*/
function setBarreChord($fret, $fromString, $toString)
{
$this->barre[$fret] = array($fromString, $toString);
}
/**
* Remove a previously set barre chord on fret $fret
*
* @param int $fret
*/
function removeBarreChard($fret)
{
if(isset($this->barre[$fret]))
unset($this->barre[$fret]);
}
/**
* Set font starting fret. It can be built in PHP font (from 1 to 5) or a TTF file
* in that case dont forget to set the font size
*
* @param mixed $v
*/
function setFontStartingFret($v)
{
if(is_int($v))
{
//1 to 5 is the correct range for built in fonts
if($v >= 1 && $v <= 5)
$this->fontStartingFret = $v;
else
throw new Exception('Built in font must be in 1 to 5 range');
}
else if(is_string($v))
{
$this->fontStartingFret = $v;
}
else
{
throw new Exception('Use a TTF file or a number reprensenting a built in font');
}
}
function getFontStartingFret() { return $this->fontStartingFret;}
function setFontStartingFretSize($v){if(is_numeric($v)){$this->fontStartingFretSize = $v;}else{throw new Exception('Floating point numbers only');}}
function getFontStartingFretSize(){return $this->fontStartingFretSize;}
/**
* Set font title if there is one. It can be built in PHP font (from 1 to 5) or a TTF file
* in that case dont forget to set the font size
*
* @param mixed $v
*/
function setFontTitle($v)
{
if(is_int($v))
{
//1 to 5 is the correct range for built in fonts
if($v >= 1 && $v <= 5)
$this->fontTitle = $v;
else
throw new Exception('Built in font must be in 1 to 5 range');
}
else if(is_string($v))
{
$this->fontTitle = $v;
}
else
{
throw new Exception('Use a TTF file or a number reprensenting a built in font');
}
}
function getFontTitle() { return $this->fontStartingFret;}
function setFontTitleSize($v){if(is_numeric($v)){$this->fontTitleSize = $v;}else{throw new Exception('Floating point numbers only');}}
function getFontTitleSize(){return $this->fontTitleSize;}
/**
* Title displayed on top of the chord. Set to empty string to hide
*
* @param mixed $v
*/
function setTitle($v) {$this->title=$v;}
function getTitle(){return $this->title;}
}
?>