MIDI-Perl comments

[email protected] (Roger Crew) Wed, 14 Mar 2001 23:10:16 -0800 (PST)
Newsgroups perl.midi
Message-ID <[email protected]>
(...ok, maybe this *is* of slightly general interest after all...)

------- Start of forwarded message -------
Date: 13 Mar 2001 18:22:01 -0800
From: Roger Crew <[email protected]>
Subject: MIDI-Perl comments
To: [email protected]

so I picked up MIDI-Perl 0.79 from CPAN some weeks ago.  
I appreciated not having to write a MIDI file reader from scratch
(... doing it once already in emacs-lisp was enough for me.. :)

Anyway, noticing various queries scattered in the code and seeing as I
have the actual specs (MIDI 1.0 spec and the Standard MIDI Files 1.0
- --- dating from 1996, but as I understand it these are still the
latest versions) in front of me, I figured I may as well provide what
answers I can.

There are also a couple other random observations that I'll put first,
since they're about stuff that is easily fixable.

(And if you want to forward any of this to the mailing list, feel free;
I didn't because I'm not sure it's of general interest, but I could be
wrong.)

Onward...

=========================
(1) [Event.pm] bad masks
=========================

*** c:/prgfiles/perl/site/lib/MIDI/Event.pm	Sat May 13 22:53:54 2000
- --- Standard Input	Tue Mar 13 13:16:36 2001
***************
*** 1028,1040 ****
          $status = 0xB0 | (int($E[0]) & 0x0F);
          $parameters = pack('C2',
!                            int($E[1]) & 0xFF, int($E[2]) & 0xFF);
        } elsif($event eq 'patch_change'){
          $status = 0xC0 | (int($E[0]) & 0x0F);
          $parameters = pack('C',
!                            int($E[1]) & 0xFF);
        } elsif($event eq 'channel_after_touch'){
          $status = 0xD0 | (int($E[0]) & 0x0F);
          $parameters = pack('C',
!                            int($E[1]) & 0xFF);
        } elsif($event eq 'pitch_wheel_change'){
          $status = 0xE0 | (int($E[0]) & 0x0F);
- --- 1028,1040 ----
          $status = 0xB0 | (int($E[0]) & 0x0F);
          $parameters = pack('C2',
!                            int($E[1]) & 0x7F, int($E[2]) & 0x7F);
        } elsif($event eq 'patch_change'){
          $status = 0xC0 | (int($E[0]) & 0x0F);
          $parameters = pack('C',
!                            int($E[1]) & 0x7F);
        } elsif($event eq 'channel_after_touch'){
          $status = 0xD0 | (int($E[0]) & 0x0F);
          $parameters = pack('C',
!                            int($E[1]) & 0x7F);
        } elsif($event eq 'pitch_wheel_change'){
          $status = 0xE0 | (int($E[0]) & 0x0F);

Parameter bytes have to be in the range 0x00-0x7F, otherwise they can
be read as status bytes.  Whether this is a bug depends whether the
parameters are being checked elsewhere (I'm guessing if that were the 
case, then you wouldn't have bothered with masking at all...).

==============================================================
(2) [Score.pm] release velocity == 0 is not the best default
==============================================================

in score_r_to_events_r()

*** perl/site/lib/MIDI/Score.pm	Mon Aug 21 15:50:52 2000
- --- Standard Input	Tue Mar 13 12:38:56 2001
***************
*** 272,274 ****
        $note_off[1] += $duration;
!       $note_off[4] = 0; # set volume to 0
        push(@events, \@note_on, \@note_off);
- --- 272,274 ----
        $note_off[1] += $duration;
!       $note_off[4] = 64; # use default velocity for note-off
        push(@events, \@note_on, \@note_off);

This isn't a bug but it'll make life easier down the road...

The spec allows for the possibility of devices that implement 
release-velocity, in which case it matters what number you use.
They also
(1) recommend velocity==64 as the value to use for devices that 
    ignore note-on velocity or release velocity and
(2) stipulate that note_on/velocity==0 be treated 
    exactly as note_off/velocity==64.  

Since transmission on the wire favors note_on/velocity==0, I'd like to
be able to convert to that as often as possible.  Having release
velocities other than 64 creep in gratuitously makes that less
convenient (since one then has to worry about the possibility of them
being real, add an option to ignore them, etc...).

Likewise, events_r_to_score_r should be emitting some kind of warning
if it encounters non-64 release velocities, so that the user can at
least know that information is being dropped on the floor.  

Ideally, release velocity would eventually be included in the 'note'
score element, but that can probably wait until somebody gets an
actual device for which it matters or a midi file intended for such a
device (I'm guessing these things are rare).


=====================================================
(3) [Filespec.pod] meta events and other differences
=====================================================

    * The file below is, in terms of computers and computer music, quite
    vintage.  However, code built to this spec runs just fine on all of
    the two-dozen or so MIDIs I pulled at random from various places on
    the Net for testing.  The only thing I've found that's not in this
    spec is a meta-event of the form "FF 21 01 00" that seems to occur
    often as the first event in a track.  (I infer it means "start track",
    but I don't know, so I've made no effort to deal with it other than to
    parse it as C<('raw_meta_event', 0, 33, "\x00")>.)  There may be other
    extensions that've been made to the standard, and which are being
    used, but I've not yet run into them.  Email me if you run across any.

My copy of the file spec includes just one more meta event:

    =head2 FF 20 01 cc : MIDI Channel Prefix

    The MIDI channel (0-15) contained in this event may be used to
    associate a MIDI channel with all events which follow, including
    System Exclusive and meta-events.  This channel is "effective" until
    the next normal MIDI event (which contains a channel) or the next MIDI
    Channel Prefix meta-event.  If MIDI channels refer to "tracks", this
    message may help jam several tracks into a format 0 file, keeping
    their non-MIDI data associated with a track.  This capability is also
    present in Yamaha's ESEQ file format.

There is no mention of an FF 21 01 00 meta-event.  Assuming you weren't
mistyping 21 for 20, I figure whatever it is, it's not standard, 
and if it's a random manufacturer extension, they deserve to lose
because they should be using "FF 7F len data", instead.

The other meta events are as you have them (minus the commentary about
conferences that occurred twelve years ago...)

Other differences between Filespec.pod and the current MIDI file spec:

(1) The description of the header (MThd) chunk has been expanded somewhat.

    In particular, the parameter that is either ticks per quarter note 
    or the (negative-SMPTE-frames/sec)+(ticks/frame) number is now 
    referred to as "divisions".  

    (...I suppose if it were up to me, I'd remove the confusing overloading 
    on the words "tempo" and "ticks" and rename [or alias so that old 
    code continues to work]

       MIDI::Simple::Tempo   =>  MIDI::Simple::Division
       $opus->{ticks}        =>  $opus->{division}

    I guess that's a taste issue, but it would allow you to blow away
    the paragraphs about why 'Tempo' is different from 'tempo' or 
    the differences between the various entities named 'ticks'...)

(2) the "MIDI Transmission of MIDI Files" section does not appear.
    I'm guessing the notion of using MIDI as an all-purpose file 
    transfer protocol makes even less sense now than it did in 1988.

(3) my version has an extra final example subsection under
    "Program Fragments and Example MIDI Files" about calculating 
    delta times.  nothing earthshattering.

==================================================================
(4) [Events.pm] feel free to blow away song-position code, etc...
==================================================================

    ######################################################################
    # Now, the MIDI file spec says:
    #  <track data> = <MTrk event>+
    #  <MTrk event> = <delta-time> <event>
    #  <event> = <MIDI event> | <sysex event> | <meta-event>
    #
    # I know that, on the wire, <MIDI event> can include note_on,
    # note_off, and all the other 8x to Ex events, AND Fx events
    # other than F0, F7, and FF -- namely, <song position msg>,
    # <song select msg>, and <tune request>.
    #
    # Whether these can occur in MIDI files is not clear from
    # the MIDI file spec.
    # 
    # So, I'm going to assume that they CAN, in practice, occur.
    # I don't know whether it's proper for you to actually emit these
    # into a MIDI file.
    #
    
The file spec explicitly says that <MIDI event> is any *channel*
message, and the MIDI (wire) spec itself is pretty clear about channel
messages [8n-En] and system messages [Fn] being distinct.

Also since system realtime [F8-FF] and system common [F1-F7] messages
(i.e., the system messages other from the system exclusive messages)
are mainly about synchronizing clocks/sequencers/drum-machines,
it's not clear there'd ever be any point to having these in files.

   =head1 MIDI BNF
   ...
   Note that this seems to describe MIDI events as they can occur in
   MIDI-on-the-wire.  I I<think> that realtime data insertion (i.e., the
   ability to have E<lt>realtime byteE<gt>s popping up in the I<middle>
   of messages) is something that can't happen in MIDI files.

This is correct.  More to the point, the file spec and the wire spec
are just different.  Even if they do happen to share the channel
message [8n-En] status codes, all bets are off for the Fn codes:

  FF on the wire is "System Reset" (which is pretty much never 
     supposed to be sent by anything but a user pushing a RESET 
     button somewhere)
  FF in a file is the meta event prefix

  F7 on the wire terminates a sysex (F0), 
  F7 in a file can either 
     terminate a sysex (F0) OR
     introduce a sysex-without-initial-F0 event (for sending arbitary data)

and, as noted above, F1-F6,F8-FE aren't really defined for files.

So you should be able to rip out the cases to handle song-position,
tune-request, MIDI time code, etc., with impunity.  Yes, code to
generate/receive wire messages has to be able to deal with these,
but that code's going to be somewhat different anyway from what 
you have in Event::encode/decode (no delta ticks), or, at least, 
it's turning out that way in my own sequencer [work in progress]

- --
Roger Crew   <[email protected]>,<[email protected]>
------- End of forwarded message -------