"Whole", 2 => "Half", 4 => "4th", 8 => "8th", 16 => "16th", 32 => "32nd", 64 => "64th" ) ; $IDidntConvertAny = 1 ; function abort ($msg) { fputs(STDERR, $msg) ; exit(1) ; } function help_msg_and_exit() { echo 'Usage: php tripletise.php ... where... t is the resulting triplet note length (4 for quarter, 8 for eighth, 16 for 16th etc) and r1 r2 r3 etc are rhythm descriptions of the patterns of notes to be converted to a triplet. Rhythm descriptions are [d][t] where d is an optional indicator of dotted note, n is 2 for half, 4 for quarter, 8 for eighth, etc and t is an optional indicator of a tied note I chose to use this generic format to provide greater flexibility for differing import granularity and differing triplet types Examples of common triplet imports are d16 d16 16 (eighth note triplets imported on default settings) 16 32 32 (16th notes triplets imported on default settings) d8 16t d16 8t 32 (quarter notes, imported on default settings) d8 16t 16 d8 (quarter notes imported on 16th notes granularity) 16 8 16 (eighth note triplet imported on 16th note granularity) As a check, tripletise adds these together to ensure that they make double the parameter eg tripletise.php 8 16 8 16 means convert 16+8+16 into triplets of eighth notes, which total a quarter. Author: Andrew Purdam Modified by Gennaro Granito to allow a rest as part of a triplet. ' ; exit(99) ; } function RhythmLength( $arg ) { if (preg_match("/d(\d+)/", $arg, $m)) { return( (64/$m[1]) * 1.5) ; } if (preg_match("/(\d+)/", $arg, $m)) { return( 64/$m[1]) ; } // ignore possible trailing ties for now } function IsADiscreetNote( $arg ) { return (! preg_match("/t$/", $arg)) ; // the trailing t means that it is tied } function splitClipLineIntoAttributes ($arg) // eg |Chord|Dur:8th,Grace,Slur|Pos:-9,-2 returns "Chord" "Dur:8th,Grace,Slur" "Pos:-9,02" { $tolist = explode ("|", $arg) ; array_shift($tolist) ; return $tolist ; } function extractAttributeValue($line, $attName) // eg given ("|Chord|Dur:8th,Grace,Slur|Pos:-9,-2", "Pos"), returns "-9,-2" { foreach (splitClipLineIntoAttributes($line) as $attBlock) { $regexp = "/^$attName:(.*)/" ; if (preg_match($regexp, $attBlock, $m)) return $m[1] ; } return "" ; } function LineMatchesRhythmElement($line, $RhythmElement, $RestPart) { global $DurText ; if (preg_match("/\|Rest/",$line) and $RestPart) { return 0; } // allow one rest, but not more // RhythmElement is of form [d]n[t] if (preg_match("/(\d+)/", $RhythmElement, $m)) { $length = $m[1] ; } $length = $DurText[ $length ] ; if (preg_match("/^d/", $RhythmElement)) { $length .= ",Dotted" ; } ; $regexp = "/Dur:$length/" ; if (!preg_match($regexp, $line)) { return 0 ; } // matches the length, so what about ties? return ( preg_match("/t$/", $RhythmElement) == preg_match("/\^/", extractAttributeValue($line, "Pos"))) ; } function ChangeAttribute($line, $att, $changeto) // Change an element attribute in a line (eg |Pos:x-3 to |Pos:50) { // either get rid of the attribute up to the next | ... $regexp = "/$att:.*?\|/" ; $replexp = "$att:|" ; $line = preg_replace($regexp, $replexp, $line) ; // ... or to end of line $regexp = "/$att:[^|]*?$/" ; $replexp = "$att:" ; // need to exclude matches already made above $line = preg_replace($regexp, $replexp, $line) ; // and sub in new one $regexp = "/$att:/" ; $line = preg_replace( $regexp, $changeto, $line ) ; return $line ; } // THE MAIN ACTION STARTS HERE // Scan and check TripletSize from command line array_shift( $argv ) ; $TripletSize = array_shift( $argv ) ; if ($TripletSize == "help") help_msg_and_exit() ; if (!(preg_match("/^(1|2|4|8|16|32)$/", $TripletSize))) { abort("1st parameter (triplet size) must be 2,4,8,16 or 32") ; } ; // Scan and check rhythms from command line $rhythms = $argv ; foreach ($rhythms as $rhy) { if (! preg_match("/d?(1|2|4|8|16|32|64)t?/", $rhy)) {abort("Parameter $rhy must be of form [d]n[t] where\nn is 2,4,8,16,32 or 64. d and t indicate dotted and/or tied.") ; } } // okay, they are well formed within themselves... now to see if we have enough of the right stuff. //We'll add up all the rhythms and see if there is a total that is double the 1st param's note length, and are three discreet notes $TotalRhythmLength = 0 ; // in 64ths. foreach ($rhythms as $rhy) { $TotalRhythmLength += RhythmLength($rhy) ; } if ($TotalRhythmLength != 2 * 64 / $TripletSize) { abort("Length of Rhythm must add up to double Triplet Size (parameter 1).") ; } $NumDiscreetNotes = 0 ; foreach ($rhythms as $rhy) { $NumDiscreetNotes += IsADiscreetNote($rhy) ; } if ($NumDiscreetNotes != 3) { abort("Number of discreet notes must be three. Check your ties.") ; } $EltNum = 0 ; // Rhythmic Element Number $RestPart = 0 ; // Position of Rest within triplet $StoredLines = array ( ) ; // Scan the stream - we're looking for series of consecutive notes which match the rhythm pattern described by @rhythms $wholeClip = file('php://stdin'); $noInput = TRUE ; foreach ($wholeClip as $line) // while there are lines... read them and { // deal with the whole shebang triplet by triplet, so store once we think it's part of one if (preg_match("/\|Note|\|Chord|\|Rest/", $line)) // is it a note, chord, or something that might break the pattern? { $noInput = FALSE ; if (LineMatchesRhythmElement($line, $rhythms[$EltNum], $RestPart) ) { // echo "Matched $rhythms[$EltNum] to $line" ; array_push($StoredLines, $line) ; $EltNum++ ; if (preg_match("/\|Rest/", $line)) { $RestPart = $EltNum ; } if ($EltNum >= count($rhythms)) // now go through and { $IDidntConvertAny = 0 ; $tiedNote = 0 ; $tripletPart = 0 ; foreach ($StoredLines as $line) { if (preg_match("/\|Note|\|Chord|\|Rest/", $line)) { if (!$tiedNote) { $toprint = ChangeAttribute($line, "Dur", "Dur:".$DurText[$TripletSize].$TripletText[$tripletPart]) ; if (preg_match("/\|Rest/", $toprint)) { $toprint .= ",Beam" ; } // Need to adjust beam and remove ties $toprint = preg_replace("/,Beam(=First|=End)/", ",Beam", $toprint) ; $toprint = preg_replace("/,Beam/", $BeamText[$tripletPart + (3 * $RestPart)], $toprint) ; $toprint = preg_replace("/\^/", "", $toprint) ; // Strip off any ties in pos echo $toprint ; $tripletPart++ ; } $tiedNote = preg_match("/\^/", extractAttributeValue($line, "Pos")) ; // evaluate if a tied note (^ at the end of a Pos) } else { echo $line ; } } //end of for @StoredLines $StoredLines = array ( ) ; $EltNum = 0 ; $RestPart = 0 ; } // end of EltNum > count($rhythms) } // end LineMatchesRhythmElement else { array_push($StoredLines, $line ); foreach ($StoredLines as $line) echo $line; // flush and zero buffer $StoredLines = array( ) ; $EltNum = 0 ; $RestPart = 0 ; } } else { array_push($StoredLines, $line) ; } ; // put in buffer } ; foreach ($StoredLines as $line) echo $line; // flush any residual lines if ($noInput) abort("This tool requires a selection to work. Please select some notes/chords on your staff and try again.") ; if ($IDidntConvertAny) // report back mismatch to prompt user to change arguments { $abortString = "Didn't find any rhythmic patterns that matched " ; foreach ($rhythms as $rhy) { $abortString .= "$rhy " ; } $abortString .= " Perhaps you need to alter the parameters? Remember to use a pattern with elements of the form [d]n[t] where d=dotted, t=tied and n=2,4,8,16,32,64 Use \"help\" for more details." ; abort ($abortString) ; } exit (0) ; ?>