[,,...] Where: is either cut - delete the matching objects (replaces time-consuming notes with rests) copy - copy matching objects to scratch merge - merge scratch with this staff paste - paste scratch onto end of this staff Cut or copied data is saved to a file called SCRATCH.TXT in the folder that the tool runs in (normally the NWC2 program folder). is eg Note, Chord, Clef, Key, TimeSig, Dynamic etc, or the reserved word \"ALL\". Note that keywords are case sensitive. is of form , meaning the named option is present or ==, meaning that is the given value, or value is present if opt is an array. or <=, meaning that the single value of opt is LTE value, ditto >, <, >= and != NB: If is . it will deal with subopt as a subtype of opt. eg Dur.Dotted or Opts.VertOffset>=0 If no type or comparison is made, everything will be copied or cut. example: To merge two staves: 1) Select first stave. Use php scratch.php copy Note Chord 2) Select stave to merge to. Use php scratch.php merge " ; exit(NWC2RC_REPORT) ; } // Copy, cut and paste are pretty straightforward. See the main loop for their logic. // Merge is quite tricky. I need to do the following... // 1) Conglomerate rests. This is so that I can make sensible decisions with merging // rests with notes, to minimise restchords. // To save me doing 300 extra lines, this must be done by the user. // 2) Gather all non-time-consuming objects before each time consuming one, and // rationalise between scratch and "mergee". eg timesigs, clefs and key sigs. // I'll need to decide whom to give precedence to. My feeling is scratch, but // I'm not sure why. Maybe need to do both. // 3) Output the objects from (2). Then merge time-consumers (notes, chords, rests, restchords) // This may prove impossible for two chords that each have more than one time in them. // Should print a warning. // So main loop for merge is... // a) If this item is not time consumer, store it // b) If it is a time consumer, gather all of scratch's non time consumer, and // merge them and print them // Then merge the two time consumers. Need to remember when to start merging // the next item from the staff with the longer note. function inform($string) { fputs(STDERR, $string) ; } $firstErrorInformation = TRUE ; function inform_error($string) { global $firstErrorInformation ; if ($firstErrorInformation) { inform("In the following error report, where a merge can't take place, one of the merge items appears after the other, marked in Color 1 (red by default) and set as a grace note.\n\n") ; $firstErrorInformation = FALSE ; } inform($string) ; } function &gm_getTaggedOpt($tag, &$o) // eg tag might be Dur.DblDotted or Opts.VertOffset { $result = FALSE ; $opts =& $o->GetOpts() ; foreach (explode(".",$tag) as $t) { if (!isset($opts[$t])) return ($result = FALSE) ; $opts =& $opts[$t] ; } return $opts ; } function gm_setTaggedOpt($tag, &$o, $val) // eg gm_setTaggedOpt("Opts.VertOffset", $o, "3") { $opts =& $o->GetOpts() ; foreach (explode(".",$tag) as $t) { if (!isset($opts[$t])) $opts[$t] = "" ; $opts =& $opts[$t] ; } $opts = $val ; } // allowed comparisons $gm_comparisons = array("<=", "<", "==", ">=", ">", "!=") ; class gm_match { var $maintype = "" ; // string, eg Note, Chord, Tempo var $present = array( ) ; // optional array of Opt names, which must be present in the Clip object for Matches($o) to return true var $comparisons = array( ) ; // comparison, string representation of comparison eg "Pos" => "==" var $comparesTo = array( ) ; // values to compare to, eg "Pos" => 30, or "Dur" => "Whole" function gm_match ($matchtext) { $gm_comparisons =& $GLOBALS['gm_comparisons'] ; $matchlist = explode(",", $matchtext) ; $this->maintype = array_shift($matchlist) ; foreach( $matchlist as $match ) // go through remainder of match criteria { foreach ($gm_comparisons as $comp) if (strstr($match, $comp)) { list($optName,$compValue) = explode($comp, $match, 2) ; $this->comparisons[$optName] = $comp ; $this->comparesTo[$optName] = $compValue ; $match = "" ; break ; // Don't try and match any other comparisons } if ($match != "") $this->present[$match] = "" ; if (strstr($match,"=")) inform("Single = (assignment) found in comparison \"$match\".\nIf you intend to compare equality, use \"==\"\nI'll continue anyway.\n") ; } } function Matches($o) // true if this gm_match instance Matches the supplied NWCClipObject // to do so, the main type must match, if an option was named, it must be present, and if // an option was named with a comparison, it must compare true. Any failures return false. It's a hard world. { if ($this->maintype == "ALL") return TRUE ; if ($o->GetObjType() != $this->maintype) return FALSE ; foreach ($this->present as $presence => $val) if (gm_getTaggedOpt($presence, $o) === FALSE) return FALSE ; foreach ($this->comparisons as $opt => $comp) // eg "Pos" => "<" or "Dur" => "==". $comparesTo has the value to compare to { if (($optVal = gm_getTaggedOpt($opt,$o)) === FALSE) return FALSE ; // getTaggedOpt might return 0 or FALSE, need to equate to type as well if (is_array($optVal)) { if (count($optVal) == 1) $optVal = array_shift(array_keys($optVal)) ; else return FALSE ; } $optVal = rtrim($optVal) ; switch ($comp) { case "<=" : if (!($optVal <= $this->comparesTo[$opt])) return FALSE ; break ; case "<" : if (!($optVal < $this->comparesTo[$opt])) return FALSE ; break ; case "==" : if (!($optVal == $this->comparesTo[$opt])) return FALSE ; break ; case ">=" : if (!($optVal >= $this->comparesTo[$opt])) return FALSE ; break ; case ">" : if (!($optVal > $this->comparesTo[$opt])) return FALSE ; break ; case "!=" : if (!($optVal != $this->comparesTo[$opt])) return FALSE ; break ; } } // well, we'd better return true, I guess return TRUE ; } } // The reason I use 12 is so that I can get a common factor of 2/3 and 7/4 and remain integral for accuracy (I don't want rounding errors) $durArray = array ( "Whole" => 64*12, "Half" => 32*12, "4th" => 16*12, "8th" => 8*12, "16th" => 4*12, "32nd" => 2*12, "64th" => 12 ) ; $durMods = array ( "Triplet" => 2/3, "Dotted" => 3/2, "DblDotted" => 7/4, "Grace" => 0 ) ; function durationness($da) // $da is a NWC2ClipObject's Opts Dur or Dur2 entry. An array { global $durArray, $durMods ; if (!$da || !is_array($da)) return 0 ; foreach ($durArray as $key => $val) if (isset($da[$key])) $t = $val ; // get base value foreach ($durMods as $key => $val) if (isset($da[$key])) $t *= $val ; // and modify accordingly return (int) round($t) ; // must return int to avoid rounding errors } function timeTaken($o) // of a note, chord, rest or rest chord. Returns minimum duration if two are present { $opts = $o->GetOpts() ; if (!isset($opts["Dur"])) return 0 ; $t1 = durationness($opts["Dur"]) ; if (!isset($opts["Dur2"])) return $t1 ; return min($t1, durationness($opts["Dur2"])) ; } // end TimeTaken function basicPosition($o) // of a note, chord or restchord. what line/space is it written on? { $opts = $o->GetOpts() ; if (!isset($opts["Pos"])) return FALSE ; if (is_array($opts["Pos"])) $i = $opts["Pos"] ; else $i = $opts["Pos"][0] ; if (preg_match("/-?\d+/", $i, $m)) return $m[1] ; else return FALSE ; } // return a string representation of a rest with \n of minimum duration of $o function ARestOfLength($o) { if (($oType =& $o->GetObjType()) == "Rest") return ($o->ReconstructClipText()."\n") ; // must be Note, Chord or RestChord. Minimum duration should be Dur $opts =& $o->GetOpts() ; unset($opts["Opts"], $opts["Dur2"], $opts["Pos"], $opts["Pos2"]) ; // Tidy it up, as we are only going to return a rest $oType = "Rest" ; return ($o->ReconstructClipText()."\n") ; } function ThisItemIsAMatch($o, $match_array) { if (!count($match_array)) return TRUE ; foreach ($match_array as $match) if ($match->Matches($o)) return TRUE ; return FALSE ; } function TakesTime($o) // true if not a grace note/chord { if (!in_array($o->GetObjType(), array("Note", "Chord", "RestChord", "Rest"))) return FALSE ; $Dur = $o->GetTaggedOpt("Dur") ; if (isset($Dur["Grace"])) return FALSE ; return TRUE ; } function mergeTimeless($cb, $sb) // merge untimed objects in clip buffer and scratch buffer { $ra = array ( ) ; // return array // scan clip buffer. if scratch buffer has it, use scratch's, otherwise clip's foreach ($cb as $co) // foreach clipbuffer as clipobject { $scratchOverridIt = FALSE ; foreach ($sb as $so) if ($so->GetObjType() == $co->GetObjType()) $scratchOverridIt = array_push($ra, $so) ; if (!$scratchOverridIt) array_push($ra, $co) ; } // now scan scratch buffer for anything that wasn't in clip's and add it foreach ($sb as $so) { $alreadyDone = 0 ; foreach ($ra as $ro) if ($so->GetObjType() == $ro->GetObjType()) $alreadyDone = TRUE ; if (!$alreadyDone) array_push($ra, $so) ; } return $ra ; } function positionness($src) // converts a position of the form "xz" "#z" "nz or z" "bz" "vz" (double sharp, sharp, natural, flat, double flat) //into an integer value 5*z , +2 for x, +1 for #, -1 for b, -2 for v { $AccMarkToValue = array ("x"=>2, "#"=>1, "n"=>0, "b"=>-1, "v"=>-2) ; // Double sharp, sharp, nat, flat, double-flat $n = new NWC2NotePitchPos($src) ; $ret = $n->Position * 5 ; if ($n->Accidental) $ret += $AccMarkToValue[$n->Accidental] ; unset($n) ; return ($ret) ; // } function positionsTangle($pos1, $pos2) // each param is the position returned by Opts["Pos"] or Pos2 { // Might be a single position or an array if ($pos1 === FALSE || $pos2 === FALSE) return FALSE ; if (!is_array($pos1)) $pos1 = array ($pos1) ; if (!is_array($pos2)) $pos2 = array ($pos2) ; $maxpos1 = max($pos1) ; $minpos1 = min($pos1) ; $maxpos2 = max($pos2) ; $minpos2 = min($pos2) ; // return true if one of the extremes lies within the range of the other extremes $result = ($minpos2 <= $minpos1 && $minpos1 <= $maxpos2) || ($minpos2 <= $maxpos1 && $maxpos1 <= $maxpos2) ; return $result ; } function coloriseMuteAndGrace(&$o) // make a clipItem colored, mute and grace { $o->Opts["Dur"]["Grace"] = "" ; $o->Opts["Opts"]["Muted"] = "" ; $o->Opts["Color"] = 1 ; } class ErrorItem extends NWC2ClipItem { function ErrorItem($dur, $pos) { $this->ObjType = "Chord" ; $this->Opts["Dur"] = $dur ; $this->Opts["Pos"] = $pos ; coloriseMuteAndGrace($this) ; } } // list($timeToUseOnScratch, $newo, $erroro) = mergeTimeful($o, $scro) // merge two notes, chords(I hope!), rests or restchords into something, returning // the timeTaken($o)-timeTaken($scro), and the new object // If the two can't be merged properly, a residual error object erroro is added // for commentary output. (Marked red, muted, grace note) // The basic premise here is that notes always annihilate rests of equal or greater length // The remainder is in the lap of the gods function mergeTimeful($o, $scro) { $newo = new NWC2ClipItem("") ; $erroro = FALSE ; $newOpts =& $newo->GetOpts() ; $newoType =& $newo->GetObjType() ; $tTUOS = timeTaken($o) - timeTaken($scro) ; // time to use on scratch is global to all these mergings global $barNumber ; switch ($o->GetObjType()) { case "Rest": switch ($scro->GetObjType()) { case "Rest": $newoType = "Rest" ; if (timeTaken($o) < timeTaken($scro)) $newOpts["Dur"] = $o->GetTaggedOpt("Dur") ; else $newOpts["Dur"] = $scro->GetTaggedOpt("Dur") ; break ; case "Note": case "Chord": case "RestChord": // if scro is a chord, only take the shortest stuff (Dur and Pos) if (timeTaken($o) < timeTaken($scro)) // need to return a restchord { $newoType = "RestChord" ; $newOpts["Dur"] = $o->GetTaggedOpt("Dur") ; $newOpts["Dur2"] = $scro->GetTaggedOpt("Dur") ; $newOpts["Pos2"] = $scro->GetTaggedOpt("Pos") ; if (!($scro->GetTaggedOpt("Dur2")===false)) { inform_error("Problem merging Note with scratch ".$scro->GetObjType()." at bar $barNumber from start of selection") ; $erroro = new ErrorItem($scro->GetTaggedOpt("Dur2"), $scro->GetTaggedOpt("Pos2")) ; } } else $newo = $scro ; break ; } // switch $scro->GetObjType break ; // case $o->GetObjType = "Rest" case "Note": // $o->GetObjType switch ($scro->GetObjType()) { case "Rest": if (timeTaken($o) <= timeTaken($scro)) // drop the rest's notation $newo = $o ; else { $newoType = "RestChord" ; $newOpts["Dur"] = $scro->GetTaggedOpt("Dur") ; $newOpts["Dur2"] = $o->GetTaggedOpt("Dur") ; $newOpts["Pos2"] = $o->GetTaggedOpt("Pos") ; } break ; case "Note": // $scro->GetObjecType $newoType = "Chord" ; if (timeTaken($o) < timeTaken($scro)) { $newOpts["Dur"] = $o->GetTaggedOpt("Dur") ; $newOpts["Pos"] = array($o->GetTaggedOpt("Pos")) ; $newOpts["Dur2"] = $scro->GetTaggedOpt("Dur") ; $newOpts["Pos2"] = array($scro->GetTaggedOpt("Pos")) ; } elseif (timeTaken($o) > timeTaken($scro)) { $newOpts["Dur"] = $scro->GetTaggedOpt("Dur") ; $newOpts["Pos"] = array($scro->GetTaggedOpt("Pos")) ; $newOpts["Dur2"] = $o->GetTaggedOpt("Dur") ; $newOpts["Pos2"] = array($o->GetTaggedOpt("Pos")) ; } else { $newOpts["Dur"] = $scro->GetTaggedOpt("Dur") ; if ($scro->GetTaggedOpt("Pos") != $o->GetTaggedOpt("Pos")) $newOpts["Pos"] = array($scro->GetTaggedOpt("Pos"), $o->GetTaggedOpt("Pos")) ; else { $newoType = "Note" ; $newOpts["Pos"] = $o->GetTaggedOpt("Pos") ; } } if (isset($newOpts["Pos2"]) && positionness($newOpts["Pos"][0]) > positionness($newOpts["Pos2"][0])) $newOpts["Opts"]["Stem"] = "Up" ; else $newOpts["Opts"]["Stem"] = "Down" ; if ($newoType == "Note") unset($newOpts["Opts"]["Stem"]) ; break ; // scro->GetObjType=="note" case "Chord": // scro->GetObjType // inform("Merging : ".print_r($o,TRUE).print_r($scro,TRUE)) ; $newoType = "Chord" ; if ((durationness($o->GetTaggedOpt("Dur")) != durationness($scro->GetTaggedOpt("Dur")) && positionsTangle($o->GetTaggedOpt("Pos"), $scro->GetTaggedOpt("Pos"))) || (durationness($o->GetTaggedOpt("Dur")) != durationness($scro->GetTaggedOpt("Dur2")) && positionsTangle($o->GetTaggedOpt("Pos"), $scro->GetTaggedOpt("Pos2")))) { inform_error("Cannot merge Note and Chord at bar $barNumber. The positions get tangled.\n") ; unset($newo) ; $newo = $o ; $erroro = $scro ; coloriseMuteAndGrace($erroro) ; } elseif (timeTaken($o) < timeTaken($scro)) { $newOpts["Dur"] = $o->GetTaggedOpt("Dur") ; $newOpts["Pos"] = array($o->GetTaggedOpt("Pos")) ; $newOpts["Dur2"] = $scro->GetTaggedOpt("Dur") ; $newOpts["Pos2"] = $scro->GetTaggedOpt("Pos") ; } elseif (timeTaken($o) == timeTaken($scro)) { $newOpts["Dur"] = $o->GetTaggedOpt("Dur") ; $newOpts["Pos"] = $scro->GetTaggedOpt("Pos") ; array_push($newOpts["Pos"], $o->GetTaggedOpt("Pos")) ; $newOpts["Pos"] = array_unique($newOpts["Pos"]) ; sort($newOpts["Pos"]) ; $newOpts["Dur2"] = $scro->GetTaggedOpt("Dur") ; if ($scro->GetTaggedOpt("Pos2")) $newOpts["Pos2"] = $scro->GetTaggedOpt("Pos2"); } elseif (timeTaken($o) > timeTaken($scro)) { $newOpts["Dur"] = $scro->GetTaggedOpt("Dur") ; $newOpts["Pos"] = $scro->GetTaggedOpt("Pos") ; $newOpts["Dur2"] = $o->GetTaggedOpt("Dur") ; if (!($scro->GetTaggedOpt("Dur2") === false)) { $newOpts["Pos2"] = $scro->GetTaggedOpt("Pos2") ; array_push($newOpts["Pos2"], $o->GetTaggedOpt("Pos")) ; $newOpts["Pos2"] = array_unique($newOpts["Pos2"]) ; sort($newOpts["Pos2"]) ; } else $newOpts["Pos2"] = array($o->GetTaggedOpt("Pos")) ; } // Now the acid test. Is the chord we've created tangled? if (isset($newOpts["Pos2"]) && positionsTangle($newOpts["Pos"],$newOpts["Pos2"])) { inform_error("Cannot merge Note and Chord at bar $barNumber. The positions get tangled.\n") ; unset($newo) ; $newo = $o ; $erroro = $scro ; coloriseMuteAndGrace($erroro) ; } break ; // scro->GetObjType=="chord" case "RestChord": if (timeTaken($o) > timeTaken($scro)) { if (durationness($o->GetTaggedOpt("Dur")) == durationness($scro->GetTaggedOpt("Dur2"))) // we can merge the note with the chord { unset($newo) ; $newo = $scro ; $newOpts =& $newo->GetOpts() ; array_push($newOpts["Pos2"], $o->GetTaggedOpt("Pos")) ; $newOpts["Pos2"] = array_unique($newOpts["Pos2"]) ; sort($newOpts["Pos2"]) ; } else // don't even try, it is too complicated, I'd need to split the longer note into tied notes { inform_error("Cannot merge Note and RestChord at bar $barNumber. The durations are incompatible\n") ; unset($newo) ; $newo = $o ; $erroro = $scro ; coloriseMuteAndGrace($scro) ; } } else // o's duration is <= scro's rest's duration { unset($newo) ; $newo = $scro ; $newo->ObjType = "Chord" ; $newOpts =& $newo->GetOpts() ; $newOpts["Pos"] = $o->GetTaggedOpt("Pos") ; $newOpts["Dur"] = $o->GetTaggedOpt("Dur") ; } break ; // $scro->GetObjType == RestChord } // switch $scro->GetObjType break ; // case $o->GetObjType == "Note" case "Chord" : // If a chord merging with a note or rest, swap 'em around and use the above code if ($scro->GetObjType() == "Note" || $scro->GetObjType() == "Rest") { unset($newo) ; list($tTUOS, $newo, $erroro) = mergeTimeful($scro, $o) ; return array(-$tTUOS, $newo, $erroro) ; } // Else we can merge two simple chords only if ($scro->GetObjType() == "Chord") { if (($o->GetTaggedOpt("Dur2") === false) && ($scro->GetTaggedOpt("Dur2") === false)) // We can merge these guys, as they are both simple chords { unset($newo) ;$newo = $o ; $newOpts =& $newo->GetOpts() ; if (timeTaken($o) == timeTaken($scro)) $newOpts["Pos"] = array_unique( array_merge($scro->GetTaggedOpt("Pos"), $o->GetTaggedOpt("Pos"))) ; elseif (!positionsTangle($o->GetTaggedOpt("Pos"), $scro->GetTaggedOpt("Pos"))) { inform_error("Cannot merge Chord and Chord/RestChord at bar $barNumber. I'm not smart enough.\n") ; unset($newo) ; $newo = $o ; $erroro = $scro ; coloriseMuteAndGrace($erroro) ; break ; } elseif (timeTaken($o) < timeTaken($scro)) { $newOpts["Dur"] = $o->GetTaggedOpt("Dur") ; $newOpts["Pos"] = $o->GetTaggedOpt("Pos") ; $newOpts["Dur2"] = $scro->GetTaggedOpt("Dur") ; $newOpts["Pos2"] = $scro->GetTaggedOpt("Pos") ; } elseif (timeTaken($o) > timeTaken($scro)) { $newOpts["Dur"] = $scro->GetTaggedOpt("Dur") ; $newOpts["Pos"] = $scro->GetTaggedOpt("Pos") ; $newOpts["Dur2"] = $o->GetTaggedOpt("Dur") ; $newOpts["Pos2"] = $o->GetTaggedOpt("Pos") ; } } break ; } // otherwise bail out inform_error("Cannot merge Chord and Chord/RestChord at bar $barNumber. I'm not smart enough.\n") ; unset($newo) ; $newo = $o ; $erroro = $scro ; coloriseMuteAndGrace($erroro) ; break ; case "RestChord" : // If a RestChord merging with a note or rest, swap 'em around and use the above code if ($scro->GetObjType() == "Note" || $scro->GetObjType() == "Rest") { list($tTUOS, $newo, $erroro) = mergeTimeful($scro, $o) ; return array(-$tTUOS, $newo, $erroro) ; } // otherwise bail out inform_error("Cannot merge RestChord and Chord/RestChord at bar $barNumber. I'm not smart enough.\n") ; unset($newo) ; $newo = $o ; $erroro = $scro ; coloriseMuteAndGrace($erroro) ; break ; } // switch o->getobjtype if ($newo->GetObjType() == "Chord" && isset($newOpts["Pos2"])) { // make sure that stem is correct if ($newOpts["Pos"][0] >= $newOpts["Pos2"][0]) $newOpts["Opts"]["Stem"] = "Up"; else $newOpts["Opts"]["Stem"] = "Down" ; } return array($tTUOS, $newo, $erroro) ; } $match_array = array ( ) ; $clipBuffer = array ( ) ; $scratchBuffer = array ( ) ; $timeToUseOnScratch = 0 ; // If >0 need to echo more notes from scratch to catch up with stdin // if <0, need to echo more notes from stdin to catch up // THE MAIN ACTION STARTS HERE array_shift($argv) ; // get rid of the first arg (script name) if (isset($argv[0]) && $argv[0] == "help") help_msg_and_exit() ; if (!count($argv)) abort("Not enough parameters! Try something of the form \ncut|copy|merge|paste [,,...] \nor use \"help\" for more details.") ; $action = array_shift($argv) ; if (!in_array($action, array("cut", "copy", "merge", "paste"))) abort("First parameter needs to be one of cut, copy, merge or paste\nor use help for more details.") ; while (count($argv)) array_push($match_array, new gm_match(array_shift($argv))) ; if (count($match_array) && ($action == "merge" || $action == "paste")) abort("Cannot use selection parameters for merge or paste") ; if (!count($match_array) && ($action == "cut" || $action == "copy")) array_push($match_array, new gm_match("ALL")) ; // Open the scratch file if ($action == "cut" || $action == "copy") { $SCRATCH_FILE = @fopen("SCRATCH.TXT", "w") ; if (!$SCRATCH_FILE) abort("Can't open SCRATCH.TXT file. Sorry.") ; } if ($action == "merge" || $action == "paste") { $SCRATCH_FILE = @fopen("SCRATCH.TXT", "r") ; if (!$SCRATCH_FILE) abort("Can't open SCRATCH.TXT, have you cut or copied anything yet?") ; } $barNumber = 0 ; $thereWasNoInputStream = TRUE ; $clip_buffer = array ( ) ; $scratch_buffer = array ( ) ; $clip = new NWC2Clip('php://stdin'); echo $clip->GetClipHeader()."\n"; foreach ($clip->Items as $item) { $thereWasNoInputStream = FALSE ; $o = new NWC2ClipItem($item); $oType = $o->GetObjType() ; if ($oType == "Bar") $barNumber++ ; switch ($action) { case "cut" : if (ThisItemIsAMatch($o, $match_array)) { if (TakesTime($o)) echo ARestOfLength($o) ; // if it takes time, replace with equivalent rest if ($oType == "Bar") echo $item ; // won't cut bars fputs($SCRATCH_FILE, $item) ; // regardless, echo to scratch_file } else { if (TakesTime($o)) fputs($SCRATCH_FILE, ARestOfLength($o)) ; if ($oType == "Bar") fputs($SCRATCH_FILE, $item) ; echo $item ; } break ; case "copy" : echo $item ; if (ThisItemIsAMatch($o, $match_array)) fputs($SCRATCH_FILE, $item) ; else { if (TakesTime($o)) fputs($SCRATCH_FILE, ARestOfLength($o)) ; if ($oType == "Bar") fputs($SCRATCH_FILE,$item) ; } break ; case "merge" : if ($timeToUseOnScratch < 0) { echo $item ; $timeToUseOnScratch += timeTaken($o) ; break ; } if (TakesTime($o)) { // Merge the non-time objects in the two buffers // First, gather the non-time objects in the scratch file while (!feof($SCRATCH_FILE) && ($scro = new NWC2ClipItem(fgets($SCRATCH_FILE))) && (!TakesTime($scro))) { array_push($scratch_buffer, $scro) ; unset($scro) ; } // Now merge the two buffers $merge_buffer = mergeTimeless($clip_buffer, $scratch_buffer) ; foreach ($merge_buffer as $mitem) echo $mitem->ReconstructClipText()."\n" ; unset($merge_buffer, $clip_buffer, $scratch_buffer) ; $clip_buffer = $scratch_buffer = array ( ) ; if (isset($scro)) { list($timeToUseOnScratch, $newo, $erroro) = mergeTimeful($o, $scro) ; unset($scro) ; } else { $newo = $o ; $erroro = FALSE ; } echo $newo->ReconstructClipText()."\n" ; if ($erroro) echo $erroro->ReconstructClipText()."\n" ; while ($timeToUseOnScratch > 0 && !feof($SCRATCH_FILE)) { $scro = new NWC2ClipItem(fgets($SCRATCH_FILE)) ; echo $scro->ReconstructClipText()."\n" ; $timeToUseOnScratch -= timeTaken($scro) ; unset($scro) ; } } else array_push($clip_buffer, $o) ; break ; case "paste" : echo $item ; break ; } unset($o, $scro, $newo, $erroro); } if ($action == "merge" || $action == "paste") while (!feof($SCRATCH_FILE)) echo fgets($SCRATCH_FILE) ; echo NWC2_ENDCLIP."\n"; fclose($SCRATCH_FILE) ; if ($thereWasNoInputStream) switch($action) { case "merge": inform("No selection to merge with. Have inserted scratch instead.") ; break ; case "cut": case "copy": abort("No selection to cut or copy from. Please select something in the staff and try again.") ; }; exit ($final_exit_value) ; ?>