NoteWorthy Software, Inc.
2014-04-18 09:47 PM *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
News: Licensed NWC2 users can upgrade their community membership [more]
 
   Home   Help Login Register  
Pages: [1]   Go Down
  Print  
Author Topic: Imported triplets  (Read 18161 times)
Brian Maskell
NWC2 User
« on: 2009-02-12 07:13 AM »

When NWC imports a MIDI file it does not faithfully represent triplets; correcting them manually is a tedious business so I have written a User Tool to do this.

It depends on correctly recognising the approximations that NWC generates during the import process. My current understanding of these is based on examples that I have exported and re-imported. I invite those who know NWC from the inside to help me to do better.

-- Brian

Code:   [Select · Download]
<?php
/*******************************************************************************
brm_Triplify Version 1.00

This script will seek sequences that NWC has created from importing a MIDI file containing 
triplets and convert those sequences into triplets.

History:
[2009-02-12] Version 1.00 - Initial version
*******************************************************************************/
require_once("lib/nwc2clips.inc");

$clip = new NWC2Clip('php://stdin');

// we shall need to assess the lengths of several notes etc, including tied notes, in units of ticks
$ticks_per_crotchet 16// as small as possible, since we will not be representing triplets
$tickLength = array(
"Whole"=> $ticks_per_crotchet *4,
"Half" => $ticks_per_crotchet *2,
"4th"  => $ticks_per_crotchet,
"8th"  => $ticks_per_crotchet /2,
"16th" => $ticks_per_crotchet /4,
"32nd" => $ticks_per_crotchet /8,
"64th" => $ticks_per_crotchet /16
);

// NWC's efforts in terms of representing a triplet come from examples. There may be more to come.
// When adding to this array, always insert sub-array elements in descending size
$validLengthGroup = array (
array(6,5,5),
array(3,3,2),
array(8,5,3), // a very poor and problematic representation of a triplet, but it does occur e.g.
      // with triplet (crotchet, crotchet rest, crotchet)=(8,3,5). However, in the quaver version of this
      // example the rest has disappeared altogether (3,NULL,1). so we have no chance!
array(11,5),
array(5,3)
);


function 
is_valid_group ($lengthSet) {
   global 
$validLengthGroup;

  
// first reduce the array by its HCF
  
if (sizeof($lengthSet) <= 1) return false;
  
$factor true;
  while(
$factor) {
    foreach(
$lengthSet as $v$factor &= !($v 2);
    if (
$factor) foreach($lengthSet as $i=>$v$lengthSet[$i] /= 2;
  }
  
$factor true;
  while(
$factor) {
    foreach(
$lengthSet as $v$factor &= !($v 3);
    if (
$factor) foreach($lengthSet as $i=>$v$lengthSet[$i] /= 3;
  }

  
// then sort the array descending to match the convention in definition of validLengthGroup
  
rsort($lengthSet);

  
// now we are ready to check for a valid group of lengths
  
foreach ($validLengthGroup as $thisgroup) {
    
$result false// in case the last group has different length to the array under test
    
if (sizeof($lengthSet) != sizeof($thisgroup)) continue;
    
$result true// assume a match until a mis-match is found
    
foreach($lengthSet as $i=>$v) if ($lengthSet[$i] != $thisgroup[$i]) $result=false;
    if (
$result) break; // found the winner, no need to go on searching
  
}
  return 
$result;
}

function 
isTiedNote($arg)
{

// if ($arg->GetObjType() == "Rest") return false; // this is caught anyway
$opts $arg->GetOpts();
if (isset($opts["Pos"])) {
$pos $opts["Pos"];
    if ($arg->GetObjType() == "Note") {
$n = new NWC2NotePitchPos($pos);
$ret false;
if ($n->Tied$ret true;
unset($n);
} else { // must be a Chord, so Pos is not a string but an array of strings
       $ret false;
       foreach($opts["Pos"] as $k=>$v) {
        $n = new NWC2NotePitchPos($v);
        if ($n->Tied$ret true// in practice, expect all or none to be tied
        unset($n);
}
}
} else $ret=false;
return ($ret);
}

// Track the number of conversions
$numConvertedTriplets 0;

//

echo $clip->GetClipHeader()."\n";

// Use arrays $TripletQ and $lengthSet to hold candidates
$TripletQ = array();
$lengthSet = array();
$tied_note_pending=false;

foreach (
$clip->Items as $item) {
$o = new NWC2ClipItem($item);
$opts $o->GetOpts();

$is_note in_array($o->GetObjType(), array("Chord","Note","Rest","RestChord")); // but there won't be a RestChord!
$is_grace = isset($o->Opts["Dur"]["Grace"]);
$is_triplet = isset($o->Opts["Dur"]["Triplet"]); // check this
$is_tied isTiedNote($o) ;
$is_dotted = isset($o->Opts["Dur"]["Dotted"]);
$is_dbldotted = isset($o->Opts["Dur"]["DblDotted"]);

if ($is_note && !$is_grace && !$is_triplet) {
   if ($TripletQarray_push($TripletQ,$o); else $TripletQ = array($o); // whatever happens, remember the note
   // evaluate its length
   foreach ($tickLength as $notename => $value) {
      if (isset($opts["Dur"][$notename])) {
        $length $value;
}
   }

   if ($is_dotted$length *= 3/2;
   elseif ($is_dbldotted$length *= 7/4;

   // there is at least one candidate in the queue, record length and check for triplet
   if($tied_note_pending) { // add its length to that stored for the previous note
    $last_element sizeof($lengthSet)-1;
$lengthSet[$last_element] += $length;
   } else {
array_push($lengthSet$length);
   }

   if ($tied_note_pending $is_tied) continue; // yes, really not "=="! this is ready for the next lap

   // check if we have a triplet
   while (true) { // start a loop so we can retest having discarded one note
if (is_valid_group($lengthSet)) { //we have a triplet, output the notes in modified form
   $numConvertedTriplets++;
   $length array_sum($lengthSet)/2// this is the un-triplet-ised length of a single element
//    $dur = array_search($tickLength, $length); WHY DOESN'T THIS WORK? use alternative code
   foreach($tickLength as $key => $value) if ($value == $length) {$dur $key; break; }
//    $durxtwo = array_search($tickLength, $length*2); WHY DOESN'T THIS WORK?

   foreach($tickLength as $key => $value) if ($value == $length *2) {$durxtwo $key; break; }
   // Output the triplet, being two or three notes/rests/chords
$output_tied_note_pending false$length_index 0;
foreach($TripletQ as $this) {
   if ($output_tied_note_pending) {
$output_tied_note_pending isTiedNote($this);
continue; // dumping this note and processing the next item in the queue
   }
   if (isset($this->Opts["Opts"]["Beam"])) unset($this->Opts["Opts"]["Beam"]);
   if (isset($this->Opts["Opts"]["Stem"])) unset($this->Opts["Opts"]["Stem"]); // triplet stems don't need to be aligned
   // can't get rid of Opts altogether - a rest might have Opts["VertOffset"] set - not likely though

   if (isset($this->Opts["Dur"]["Dotted"])) unset($this->Opts["Dur"]["Dotted"]); // a dotted triplet makes no sense
   if (isset($this->Opts["Dur"]["DblDotted"])) unset($this->Opts["Dur"]["DblDotted"]); // nor does this!

   if (sizeof($lengthSet)==3$this->Opts["Dur"] = array($dur => "","Triplet" => "");
   elseif ($lengthSet[0] > $lengthSet[1]) { // first note is of double duration
$this->Opts["Dur"] = array ((($length_index == 0) ? $durxtwo $dur)=> "","Triplet" => "");
   } else { // second note is of double duration
$this->Opts["Dur"] = array ((($length_index == 0) ? $dur $durxtwo)=> "","Triplet" => "");
   }

   if ($length_index == 0$this->Opts["Dur"]["Triplet"] = "First";
   if ($length_index == (sizeof($lengthSet)-1)) $this->Opts["Dur"]["Triplet"] = "End";

   $output_tied_note_pending isTiedNote($this);
   // un-tie the note/chord if it is tied
   if (isset($this->Opts["Pos"]) && $output_tied_note_pending) { // i.e. not a rest
      if ($this->GetObjType() == "Note") {
        $pos = new NWC2NotePitchPos($this->Opts["Pos"]);
       $pos->Tied=false;
       $this->Opts["Pos"] = $pos->ReconstructClipText(); 
       unset($pos);
      } else { // a Chord, must deal with all the Pos elements
 foreach($this->Opts["Pos"] as $k=>$v) {
          $pos = new NWC2NotePitchPos($v);
        $pos->Tied false;
        $this->Opts["Pos"][$k] = $pos->ReconstructClipText(); 
        unset($pos);
 }
      }
   }
   if ($this->GetObjType() == "Rest") unset($this->Opts["Pos"]); 
   echo $this->ReconstructClipText()."\n";

   $length_index++;
}
$TripletQ = array(); $lengthSet = array(); $tied_note_pending=false;
break; // out of the while loop
} elseif (sizeof($lengthSet)<3) { // not a triplet yet but there is still time so store data
   continue 2// breaking out of the while loop to get a new item from the clip    
} else { // not a triplet so output the first note, drop it from the stored queue and retest
$output_tied_note_pending true;
while ($output_tied_note_pending) {
   $this array_shift($TripletQ);
   $output_tied_note_pending isTiedNote($this);
   echo $this->ReconstructClipText()."\n";
}
$this array_shift($lengthSet); // dump its length too
// Now we must retest because the remaining notes, if any, could be a (2 note) triplet
if ($TripletQ) continue; // repeat the while loop
// end if triplet, maybe triplet or not triplet
   } // end while(true)loop
} else { // not a note, sequence is spoiled so output everything in the queue, plus this non-note item and start afresh
   if ($TripletQ) foreach($TripletQ as $this) {
echo $this->ReconstructClipText()."\n";
   }
   echo $o->ReconstructClipText()."\n";
   $TripletQ = array(); $lengthSet = array(); $tied_note_pending=false;
// end if is_note else
// end for each clip

if ($TripletQ) foreach($TripletQ as $this) {
   echo 
$this->ReconstructClipText()."\n";
}

echo 
NWC2_ENDCLIP."\n";

if (!
$numConvertedTriplets) {
fputs(STDERR,"No valid triplets were found within the selection");
exit(NWC2RC_ERROR);
}

exit(
NWC2RC_SUCCESS);

?>
Logged
Lawrie Pardy
Virtuoso


« Reply #1 on: 2009-02-12 10:51 PM »

G'day Brian,
just did a quick test with a file I originally created to test Bryan Creer's Swing tool.  Took out the Swing staff, exported to MIDI and re-imported.  Unfortunately your tool didn't recognise any triplets.

Please find attached the files in question...  You'll need to rename the extension of the MIDI file - can't post MIDI's here...
LPTripTest.nwc = original file
LPTripTest-mid.txt = MIDI output file (needs rename)
LPTripTest-postMIDIimport.nwc = imported MIDI

MIDI import parameters:
All checked; 16th note resolution and quarter rest resolution

* LPTripTest.nwc (0.4 KB - downloaded 577 times.)
* LPTripTest-mid.txt (0.99 KB - downloaded 615 times.)
* LPTripTest-postMIDIimport.nwc (0.47 KB - downloaded 565 times.)
Logged

I plays 'Bones, crumpets, coronets, floosgals 'n youfonymums
Brian Maskell
NWC2 User
« Reply #2 on: 2009-02-13 09:28 AM »

Lawrie,

The answer lies in the MIDI import parameters you have used: "16th note resolution and quarter rest resolution". At this resolution your example triplets are very poorly represented, the note length ratios being (1,2,1). I could add this to my array of valid 'triplet signatures' but I fear that would lead to too many false positives (though when I tried this, there are none coming from your test file).

I generally use 32nd note resolution, which I assume is the default, and then your triplets come out as (3,3,2). With this change, the triplets in bars 5 and 7 are correctly recognised but in bars 13 and 15 a triplet is recognised one note too early; this is due to my accepting the triplet ratios in any order - so maybe I shouldn't do that.

This can easily be changed by commenting out the sort at line 57, though it is then necessary to modify the comment at line 28 and the subsequent triplet signature array so that note length ratios are always presented in the order they have been observed to occur, rather than descending size:
Code:   [Select · Download]
// When adding to this array, always insert sub-array elements in the order they have been observed
$validLengthGroup = array (
array(6,5,5),
array(3,3,2),
array(8,3,5), // a very poor and problematic representation of a triplet, but it does occur e.g.
      // with triplet (crotchet, crotchet rest, crotchet)=(8,3,5). However, in the quaver version of this
      // example the rest has disappeared altogether (3,NULL,1). so we have no chance!
array(1,2,1), // another poor one, included to cope with Lawrie's test file
array(11,5),
array(3,5)
);
Here I have also added a triplet signature (1,2,1) which is needed to cope with your re-import of your test file but I do think this is a bad idea for the reasons stated!

As I said in the original post, the tool is programmed to recognise the sequence of notes that NWC generates as an approximation to a triplet, based on the ratio of their lengths, but of course I have only put in the 'triplet signatures' that I have seen as a result of exporting and re-importing various test triplets. How much better it would be if I had the inside information that would enable me to predict what NWC will do in any import situation!

In getting to the bottom of why your test file was not ideally handled, I now understand my own comment in the code snippet above, so I need to change that too. With a rest resolution of a quarter, NWC doesn't have any chance to represent a quaver triplet rest!

Finally, NWC itself is in the best position to recognise triplets when importing MIDI files because at that stage the full resolution is available. Why doesn't it attempt this?

-- Brian

PS I had intended to attach a NWC file to this response as you did in your post; how does one do that?
Logged
Lawrie Pardy
Virtuoso


« Reply #3 on: 2009-02-13 09:37 AM »

<snip>
Finally, NWC itself is in the best position to recognise triplets when importing MIDI files because at that stage the full resolution is available. Why doesn't it attempt this?
Yep, after I'd posted I thought about my import parameters and figured that might be the case - thanks for confirming it.  I agree that a mod to cope with my poor import is probably not ideal.

Quote
PS I had intended to attach a NWC file to this response as you did in your post; how does one do that?

You need to upgrade yourself to NWC2User and then there is an "Additional Options..." link that opens up and allows attachments plus some additional options.

Go here:
https://www.noteworthysoftware.com/forum/?topic=5483.0
and follow the prompts.
Logged

I plays 'Bones, crumpets, coronets, floosgals 'n youfonymums
Brian Maskell
NWC2 User
« Reply #4 on: 2009-02-13 05:38 PM »

Lawrie,

Thanks for your input. This has raised issues about the range of MIDI import resolutions that should be accommodated, also the need to deal with concatenated rests. I can see a way to deal with these and will post a new version when I have had the time to test it out thoroughly.

-- Brian
Logged
Lawrie Pardy
Virtuoso


« Reply #5 on: 2009-02-13 06:55 PM »

G'day Brian,
out of curiosity, have you looked at Andrew Purdam's "tripletise" user tool?

Scripto: http://nwc-scriptorium.org/nwc2scripts_trip.html

Logged

I plays 'Bones, crumpets, coronets, floosgals 'n youfonymums
Brian Maskell
NWC2 User
« Reply #6 on: 2009-02-13 07:30 PM »

Oh dear! Have I re-invented the wheel? I will have a look before I go much further. If nothing else, I have learnt a lot about the API.

-- Brian
Logged
Brian Maskell
NWC2 User
« Reply #7 on: 2009-02-14 05:14 PM »

I have now revised my User Tool to deal with approximations to triplets imported by NWC from a MIDI file; the new version appears below (brm_triplify).

The main change is to recognise that not everybody will import their MIDI file using the default parameters, so the tool certainly has to cope with finer resolutions. Coarser resolutions are problematic because as the approximation to a perfect triplet gets worse, the risk of falsely identifying a sequence of notes as a triplet becomes greater and there comes a point where it is best to skip these.

The other change is to handle concatenated rests. The need for this really only becomes apparent at high resolution and it is tricky: adjacent rests behave like tied notes, except that there is no indication which ones are notionally tied together. It gets worse: NWC will happily generate, for example, a dotted 4th rest, where one component - the 4th rest - does not form part of a triplet but the dotted bit - an 8th rest - does. I hope it doesn't churn out double-dotted rests - I could cater for them but I haven't.

I attach my test file which contains, on the top stave, a variety of triplets and, on the other staves, the result of exporting and re-importing at different resolutions. No parameters are required when invoking the tool; it will work with what it is given and convert to triplets any sequence that can safely be identified.

Lawrie drew my attention to Andrew Purdam's "tripletise" user tool but this requires very specific steering; for example the first triplet in my test file, at 64th note resolution (on the bottom stave) requires steering parameters "4 8t d32 16t 64t 16t 64 8t d32". It took longer to work that out than to do the edit manually, especially since it dealt with only one other triplet in the clip. I wanted something that will automatically unscramble as much as possible of the mess that NWC makes of importing triplets, with as little intervention as possible by the user.

I have no doubt that there are other triplet 'signatures' that I should have included but haven't; that is what the Newsgroup is for!

Finally, since it hasn't evoked a reponse yet, I repeat the question: why doesn't NWC make a better job of importing triplets in the first place, when exact information is available?

-- Brian

Code:   [Select · Download]
<?php
/*******************************************************************************
brm_Triplify Version 1.01

Seeks the triplet approximation sequences that NWC has created when importing a MIDI file
and converts those sequences into normal triplets.

History:
[2009-02-14] Version 1.01 - Recognition of triplets from a wider range of MIDI import 
     resolutions and handling of concatenated rests
[2009-02-12] Version 1.00 - Initial version
*******************************************************************************/
require_once("lib/nwc2clips.inc");

$clip = new NWC2Clip('php://stdin');

// we shall need to assess the lengths of several notes etc, including tied notes, in units of ticks
$ticks_per_crotchet 16// as small as possible, since we will not be representing triplets
$tickLength = array(
"Whole"=> $ticks_per_crotchet *4,
"Half" => $ticks_per_crotchet *2,
"4th"  => $ticks_per_crotchet,
"8th"  => $ticks_per_crotchet /2,
"16th" => $ticks_per_crotchet /4,
"32nd" => $ticks_per_crotchet /8,
"64th" => $ticks_per_crotchet /16
);

// NWC's efforts in terms of representing a triplet come from examples. There may be more to come.
// When adding to this array, always insert sub-array elements in the order they have been observed. Also beware that
// the further these ratios stray from (1,1,1),(2,1) or (1,2) the greater is the risk of false positive triplet identification
$validLengthGroup = array (
array(6,5,5),
array(5,6,5),
array(3,3,2),
array(8,3,5), // a very poor and potentially problematic representation of a triplet, but it does occur e.g.
      // with triplet (crotchet, crotchet rest, crotchet)=(8,3,5) if the import resolution is a bit low
array(11,10,11),
array(19,24,21),
array(21,11),
array(11,21),
array(11,5),
array(5,11),
array(3,5)
);


function 
isValidGroup ($lengthSet) {
    global 
$validLengthGroup;

    
// first reduce the array by its HCF
    
if (sizeof($lengthSet) <= 1) return false;
    
$factor true;
    while(
$factor) {
        foreach(
$lengthSet as $v$factor &= !($v 2);
if ($factor) foreach($lengthSet as $i=>$v$lengthSet[$i] /= 2;
    }
    
$factor true;
    while(
$factor) {
        foreach(
$lengthSet as $v$factor &= !($v 3); // just in case ticks_per_crotchet ever aquires a factor 3!
if ($factor) foreach($lengthSet as $i=>$v$lengthSet[$i] /= 3;
    }

    
// now we are ready to check for a valid group of lengths
    
foreach ($validLengthGroup as $thisgroup) {
        
$result false// just in case the last group has different length to the array under test
if (sizeof($lengthSet) != sizeof($thisgroup)) continue;
$result true// assume a match until a mis-match is found
foreach($lengthSet as $i=>$v) if ($lengthSet[$i] != $thisgroup[$i]) $result=false;
if ($result) break; // found the winner, no need to go on searching
    
}
    return 
$result;
}

function 
isTiedNote($arg) {
if (($arg->GetObjType() != "Note") &&
    ($arg->GetObjType() != "Chord") &&
    ($arg->GetObjType() != "RestChord")) return false
$opts $arg->GetOpts();
if (isset($opts["Pos"])) {
    $pos $opts["Pos"];
    if ($arg->GetObjType() == "Note") {
        $n = new NWC2NotePitchPos($pos);
$ret false;
if ($n->Tied$ret true;
unset($n);
    } else { // must be a Chord, so Pos is not a string but an array of strings
$ret false;
foreach($opts["Pos"] as $k=>$v) {
    $n = new NWC2NotePitchPos($v);
    if ($n->Tied$ret true// in practice, expect all or none to be tied
    unset($n);
}
    }
} else $ret=false;
return ($ret);
}

function 
note_length ($arg) {
    
// returns the base length of the item;s Dur - we deal with dotted etc elsewhere
    
global $tickLength;
    
$opts $arg->GetOpts();         
    foreach (
$tickLength as $notename => $value) if (isset($opts["Dur"][$notename])) $length $value;
    return 
$length;
}

// Track the number of conversions
$numConvertedTriplets 0;

// MAIN PROGRAM

echo $clip->GetClipHeader()."\n";

// Use arrays $TripletQ and $lengthSet to hold candidates
$TripletQ = array();
$lengthSet = array();
$tied_note_pending=false$last_item_was_rest false;

foreach (
$clip->Items as $item) {
    
$o = new NWC2ClipItem($item);
    
$opts $o->GetOpts();

    
$is_note in_array($o->GetObjType(), array("Chord","Note","Rest","RestChord")); // but there won't be a RestChord!
    
$is_rest = ($o->GetObjType() == "Rest");
    
$is_grace = isset($o->Opts["Dur"]["Grace"]);
    
$is_triplet = isset($o->Opts["Dur"]["Triplet"]);
    
$is_tied isTiedNote($o) ;
    
$is_dotted = isset($o->Opts["Dur"]["Dotted"]);
    
$is_dbldotted = isset($o->Opts["Dur"]["DblDotted"]);

    if (
$is_note && !$is_grace && !$is_triplet) {
        if (
$TripletQarray_push($TripletQ,$o); else $TripletQ = array($o); // whatever happens, remember the note
// evaluate its length
$length note_length($o);

if ($is_dotted$length *= 3/2;
elseif ($is_dbldotted$length *= 7/4;

// there is at least one candidate in the queue, record length and check for triplet
if ($tied_note_pending) { // add its length to that stored for the previous note
    $last_element sizeof($lengthSet)-1;
    $lengthSet[$last_element] += $length;
} elseif ($is_rest && $last_note_was_rest) { // similarly add length
    if (sizeof($lengthSet)==0array_push($lengthSet$length); // this really shouldn't happen!
    else { $last_element sizeof($lengthSet)-1$lengthSet[$last_element] += $length;}
} else array_push($lengthSet$length);

if ($tied_note_pending $is_tied) continue; // yes, really not "=="! this is ready for the next lap

// check if we have a triplet
while (true) { // start a loop so we can retest having discarded one note
    if (isValidGroup($lengthSet)) { //we have a triplet, output the notes in modified form
$numConvertedTriplets++;
$length array_sum($lengthSet)/2// this is the un-triplet-ised length of a single element
// $dur = array_search($tickLength, $length); WHY DOESN'T THIS WORK? use alternative code
foreach($tickLength as $key => $value) if ($value == $length) {$dur $key; break; }
// $durxtwo = array_search($tickLength, $length*2); WHY DOESN'T THIS WORK?
foreach($tickLength as $key => $value) if ($value == $length *2) {$durxtwo $key; break; }

// Output the triplet, being two or three notes/rests/chords
$output_tied_note_pending false$length_index 0$last_output_note_was_rest false;
foreach($TripletQ as $this) {
    if ($output_tied_note_pending) {
        $output_tied_note_pending isTiedNote($this);
continue; // dumping this note and processing the next item in the queue
    }
    if ($last_output_note_was_rest && ($this->GetObjType() == "Rest")) {
// $last_output_note_was_rest is set anyway
continue; // dumping this partial rest and processing the next item
    }

    if (isset($this->Opts["Opts"]["Beam"])) unset($this->Opts["Opts"]["Beam"]);
    if (isset($this->Opts["Opts"]["Stem"])) unset($this->Opts["Opts"]["Stem"]); // triplet stems don't need to be aligned
    // can't get rid of Opts altogether - a rest might have Opts["VertOffset"] set - not likely though

    if (isset($this->Opts["Dur"]["Dotted"])) unset($this->Opts["Dur"]["Dotted"]); // a dotted triplet makes no sense
    if (isset($this->Opts["Dur"]["DblDotted"])) unset($this->Opts["Dur"]["DblDotted"]); // nor does this!

    if (sizeof($lengthSet)==3$this->Opts["Dur"] = array($dur => "","Triplet" => "");
    elseif ($lengthSet[0] > $lengthSet[1]) { // first note is of double duration
        $this->Opts["Dur"] = array ((($length_index == 0) ? $durxtwo $dur)=> "","Triplet" => "");
    } else { // second note is of double duration
        $this->Opts["Dur"] = array ((($length_index == 0) ? $dur $durxtwo)=> "","Triplet" => "");
    }

    if ($length_index == 0$this->Opts["Dur"]["Triplet"] = "First";
    if ($length_index == (sizeof($lengthSet)-1)) $this->Opts["Dur"]["Triplet"] = "End";

    $output_tied_note_pending isTiedNote($this);
    // un-tie the note/chord if it is tied
    if (isset($this->Opts["Pos"]) && $output_tied_note_pending) { // i.e. not a rest
        if ($this->GetObjType() == "Note") {
    $pos = new NWC2NotePitchPos($this->Opts["Pos"]);
    $pos->Tied=false;
    $this->Opts["Pos"] = $pos->ReconstructClipText(); 
    unset($pos);
} else { // a Chord, must deal with all the Pos elements
    foreach($this->Opts["Pos"] as $k=>$v) {
                $pos = new NWC2NotePitchPos($v);
        $pos->Tied false;
      $this->Opts["Pos"][$k] = $pos->ReconstructClipText(); 
        unset($pos);
    }
}
    }
    if ($this->GetObjType() == "Rest"){
        unset($this->Opts["Pos"]);
$last_output_note_was_rest = ($this->GetObjType() == "Rest");
    } else $last_output_note_was_rest false;
echo $this->ReconstructClipText()."\n";

$length_index++;
    }
    $TripletQ = array(); $lengthSet = array(); $tied_note_pending=false$last_output_note_was_rest=false;
    break; // out of the while loop
} elseif ((sizeof($lengthSet)<3) || ($is_rest)) { // not a triplet yet but there is still time
    $last_note_was_rest $is_rest;
    continue 2// breaking out of the while loop to get a new item from the clip    
} else { // not a triplet so output the first note, drop it from the stored queue and retest
    $output_tied_note_pending true;
    while ($output_tied_note_pending) {
        $this array_shift($TripletQ);
if (($this->GetObjType() == "Rest") && (note_length($this) < $lengthSet[0])) {
    // now it gets complicated because if the partial rest we have just removed from the queue was dotted
    // we should only strip off 2/3 of it and put the rest back in the queue!
    // NB ignore the double-dotted case - I have yet to see one of these!
    $this_opts $this->GetOpts();
    $this_is_dotted = isset($this->Opts["Dur"]["Dotted"]);
    if ($this_is_dotted) { // the rest to be output is of this duration but not dotted
        unset($this->Opts["Dur"]["Dotted"]);
       echo $this->ReconstructClipText()."\n";
$lengthSet[0] -= note_length($this);
$this_length note_length($this);
// $this_dur = array_search($tickLength, $length); WHY DOESN'T THIS WORK? use alternative code
foreach($tickLength as $key => $value) if ($value == $this_length) {$this_dur $key; break; }
unset($this->Opts["Dur"][$this_dur]);
$this_length /= 2;
// $this_dur = array_search($tickLength, $length); WHY DOESN'T THIS WORK? use alternative code
foreach($tickLength as $key => $value) if ($value == $this_length) {$this_dur $key; break; }
$this->Opts["Dur"][$this_dur]="";
$new_size array_unshift($TripletQ,$this);
continue 2// break out of this while loop and retest for a triplet
    } else { // not dotted so it's much simpler
       echo $this->ReconstructClipText()."\n";
$lengthSet[0] -= note_length($this);
continue 2// break out of this while loop and retest for a triplet
    }  
} else { // a note, chord or a single rest
    $output_tied_note_pending isTiedNote($this);
    echo $this->ReconstructClipText()."\n";
}
    }
    $this array_shift($lengthSet); // dump its length too
    // Now we must retest because the remaining notes, if any, could be a (2 note) triplet
    if ($TripletQ) continue; // repeat the while loop
// end if triplet, maybe triplet or not triplet
    } // end while(true)loop
    $last_note_was_rest $is_rest;
} else { // not a note, sequence is spoiled so output everything in the queue, plus this non-note item and start afresh
    if ($TripletQ) foreach($TripletQ as $this) {
echo $this->ReconstructClipText()."\n";
    }
    echo $o->ReconstructClipText()."\n";
    $TripletQ = array(); $lengthSet = array(); $tied_note_pending=false$last_note_was_rest false;
// end if is_note else
    
unset($o); //???
// end for each clip

if ($TripletQ) foreach($TripletQ as $this) {
   echo 
$this->ReconstructClipText()."\n";
}

echo 
NWC2_ENDCLIP."\n";

if (!
$numConvertedTriplets) {
fputs(STDERR,"No triplets were found within the selection. Check the MIDI import resolution used.");
exit(NWC2RC_ERROR);
}

exit(
NWC2RC_SUCCESS);

?>


* TripletTest.nwc (1.65 KB - downloaded 515 times.)
Logged
Rick G.
Virtuoso
« Reply #8 on: 2009-02-14 07:04 PM »

I hope it doesn't churn out double-dotted rests
It doesn't. It will churn out chords in places where it sees a fast run of single notes.
Logged

Registered user since 1996
Lawrie Pardy
Virtuoso


« Reply #9 on: 2009-02-14 10:14 PM »

Quote
Finally, since it hasn't evoked a reponse yet, I repeat the question: why doesn't NWC make a better job of importing triplets in the first place, when exact information is available?
that would be 'cos we don't really know the answer.  There is some surmise that it may be to restrict exposure to copyright violation...  for myself, I don't know and in the time I've been on this forum Eric hasn't revealed his reasons for not improving those areas of MIDI import.

I haven't downloaded the new version yet - no time to test for now.
Logged

I plays 'Bones, crumpets, coronets, floosgals 'n youfonymums
Brian Maskell
NWC2 User
« Reply #10 on: 2009-02-15 03:03 AM »

It will churn out chords in places where it sees a fast run of single notes.
Rick,

Do you have an example of this? I have not managed to generate one.

-- Brian
Logged
Rick G.
Virtuoso
« Reply #11 on: 2009-02-15 07:42 AM »

Rename the attachment to: tt.mid
The left hand notes are arpeggiated, but NWC imports them as Chords.

I'm not saying NWC is wrong here, just that this is yet another thing that anyone hoping to decypher MIDI import needs to know.

* Tt-mid.txt (0.41 KB - downloaded 531 times.)
Logged

Registered user since 1996
Brian Maskell
NWC2 User
« Reply #12 on: 2009-02-15 02:04 PM »

Rick, I don't understand how this example illustrates the behaviour you mentioned, viz rapid single notes imported as a chord; indeed it shows chords imported as single notes.

The left hand notes are arpeggiated, but NWC imports them as Chords. I'm not saying NWC is wrong here,
The left hand notes start almost at the same time and are shown as a chord but thereafter, as notes are added to the arpeggio, instead of adding notes to the chord, NWC cuts off the previous note/chord before starting the next. Maybe it would be too complicated to do anything else; it seems a reasonable import strategy.

Quote
just that this is yet another thing that anyone hoping to decypher MIDI import needs to know.
It is worth saying that any automated tool (even NWC itself) would struggle to deal with a source like your example, having a degree of randomness; presumably this one is was played in real time. It has, at least, shown me a small bug in the code at line 47, which should be
Code:   [Select · Download]
$tied_note_pending=false; $last_note_was_rest = false;

in the time I've been on this forum Eric hasn't revealed his reasons for not improving those areas of MIDI import.
I can only hope that Eric will be moved to share his thoughts on the subject. ;-)

-- Brian
Logged
Rick G.
Virtuoso
« Reply #13 on: 2009-02-15 03:08 PM »

Rick, I don't understand how this example illustrates the behaviour you mentioned, viz rapid single notes imported as a chord; indeed it shows chords imported as single notes.
Much depends on the definition of a chord.
Logged

Registered user since 1996
Flurmy
Virtuoso


« Reply #14 on: 2009-02-16 02:52 AM »

Quote
It has, at least, shown me a small bug in the code at line 47, which should be

Line 47?

Of course you mean line 117.
The variable $last_item_was_rest is never used anywhere else.
Logged
Brian Maskell
NWC2 User
« Reply #15 on: 2009-02-16 07:47 AM »

Of course you mean line 117.
The variable $last_item_was_rest is never used anywhere else.
Yes, indeed I do mean line 117; thanks for pointing it out. Changing the initialisation was the line of least resistance even though $last_item_was_rest is the more appropriate name.

-- Brian
Logged
Flurmy
Virtuoso


« Reply #16 on: 2010-04-14 02:34 AM »

After the latest modifications to the user tool framework, brm_Triplify doesn't work anymore.

It gets:
Quote
Fatal error: Cannot re-assign $this in C:\Programmi\NoteWorthy Composer 2\Scripts\brm_Triplify.php on line 162

The problem is the name "$this" clashing with the new libraries.

To solve the problem simply rename all the instances of "$this" in brm_Triplify as, for example, $this_triplet.
Logged
Randy Williams
Virtuoso
« Reply #17 on: 2010-04-14 07:55 AM »

Quote
The problem is the name "$this" clashing with the new libraries.

Brian may be using an old version of PHP - I think NWC used to use version 3 or so.  He may not see this problem with "$this" until he upgrades to the version 5 PHP that the latest NWC now uses.

Brian, if you upgrade to PHP 5 [and I think it's reasonable to expect all users of your script to upgrade], within function isValidGroup, I think you could just use in_array in place of the last foreach loop, since PHP supports arrays as the "needle" as of version 4.2:
     return in_array($lengthSet, $validLengthGroup);

You could perhaps similarly avoid a foreach loop in function note_length, using array_intersect_key:
     return array_shift(array_intersect_key($tickLength, $opts["Dur"]));

Other miscellaneous comments:
- The "if ($TripletQ) array_push..." could probably be simply "$TripletQ[] = $o;"
- Your 2 array_search's didn't work because you have the 2 parameters reversed: some functions are needle/haystack, and others are haystack/needle :-(
- Your first "continue 2" could probably simply be a "break"? - your last "continue" could be removed?
Logged
NoteWorthy Online
Admin


« Reply #18 on: 2010-05-04 09:09 AM »

The NWC2 User Tool Starter Kit started with PHP4. The latest kit includes PHP5, which has a new object model which enforces a more restrictive use of a $this variable (which is predefined as an instance pointer in class object methods).
Logged
Pages: [1]   Go Up
  Print  
 
Jump to:  

Powered by SMF 1.1.19 | SMF © 2013, Simple Machines
Page created in 0.156 seconds with 19 queries.

Copyright © 2014 Noteworthy Software, Inc.