why you should use MIDI::Simple.

[email protected] ("Sean M. Burke") Thu, 26 Oct 2000 20:30:13 -0600
Newsgroups perl.midi
Message-ID <[email protected]>
Thumper, your program is practically an adverisement for MIDI::Simple --
because if it weren't for MIDI::Simple, your code is what all
MIDI-generation code would have to look like!

At 10:42 PM 2000-10-25 -0700, Web Thumper wrote:
>Below is sniplets of perl script (from a much larger project) to create a
>click-track of four notes.  The problem is that unless I add another note to
>the first measure I only hear 3 notes instead of 4 notes.
>[...]

First off, this business of 
  $array[$lastindex] = ...; ++$last_index;
is just nasty.  Use push @array, ...;

I.e., replace this:
  $notes[$nindex] = ['note_on',384,9,0,0] ; $nindex++ ;
  $notes[$nindex] = ['note_off',0,9,0,0] ; $nindex++ ;
  $notes[$nindex] = ['note_on',0,9,0,0] ; $nindex++ ;
  $notes[$nindex] = ['note_off',0,9,0,0] ; $nindex++ ;
  $notes[$nindex] = ['note_on',0,9,0,0] ; $nindex++ ;
  $notes[$nindex] = ['note_off',0,9,0,0] ; $nindex++ ;
  $notes[$nindex] = ['note_on',0,9,0,0] ; $nindex++ ;
  $notes[$nindex] = ['note_off',0,9,0,0] ; $nindex++ ;
  $notes[$nindex] = ['text_event',48] ;
with this:
  push @notes,
    ['note_on',384,9,0,0],
    ['note_off',0,9,0,0],
    ['note_on',0,9,0,0],
    ['note_off',0,9,0,0],
    ['note_on',0,9,0,0],
    ['note_off',0,9,0,0],
    ['note_on',0,9,0,0],
    ['note_off',0,9,0,0],
    ['text_event',48],
  ;
Now, you can't tell me you've never seen push() before!


Second off:
if you run your program with perl -w, you get:
Use of uninitialized value at [path]/MIDI/Event.pm line 1107.
Use of uninitialized value at [path]/MIDI/Event.pm line 1107.
Use of uninitialized value at [path]/MIDI/Event.pm line 958.

Event.pm 1107 says:
      } elsif($event eq 'time_signature') {
 	$event_data = pack("CCwCCCC",  0xFF, 0x58, 4, @E[0,1,2,3] );
which send us looking to the docs for time_signature.
MIDI::Event says time_signature needs five parameters:
 ('time_signature', I<dtime>, I<nn>, I<dd>, I<cc>, I<bb>)
But your code feeds it only four:
    ['time_signature',0,4,2,24],

As to line 958... hm, it's inside MIDI::Event::Encode...
   if($last->[0] eq 'text_event' and length($last->[2]) == 0) {
which sends us looking for text_events; your code has:
    ['text_event',48] ;
but the docs say it has to take two parameters:
 ('text_event', I<dtime>, I<text>)


Now, adding $midi1->dump({dump_tracks => 1}) and $midi2->dump({dump_tracks
=> 1}) at appropriate moments (right after writing) shows:
 ...    
    # Track #1 ...
    MIDI::Track->new({
      'type' => 'MTrk',
      'events' => [  # 8 events.
        ['note_on', 0, 9, 37, 100],
        ['note_off', 0, 9, 37, 0],
        ['note_on', 384, 9, 37, 100],
        ['note_off', 0, 9, 37, 0],
        ['note_on', 384, 9, 37, 100],
        ['note_off', 0, 9, 37, 0],
        ['note_on', 384, 9, 37, 100],
        ['note_off', 0, 9, 37, 0],
      ]
    }),
 ...
    # Track #1 ...
    MIDI::Track->new({
      'type' => 'MTrk',
      'events' => [  # 17 events.
        ['note_on', 0, 9, 37, 100],
        ['note_off', 0, 9, 37, 0],
        ['note_on', 384, 9, 37, 100],
        ['note_off', 0, 9, 37, 0],
        ['note_on', 384, 9, 37, 100],
        ['note_off', 0, 9, 37, 0],
        ['note_on', 384, 9, 37, 100],
        ['note_off', 0, 9, 37, 0],
        ['note_on', 384, 9, 0, 0],
        ['note_off', 0, 9, 0, 0],
        ['note_on', 0, 9, 0, 0],
        ['note_off', 0, 9, 0, 0],
        ['note_on', 0, 9, 0, 0],
        ['note_off', 0, 9, 0, 0],
        ['note_on', 0, 9, 0, 0],
        ['note_off', 0, 9, 0, 0],
        ['end_track', 48],
      ]
    }),

and....

>This script creates two files - test1.mid and test2.mid.  The test1.mid only
>plays 3 notes.  The test2.mid will play all four notes, but then it has that
>note of silence.

...and that is exactly what the events you encode say to do.

test1 starts out:
        ['note_on', 0, 9, 37, 100],
        ['note_off', 0, 9, 37, 0],
meaning: hit a note, and then at the exact same moment in time, end it.
What's a sequencer supposed to do with that?


And test2 ends:
    ['text_event',48]
which means "wait 48 ticks, and then express a text event".  Which is a
pause.  (It's left to the reader to figure out the various reasons it turns
into an 'end_track' event.) 

Now, as MIDI::Filespec explains, "<delta-time>... represents the amount of
time before the following event."  I.e., THAT event. (In the encoding, the
delta time precedes every actual event code -- that's what they mean by
"following").  So this:
        ['note_off', 0, 9, 37, 0],
means "wait 0 ticks before applying note_off on channel 9...".

With that in mind, you'll see why this is wrong:
        ['note_on', 384, 9, 37, 100],
        ['note_off', 0, 9, 37, 0],
        ['note_on', 384, 9, 0, 0],
        ['note_off', 0, 9, 0, 0],

However, generating your own MIDI files with raw events is absolute folly
-- and that's why I wrote MIDI::Simple, so you'll never have to deal with
such things.  Use MIDI::Simple.

Among other things, MIDI::Simple's event-functions (text_event, etc.) are
all prototyped, so if you go using the wrong number of parameters to them,
it's a compile-time error.  And the n() and r() functions will make it
rather easier to generate notes and rest in the appropriate places.

--
Sean M. Burke  [email protected]  http://www.spinn.net/~sburke/