<?php
require('midi.class.php');
/****************************************************************************
Software: Rttl Class
Version: 0.3
Date: 2005/04/25
Author: Valentin Schmidt
Contact: fluxus@freenet.de
License: Freeware
Extends Midi Class to support the RTTL ringtone format
****************************************************************************/
class Rttl extends Midi {
var $notes = array('c','c#','d','d#','e','f','f#','g','g#','a','a#','b');
var $defaultDur = 4;
var $defaultScale = 5;
var $defaultBpm = 63;
/****************************************************************************
* *
* Public methods *
* *
****************************************************************************/
//---------------------------------------------------------------
// returns RTTL string (MIDI2RTTL conversion)
// if $title is specified, this will be the RTTL name (max. 10 characters)
// if tracknumber $tn is specified, the corresponding track will be used
//---------------------------------------------------------------
function getRttl($title='',$tn=-1){
if ($tn<0) $track = $this->_findFirstContentTrack();
else $track = $this->getTrack($tn);
$commands = array();
$last = 0;
$dt = 0;
$cnt = count($track);
for ($i=0;$i<$cnt;$i++){
$line = $track[$i];
$msg = explode(' ',$line);
// try to get title from meta event
if ($title==''&&$msg[1]=='Meta'&&$msg[2]=='TrkName') {
$title=trim($msg[3]);
if ($title{0}=='"') $title=substr($title, 1);
if ($title{strlen($title)-1}=='"') $title=substr($title, 0, -1);
}
if ($msg[1]=='On' && $msg[4]!='v=0'){
$time = $msg[0];
$pause=$time-$last-$dt;
if ($pause>0){
list($dot, $quarters) = $this->_checkDotted($pause/$this->timebase);
$dur = max(1,round(4 / $quarters));
$commands[] = ($dur!=$this->defaultDur?$dur:'').'p'.$dot;
}
// find note duration
$dt = 0;
for ($j=$i+1;$j<$cnt;$j++){
$msgNext = explode(' ',$track[$j]);
if ($msgNext[1]=='On'||$msgNext[1]=='Off'){
$dt = $msgNext[0] - $msg[0];
break;
}
}
eval("\$".$msg[3].';');
$note = $this->notes[$n % 12];
$scale = floor($n/12);
if ($dt>0){
list($dot, $quarters) = $this->_checkDotted($dt/$this->timebase);
$dur = max(1,round(4 / $quarters));
//<duration> := "1" | "2" | "4" | "8" | "16" | "32"
$commands[] = ($dur!=$this->defaultDur?$dur:'').$note.$dot.($scale!=$this->defaultScale?$scale:'');
$last = $time;
}
}
}// for
$title = ($title=='')?'mid2rttl':trim(substr($title, 0, 10));
$rttl = "$title:d={$this->defaultDur},o={$this->defaultScale},b=".$this->getBpm().":" . implode(',', $commands);
return $rttl;
}
//---------------------------------------------------------------
// import RTTL (RTTL2MIDI conversion)
//---------------------------------------------------------------
function importRttl($rttl){
list($name,$controls,$tones) = explode(':', $rttl);
$controls = explode(',', $controls);
$tones = explode(',', $tones);
foreach ($controls as $c) eval('$'.$c.';');
$this->open();
$this->type = 0;
$this->timebase = 480;// ???
$bpm = isset($b)?$b:$this->defaultBpm;
$this->tempo = round(60000000/$bpm);
$track = array();
$track[] = '0 Meta TrkName "'.$name.'"';
$track[] = '0 Tempo '.$this->tempo;
$last = 0;
$time = 0;
foreach ($tones as $tone){
preg_match ( '/^[0-9]*/', $tone, $test);
$dur = $test[0];
if ($dur == '') $dur = isset($d)?$d:$this->defaultDur;
preg_match ( '/[a-p](\#*)/', $tone, $test);
$note = $test[0];
preg_match ( '/\./', $tone, $test);
$dot = @$test[0];
preg_match ( '/[0-9]*$/', $tone, $test);
$scale = @$test[0];
if ($scale=='') $scale = isset($o)?$o:$this->defaultScale;
$quarters = 4 / $dur;
$dt = $quarters * $this->timebase;
if ($dot) $dt *= 1.5;
if ($last) {
$track[] = "$time Off ch=1 n=$last v=100";
$last = 0;
}
if ($note!='p') {
$note = 12 * $scale + array_search ( $note, $this->notes);
$track[] = "$time On ch=1 n=$note v=100";
$last = $note;
}
$time += $dt;
} // foreach
if ($last) $track[] = "$time Off ch=1 n=$last v=100";
$track[] = "$time Meta TrkEnd";
$this->tracks = array($track);
}
/****************************************************************************
* *
* Private methods *
* *
****************************************************************************/
//---------------------------------------------------------------
// finds first track containing note on events
//---------------------------------------------------------------
function _findFirstContentTrack(){
if ($this->type==0) return $this->tracks[0];
else {
foreach ($this->tracks as $track)
foreach ($track as $line){
list(,$event) = explode(' ',$line);
if ($event=='On') return $track;
}
}
return false;
}
//---------------------------------------------------------------
// handles dotted notes
//---------------------------------------------------------------
function _checkDotted($quarters){
$dotted = array(6, 3, 3/2, 3/4, 3/8, 3/16);
foreach ($dotted as $test)
// to avoid rounding errors check for +/- 10%
if (abs($quarters/$test-1)<0.1) //($this->_compare($quarters,$test))
return array('.', $quarters*2/3);
return array('', $quarters);
}
} // END OF CLASS
?>
|