MIDI::Score: Duration < 0, Adding Events, Native NRPNs?



Hi,
In some MIDI files I receive negative note durations from MIDI::Score. I'm
pretty certain it is some bug in my own code. Here's a minimal version:

test.plx:

#!perl
# This is perl, v5.8.8 built for MSWin32-x86-multi-thread.
use strict; use warnings;
use MIDI; # V0.81.

(my($inFile, $outFile) = @ARGV) == 2 or die "2 files please.\n";
my $opus = MIDI::Opus->new( { from_file => $inFile } );
my $ticks = $opus->ticks();
my $tracks = $opus->tracks_r();
my $newTracks; # For output.

for(@$tracks)
{ # Copy each one.
# Convert only events.
if($_->type() ne 'MTrk')
{ # Not events, but data in stead.
push @$newTracks, $_->copy();
next;
} # if

my $events = $_->events_r();
my $score = MIDI::Score::events_r_to_score_r($events);

# MIDI::Score says the format is:
# ['note', starttime, duration, channel, note, velocity]
my @bad = grep { $_->[0] eq 'note' and $_->[2] < 0 } @$score;
if(@bad)
{ # No such thing as negative duration? At least in seqs.
warn "Bad: [ @$_ ]\n" for @bad;
# This reveals both note and note_on events.
# print $_->dump;
# die "Stopped.\n";
} # if

# Turn score back to events and tracks.
my $newEvents = MIDI::Score::score_r_to_events_r($score);
push @$newTracks, MIDI::Track->new( {events_r => $newEvents} );
} # for

# Now we can write out the file safely.
my $newOpus = MIDI::Opus->new;
$newOpus->tracks_r($newTracks);
$newOpus->ticks($ticks); # Many seqs support > 96 PPQ.
$newOpus->write_to_file($outFile);
print "Done.\n";

The check for negative note lengths appears to catch some note events in
quite a number of MIDI files. Here's one nice piano boogie called sockhop
which does this:

http://netdancer.com/midis/Sockhop.mid

Say we run:

test.plx sockhop.mid test.mid

The output would be:

Bad: [ note 15344 -15344 2 64 88 ]
Bad: [ note 15351 -15351 2 67 82 ]
Bad: [ note 71520 -71520 9 39 102 ]
Bad: [ note 71760 -71760 9 39 85 ]
Bad: [ note 73429 -73429 9 39 108 ]
Bad: [ note 73674 -73674 9 39 108 ]
Bad: [ note 74400 -74400 9 39 102 ]
Bad: [ note 115680 -115680 9 38 102 ]
Bad: [ note 115920 -115920 9 38 85 ]
Bad: [ note 116640 -116640 9 38 102 ]
Bad: [ note 167907 -167907 9 38 99 ]
Bad: [ note 15344 -15344 12 64 103 ]
Bad: [ note 15351 -15351 12 67 96 ]
Bad: [ note 71520 -71520 15 39 120 ]
Bad: [ note 71760 -71760 15 39 100 ]
Bad: [ note 73429 -73429 15 39 127 ]
Bad: [ note 73674 -73674 15 39 127 ]
Bad: [ note 74400 -74400 15 39 120 ]
Bad: [ note 115680 -115680 15 38 120 ]
Bad: [ note 115920 -115920 15 38 100 ]
Bad: [ note 116640 -116640 15 38 120 ]
Bad: [ note 167907 -167907 15 38 117 ]
Done.

Oddly enough the Note duration and its start ttime are exactly the same
absolute tick counts, according to this dump, though they have a different
sign. Obviously this shouldn't happen and at any rate no MIDI sequencer I've
ever used shows the lengths in the event list as being negative. Despite
this a proper copy of the MIDI is written hinting at some problem in my
usage of the SCore and Event structures.


By the way, another MIDI file with this negative length problem is the one
coming with Windows i.e. %windir%\media\town.mid

But to make things more interesting many MIDI files are converted quite all
right. Take Axel F for example at the same MIDI site

http://netdancer.com/midis/Axelf.mid

Uncommenting the dump line and dumping the head of the output would produce
(for sockhop.mid):

Bad: [ note 15344 -15344 2 64 88 ]
Bad: [ note 15351 -15351 2 67 82 ]
MIDI::Track->new({
'type' => 'MTrk',
'events' => [ # 2879 events.
['raw_meta_event', 0, 33, "\x00"],
['track_name', 0, 'Piano Right Hand'],
['patch_change', 0, 2, 0],
['control_change', 0, 2, 7, 108],
['control_change', 0, 2, 10, 64],
['control_change', 0, 2, 91, 65],
['control_change', 0, 2, 93, 0],
['pitch_wheel_change', 0, 2, 0],
['control_change', 0, 2, 64, 0],
['note', 1173, 122, 2, 74, 108],
['note', 1210, 82, 2, 75, 108],
['note', 1288, 150, 2, 76, 108],
['note_on', 1292, 2, 75, 0],
['note_on', 1295, 2, 74, 0],
['note_on', 1438, 2, 76, 0],
['note', 1475, 146, 2, 79, 108],
['note_on', 1621, 2, 79, 0],
['note', 1700, 136, 2, 81, 108],
...

Is the output supposed to contain both the note_on events in the event
structure an the abstracted note events with duration?

I wonder what might cause the negative length to show up. I'm suspecting
some obvious error in my own code very first but if that's not the case,
some other possibilities do come to mind.

1. Many sequencers support HiFi resolutions far exceeding 96 pulses per
quarter note (PPQ). Sonar can do 960, for example, even if many hardware is
96 or 120. I've noticed that MIDI::Simple assumes 96 PPQ and thus Windows's
default 192 PPQ MIDs play all too slowly after conversion. Unless you add
the PPQ yourself as I did in the above example with MIDI::Score.

2. Imagine a hypothetical scenario. That is hold down a note while you start
recording and then release it. IF the sequencer is silly, and doesn't
monitor active sensing, see:

http://www.borg.com/~jglatt/tech/midispec/sense.htm

then it might just record a note off event without a corresponding note on
at all. A smarter sequencer might wait for you to release notes before
beginning to record or insert the missing note on automatically. If
MIDI::Score has been written under the assumption of strict note on/off
pairs with note on always first, such mishaps, though arguably bugs in the
MIDI performance, might affect matters.

3. Many synthesizers, Rolands in particular, have adopted a special
convention for note off events. In stead of actually transmitting as many
bytes as they'd have for a normal note off, they can use note on messages
with velocity 0 and running status to compress data. See:

http://www.borg.com/~jglatt/tech/midispec/run.htm

and

http://www.borg.com/~jglatt/tech/midispec/noteon.htm

In addition to saving bytes, very few keyboards actually send out varying
release velocity values anyway, so the note on method doesn't lose
information. For MIDI::Score is a note on with velo 0 equivalent to a note
off? I've tried reading the source and while I can understand bits and
pieces, I don't seem to get the gist of the processing.

By the way, I've also got a related question on adding new MIDI data on
event structures that have been converted to scores. Do I need to add
note_on events as well? Here I'm refering to the above score structure that
had both note and note_off events. I thought I could simply, when modifying
MIDI data, push 'note' events understood by MIDI::Score (including time and
duration) at the very end of the score structure and then sort the whole
thing by time using sort_score_r. After that, I would finally convert the
whole thing back to events and tracks. I'd imagine the same thing works for
controellers, sysex or whatever. Sometimes, in another script of mine,
adding notes as described above works fine and at other times not. The issue
is problably something different, but thought I'd ask about the canonical
way of adding data to a MIDI::Score. I'm interested in controllers, sysex
and channel after touch as all of my synths send those. Another abstraction,
which would be nice to get into MIDI::Score, would be virtual RPN and NRPN
events as in Sonar. Normally they are composed of several controllers.
Chehck out:

http://www.borg.com/~jglatt/tech/midispec/dslider.htm

http://www.borg.com/~jglatt/tech/midispec/nrpn.htm

http://www.borg.com/~jglatt/tech/midispec/rpn.htm

PS: My favorite MIDI site of all time, which also includes both MIDI and
MIDI file specs, is this Jeff Glatt's MIDI programming page:

http://www.borg.com/~jglatt/tech/miditech.htm

Much better and easier to understand than just the MIDI BNF quoted in the
manual pages.
Most of the links I've put here are from the above site.

--
With kind regards Veli-Pekka Tätilä (vtatila@xxxxxxxxxxxxxxxxxxxx)
Accessibility, game music, synthesizers and programming:
http://www.student.oulu.fi/~vtatila/


.



Relevant Pages

  • Re: MIDI::Score: Duration < 0, Adding Events, Native NRPNs?
    ... has ever happened to me in Perl. ... # ['note', starttime, duration, channel, note, velocity] ... events in quite a number of MIDI files. ... on messages with velocity 0 and running status to compress data. ...
    (comp.lang.perl.misc)
  • Re: MIDI::Score: Duration < 0, Adding Events, Native NRPNs?
    ... In some MIDI files I receive negative note durations from MIDI::Score. ... I don't think it's your bug. ... # ['note', starttime, duration, channel, note, velocity] ...
    (comp.lang.perl.misc)
  • Re: Is there any software to convert songs to ringtones
    ... kcp wrote: ... Ringtones do not have midi formats. ... Smaller number gives larger duration. ... A simple tone Go to the composer mode ...
    (rec.music.indian.misc)
  • Re: Midi Programming environments - who has experience?
    ... you must use a programming language. ... When you will effective send midi events, ... But a short program shows that it is possible to generate 25 random note events in a ms. ... print 'Duration:', duration ...
    (comp.music.midi)
  • Re: Sequencing
    ... >> about all these pay sites that have midi files of all these songs? ... >> full band and looking to just sequence stuff outside of drums, guitar, ... The quality of MIDI files is all over the board. ...
    (rec.music.makers.percussion)