MIDI::Event callbacks; MIDI::Opus methods
[email protected] ("Sean M. Burke") Wed, 25 Oct 2000 20:33:22 -0600
| Newsgroups | perl.midi |
|---|---|
| Message-ID | <[email protected]> |
At 09:47 AM 2000-10-25 -0400, you wrote:
>[...]
>Your docs say "So if you want to alter the event stream on the way to the
>event structure (which counts as deep voodoo), define 'event_callback' and
>have it modify its @_. " But that doesn't actually, work, I don't think?
>MIDI/Event.pm just calls $event_callback() passing in @E (by value, not by
>reference), and it ignores any return value. So the event callback can't
>change the event. That's sort of too bad!
I think that when I wrote that, I was under the impression (which I swear I
arrived at by actual experimentation, but it must have been ages ago and
with a buggy Perl 4 or something) that if you called a sub as:
foo(@array);
Then foo's @_ would actually be an alias to @array (instead of the normal
case of merely having @_'s elements be aliases to the elements of the
parameter list).
In other words, I thought one say @_ = (...new things...) and thereby
change @array.
But that's not the way it is. @_'s elements are aliased to the elements of
the parameter list, but that's it.
So my callback mechanism isn't very expressive.
Currently, I have:
if(@E){
if( $exclusive_event_callback ) {
&{ $exclusive_event_callback }( @E );
} else {
&{ $event_callback }( @E ) if $event_callback;
push(@events, [ @E ]);
}
}
>Might I suggest something like:
>
> if(@E){
> if( $exclusive_event_callback ) {
> &{ $exclusive_event_callback }( @E );
> } elsif ($event_callback) {
> my $replacement = &{ $event_callback }( @E );
> @E = @$replacement if isref $replacement;
> push(@events, [ @E ]);
> }
> }
>
>(and the corresponding change to the docs), which would
>allow an event_callback to return a reference to a
>replacement event-list, without (I think!) breaking any
>uses of the current readonly callback? (Note that
>I just typed in the above, and didn't test it at all.)
Well, if I had it to do over, I'd probably have:
if(@E){
if( $exclusive_event_callback ) {
&{ $exclusive_event_callback }( @E );
} else {
if($event_callback) {
push(@events, &{ $event_callback }( @E );
} else {
push(@events, \@E);
}
}
}
But if I did that now, that would break any existing code that uses the
'event_callback' option to, say, merely alter the channels of events (by
altering $_[2] -- well, for those events that have channels in their
parameters). But it /would/ allow the callback to turn @E into no events,
one event, or any number of events.
But I think I'll just err toward breaking nothing by:
1) declaring the current behavior a feature (if a crippled one),
2) just adding a new kind of callback, event_callback_deluxe, that can
do damned near anything:
if(@E){
if( $exclusive_event_callback ) {
&{ $exclusive_event_callback }( @E );
} elsif($event_callback_deluxe) {
push(@events, &{ $event_callback_deluxe }(\@events, $eot, \@E);
# so that an abort-track-parse can be signalled
# by setting $_[1] = 1;
} else {
&{ $event_callback }( @E );
push(@events, \@E) if @E;
# so that event_callback can, at most, kill this event.
}
}
>Also, I'd like to have a method of MIDI::Opus that returned the data for
>the MIDIfile (rather than writing it to a handle or a file). This would be
>useful when you need the data (and/or its length) for something else, like
>embedding into a RIFF/rmi file or returning from a CGI or whatever. Should
>be simple to write, and then write_to_handle could just use it (I'd be glad
>to draft the mods and send them to you if you'd like).
1) in principle, a good idea.
2) having write_to_handle use it is a bad idea -- it'd mean /always having/
to create an scalar in memory the size of the whole MIDI file, and then
dumping it. Currently, one creates only a scalar as big as each track chunk.
However, I think there's no harm in making a write_to_scalar that takes:
$opus->write_to_scalar(SCALARREF, { ...options...} )
but just not having any of the other methods use it. It'd basically be
just the current write_to_handle, but with
$$scalar_r .= join '', ...
replacing
print $fh ...
So I think I'll do that. That's easy.
3) If ever you need a module to do something like this (i.e., do to a
string what it expects to do only to a filehandle), then I think there's a
class -- IO::Stringy? -- that does that -- you give it a string, and it
makes an object that manipulates that string as if it were a file,
presenting a filehandle's usual interface methods; then you pass that
around to whatever class that expects a FH. I've never used it myself, but
I suggest looking at it. (Or IO::Stringy might present a string interface
to a FH, in which case what I'm thinking of is another module entirely,
hopefully not one confined to my imagination.)
4) For a CGI, I think what you're dancing around is this:
print "Content-type: audio/x-midi\n\n"; # or whatever
$opus->write_to_handle(*STDOUT);
5) I'm now also considering how a read_from_string might be nice to have.
I'll throw that in, too.
I won't go making the above changes immediately. I figure I'll give the
MIDI-Perl list a week or two to think about this, and make any comments.
That'll also give me a little time to puzzle over some optimizations I've
got in mind now for the encode/decode loops in Events; to say nothing of
finishing my next TPJ article, proofing a friend's upcoming Perl book, and
finally fixing Locale::Maketext, which has been wilting from neglect.
By the way, I bet the TPJ editors would like it if someone wrote a piece or
two about using MIDI-Perl to do, hell, anything. My TPJ13 article on
MIDI::Simple is hardly the last word possible on the subject of music and
Perl.
--
Sean M. Burke [email protected] http://www.spinn.net/~sburke/