Re: Only MIDI type 0?

[email protected] ("Sean M. Burke") Thu, 25 Jan 2001 23:13:54 -0700
Newsgroups perl.midi
Message-ID <[email protected]>
At 03:12 PM 2001-01-25 -0500, Mike Shapiro wrote:
>>At 05:56 PM 2001-01-24 -0500, Mike Shapiro wrote:
>> >MIDI-PERL looks great, but the limitation of reading in only type
>> >0 (single track) MIDI files [...]
>>
>>...is happily nonexistent.
>Does this mean that you can't use the MIDI::Score interface 
>when dealing with Type ! MIDI files, or that you simply need to use the 
>Opus interface to read in the file, then convert it somehow into a score 
>object first?

Ah, now scores are a different thing altogether.
I provide for the notion of scores simply as an alternate representation of
a group of events.
And it's my intention that one could convert a single track's events from
an event structure to a score structure AND BACK without losing any
information.
However, a multi-track type-1 file isn't just a list of events -- it's
several lists of events, as many lists as there are tracks.

The thing I don't know is if anything would break if you just took each
track in your type-1 file, made a score from each, then made a score
containing ALL  the events from all those scores, turned that into one
event list, and then made a single track file from that.
I suppose a significant subquestion is:  is, say, channel 2 on one track
independent of channel 2 on another track?  If so, then smashing events
from the two tracks together will lose the distinction between the two
different track 2s.

There's a way around this, which is a bit of a hack:  you know and I know
that channels go only from 0 to 15 inclusive.  But the score<->events
routines don't enforce that.
So you have events in track 0 that use (in theory, potentially) all 15
tracks.  Fine.  And you have events in track 1 that use (in theory,
potentially) all 15 tracks.  Map their channel numbers to 16-31.  And so on
for each track: increment its channel numbers by 16*$tracknum.  Then you
can smoosh all the notes into one great big event structure, and shuffle or
alter them as you like, because in the end you can sort them back into the
right track based on each note's int($tracknum / 16).
(As to what to do with score-items that don't have target channels, I
dunno.  You're on your own.)


Now, in theory, you could do the $channum += 16 * $tracknum either to the
channums in each event structure, or in each score structure.  However, if
you do it to each event structure, AND if you have more than 15 tracks,
then mayhem, will ensue, since MIDI::Score::events_r_to_score_r does happen
to ASSUME that channums can't be over 255.  If you look, you'll see that
the function has to keep, as it's scanning the event list, a list of what
notes are playing at a given instant: on what channel, at what note-number.
 It logs this in a hash, with the key from:
  pack 'CC', $channum, $notenum
...but because of how the 'C' format behaves, that might as well be:
  pack 'CC', 255 & $channum, 255 & $notenum
i.e., it breaks for $channums > 255.
Now, you could change the 'CC' formats to 'NC' or something.
But the simple way is to just convert each track's events to a score, THEN
tweak the channums on each score, THEN smoosh it all into one big score,
run wild with it, then resegregate events back to a score for each track,
and turn each track-score back into a real event list for each track, and
insert that into the track object, and write it back as you like.

Mind you, I've not actually tried any of this.


Maybe the inability to disinguish channel-less events is a problem.

An alternate approach is store the source-track information not as part of
the channel number of each score-event, but instead by just pushing it as a
new list-item on each event (whether in a score or an event structure).
That should be harmless, I think.  The routine &MIDI::Event::encode would
be the one routine that would care about how many items are in the list
comprising each event.  And it seems to just do things like:
      if($event eq 'note_off'){
        $status = 0x80 | (int($E[0]) & 0x0F);
        $parameters = pack('C2',
                            int($E[1]) & 0x7F, int($E[2]) & 0x7F);
I.e., it asks for elements 0, 1, and 2, and it asks for them by (positive)
index.  But if you've decided to use a extra element there, or elsewhere,
for whatever purpose you like (like a number signalling what track it
started out from), then that's A-OK (and if it weren't, then you could
simply make sure to have removed it by encoding-time anyway).
What index-number I mean by "extra" would vary from event-type to
event-type, but if you just push(@$each_event, $tracknum), and then later
$tracknum = pop(@$each_event), then you should be fine.
(Or alternately $tracknum = $each_event->[-1] if you weren't bothering to
remove those things.)


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