1
Show Posts
This section allows you to view all Show Posts made by this member. Note that you can only see Show Posts made in areas you currently have access to.
Messages - Brian Maskell
2
General Discussion / Re: Upgrade problems?
Selector 01 Clef Selector (c)
Selector 02 Key Signature Selector (k)
Selector 03 Time Signature Selector (g)
Selector 04 Note Chord Selector (h)
Selector 05 Rest Selector
Selector 06 Bar Selector (b)
Selector 07 Dynamics Selector (d)
Selector 08 Tempo Variance Marks (e)
Selector 09 Pedal Marks (u)
Selector 10 Note Space
Selector 11
Selector 12
Selector 13
Selector 14
Selector 15
Selector 16
3
General Discussion / Re: Skipping a section of music
4
General Discussion / Skipping a section of music
I don't want actually to remove the cut sections, just skip over them, so I added on each stave:
at bar 233 a special ending (D) and the text 'cut to 369'
at bar 369 a special ending (1)
at bar 433 a special ending (D) and the text 'cut to 571'
at bar 571 a special ending (1)
The second cut works fine but in the first, play resumes at bar 321. If I add:
at bar 321 a special ending (D)
that cures the problem; play now resumes as required at 369 but what was going wrong at bar 321 initially and is there a better way to do this?
The working version is attached.
5
General Discussion / Re: Changing accidentals
6
User Tools / Tools to favour sharps or flats in chords
Copy the code and save it in your Scripts folder as: brm_FavourSharps.php
Code: [Select · Download]
<?php
/*******************************************************************************
brm_FavourSharps 1.00
Brian Maskell
History:
[2010-09-29] Version 1.00 - Initial version
*******************************************************************************/
require_once("lib/nwc2clips.inc");
$clip = new NWC2Clip('php://stdin');
// MAIN PROGRAM
echo $clip->GetClipHeader()."\n";
foreach ($clip->Items as $item) {
$o = new NWC2ClipItem($item);
if (($o->GetObjType()=="Note")||($o->GetObjType()=="Chord")) {
$opts = $o->GetOpts();
$pos = $opts["Pos"];
if (is_array($pos)) {
for ($i=0; $i<count($pos); $i++) {
$n = new NWC2NotePitchPos($pos[$i]);
if ($n->Accidental=="b") {
$n->Accidental="#";
$n->Position--;
$pos[$i]=$n->ReconstructClipText();
unset($n);
}
}
} else {
$n = new NWC2NotePitchPos($pos);
if ($n->Accidental=="b") {
$n->Accidental="#";
$n->Position--;
$pos=$n->ReconstructClipText();
unset($n);
}
}
$o->Opts["Pos"] = $pos;
} elseif ($o->GetObjType()=="RestChord") {
$opts = $o->GetOpts();
$pos = $opts["Pos2"];
if (is_array($pos)) {
for ($i=0; $i<count($pos); $i++) {
$n = new NWC2NotePitchPos($pos[$i]);
if ($n->Accidental=="b") {
$n->Accidental="#";
$n->Position--;
$pos[$i]=$n->ReconstructClipText();
unset($n);
}
}
} else {
$n = new NWC2NotePitchPos($pos);
if ($n->Accidental=="b") {
$n->Accidental="#";
$n->Position--;
$pos=$n->ReconstructClipText();
unset($n);
}
}
$o->Opts["Pos2"] = $pos;
} else {
// just echo the item unchanged
}
echo $o->ReconstructClipText()."\n";
}
echo NWC2_ENDCLIP."\n";
exit(NWC2RC_SUCCESS);
// exit(NWC2RC_REPORT); // alternative for debugging
?>
Copy the code and save it in your Scripts folder as: brm_FavourFlats.php
Code: [Select · Download]
<?php
/*******************************************************************************
brm_FavourFlats 1.00
Brian Maskell
History:
[2010-01-22] Version 1.00 - Initial version
*******************************************************************************/
require_once("lib/nwc2clips.inc");
$clip = new NWC2Clip('php://stdin');
// MAIN PROGRAM
echo $clip->GetClipHeader()."\n";
foreach ($clip->Items as $item) {
$o = new NWC2ClipItem($item);
if (($o->GetObjType()=="Note")||($o->GetObjType()=="Chord")) {
$opts = $o->GetOpts();
$pos = $opts["Pos"];
if (is_array($pos)) {
for ($i=0; $i<count($pos); $i++) {
$n = new NWC2NotePitchPos($pos[$i]);
if ($n->Accidental=="#") {
$n->Accidental="b";
$n->Position++;
$pos[$i]=$n->ReconstructClipText();
unset($n);
}
}
} else {
$n = new NWC2NotePitchPos($pos);
if ($n->Accidental=="#") {
$n->Accidental="b";
$n->Position++;
$pos=$n->ReconstructClipText();
unset($n);
}
}
$o->Opts["Pos"] = $pos;
} elseif ($o->GetObjType()=="RestChord") {
$opts = $o->GetOpts();
$pos = $opts["Pos2"];
if (is_array($pos)) {
for ($i=0; $i<count($pos); $i++) {
$n = new NWC2NotePitchPos($pos[$i]);
if ($n->Accidental=="#") {
$n->Accidental="b";
$n->Position++;
$pos[$i]=$n->ReconstructClipText();
unset($n);
}
}
} else {
$n = new NWC2NotePitchPos($pos);
if ($n->Accidental=="#") {
$n->Accidental="b";
$n->Position++;
$pos=$n->ReconstructClipText();
unset($n);
}
}
$o->Opts["Pos2"] = $pos;
} else {
// just echo the item unchanged
}
echo $o->ReconstructClipText()."\n";
}
echo NWC2_ENDCLIP."\n";
exit(NWC2RC_SUCCESS);
// exit(NWC2RC_REPORT); // alternative for debugging
?>
7
General Discussion / Re: Grace notes exported to NWC 1.75
8
General Discussion / Re: Grace notes exported to NWC 1.75
I think my work-around must be to use only the configurations illustrated in bars 1 and 4 of my example. They don't need any attributes to export reasonably and they display well (though not identically) using any of the programs; the addition of a Lyric:Never attribute by the export process is unnecessary but does no harm.
I too have never found any issues in having NWC1, NWC1 Player, NWC2 and NWC2 Viewer all installed.
9
General Discussion / Grace notes exported to NWC 1.75
Stave 1 of the attached file shows examples of grace notes with and without slurs; all notes have the attribute Lyric:Default.
Stave 2 shows what happens when this is exported to a v1.75 file. The middle notes in bars 2 and 4 now have the attribute Lyric:Never and the slur in bar 3 is missing.
Stave 3 has been doctored by modifying the Lyric attribute of three notes as indicated by the text notes.
Stave 4 confirms that, when this is exported, the lyrics are correct, although the slur in bar 3 is still missing.
Is there a good reason why export from NWC2 behaves in this way?
10
General Discussion / Re: Overlaying parts
Here is a little sample of two parts on one stave. NWC resolves all the conflicts.
Code: (nwc) [Select · Download]
!NoteWorthyComposerClip(2.0,Single)
|Clef|Type:Treble
|Key|Signature:F#
|TimeSig|Signature:2/4
|Text|Text:"two parts on one stave"|Font:StaffItalic|Pos:9
|Rest|Dur:8th
|Chord|Dur:8th|Pos:n-3,-1|Opts:Stem=Up
|Chord|Dur:8th|Pos:#-6,-3|Opts:Stem=Up,Beam=First
|Chord|Dur:8th|Pos:-6,-4|Opts:Stem=Up,Beam=End
|Bar
|Chord|Dur:4th|Pos:b-5,b-4
|Chord|Dur:8th|Pos:-1,0|Opts:Stem=Up,Beam=First
|Chord|Dur:8th|Pos:-1,0|Opts:Stem=Up,Beam=End
!NoteWorthyComposerClip-End
If I put the two parts on separate staves (eg using adp's very useful Parts tool) and simply overlay them, there are stem conflicts with the quaver notes, accidental conflicts and note head conflicts. All of these can be resolved with some rather painstaking adjustment; for example here are two separate staves which, when overlayed, will print correctly and play correctly, allow the audio of the parts to be controlled separately but not require anything to be duplicated or dictate stem directions.
Upper part:
Code: (nwc) [Select · Download]
!NoteWorthyComposerClip(2.0,Single)Lower part:
|Clef|Type:Treble
|Key|Signature:F#
|TimeSig|Signature:2/4
|Text|Text:"manually adjusted and overlayed"|Font:StaffItalic|Pos:9
|Rest|Dur:8th
|Note|Dur:8th|Pos:-1|Opts:Stem=Up
|Note|Dur:8th|Pos:-3|Opts:Stem=Up,Beam=First
|Note|Dur:8th|Pos:-4|Opts:Stem=Up,Beam=End
|Bar
|Note|Dur:4th|Pos:b-4|Opts:StemLength=0,XNoteSpace=1
|Note|Dur:8th|Pos:0|Opts:Stem=Up,StemLength=0,Beam=First,XNoteSpace=1
|Note|Dur:8th|Pos:0|Opts:Stem=Up,StemLength=0,Beam=End,XNoteSpace=1
!NoteWorthyComposerClip-End
Code: (nwc) [Select · Download]
!NoteWorthyComposerClip(2.0,Single)
|Clef|Type:Treble
|Key|Signature:F#
|TimeSig|Signature:2/4
|Rest|Dur:8th
|Note|Dur:8th|Pos:n-3|Opts:Stem=Up,StemLength=9
|Note|Dur:8th|Pos:#-6|Opts:Stem=Up,StemLength=10,Beam=First
|Note|Dur:8th|Pos:-6|Opts:Stem=Up,StemLength=9,Beam=End
|Bar
|Note|Dur:4th|Pos:b-5|Opts:XAccSpace=1
|Note|Dur:8th|Pos:-1|Opts:Stem=Up,Beam=First
|Note|Dur:8th|Pos:-1|Opts:Stem=Up,Beam=End
!NoteWorthyComposerClip-End
So it can be done, but with a lot of trouble to replicate what NWC does. I was considering whether this process can be automated.
11
General Discussion / Overlaying parts
It is of course possible to have two sets of staves - one visible but muted, the other undisplayed but audible - but that makes editing tedious and there is a danger of discrepancies creeping in between the two sets.
Does anyone have ideas of how to capture NWC's musical intelligence, for example by deleting one note in a chord but retaining any special treatment given to the other?
-- Brian Maskell
12
User Tools / Re: Imported triplets
Of course you mean line 117.
The variable $last_item_was_rest is never used anywhere else.
-- Brian
13
User Tools / Re: Imported triplets
The left hand notes are arpeggiated, but NWC imports them as Chords. I'm not saying NWC is wrong here,
Quote
just that this is yet another thing that anyone hoping to decypher MIDI import needs to know.
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.
-- Brian
14
User Tools / Re: Imported triplets
It will churn out chords in places where it sees a fast run of single notes.
Do you have an example of this? I have not managed to generate one.
-- Brian
15
User Tools / Re: Imported triplets
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 ($TripletQ) array_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)==0) array_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);
?>
16
User Tools / Re: Imported triplets
-- Brian
17
User Tools / Re: Imported triplets
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
18
User Tools / Re: Imported triplets
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 observedHere 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!
$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)
);
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?
19
User Tools / Imported triplets
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 ($TripletQ) array_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);
?>
20
User Tools / Re: Documentation for the User Tools API
Indeed, I have found "Variable Dump for Developers (nwsw)" very instructive but of course it only shows the elements I have included in my clip. No doubt I shall discover them all eventually; I was hoping for a quicker and more reliable route.
-- Brian
21
User Tools / Documentation for the User Tools API
I am slowly getting the hang of it but I keep encountering new (to me) elements; ArticulationsOnStem is the my latest and in this case I still havn't worked out when it occurs and what effect it has.
On this basis, writing robust User Tool code is a rather uncertain business.
-- Brian