Thanks for your input. There are so many questions, I'm resending the
discussion and adding more to it below...
Evan Laforge wrote:
[ re-adding media_api, I assume you meant to reply-all? ]
On Tue, Sep 2, 2008 at 2:23 PM, Roger Dannenberg <[EMAIL PROTECTED]> wrote:
Evan,
Merging MIDI streams with timestamps is a difficult problem, at least from
an API point of view. As far as I know, Windows MIDI drivers and the MME API
do not support merging, and since a goal of PortMidi is to run on multiple
platforms, there is no built-in merging capability. One thing you can do is
Well, it's pretty easy on platforms that support merging, though I
certainly appreciate the desire to be cross platform (well, support
windows mme really). Actually, I heard some noise about vista wanting
to be more reasonable about audio stuff so maybe they have a driver
now that does that. Not that many people use vista...
It seems like kind of a shame though to have to write a whole separate
thread in C and figure out system specific priority and timing stuff
when this is exactly what is already provided by the driver...
especially if it's just to support windows mme, which I have no plans
of ever supporting anyway. I notice my windows sequencer (cubase)
uses DirectMusic and seems to consider mme legacy anyway (dunno if
DirectMusic provides merging). I guess if someone really cared they
would have written a DirectMusic backend for portmidi though.
I don't think DirectMusic provides any advantage over MME, but I'd be
happy to know if I'm wrong. There haven't been any requests for a
DirectMusic version of PortMidi that I know of.
There's an example, pm_test/midithru.c, that uses porttime to set up a
high-priority thread and provide MIDI THRU along with communications to
a non-real-time thread. This was written when I took out some support
for MIDI THRU that turned out to have some synchronization problems.
After thinking about this a bit more, I'm surprised that a merging
Pm_Write() would actually help you. With PortMidi, you need to actively
request/poll for incoming MIDI, so to provide a MIDI thru capability,
you need to quickly read any incoming message and send it back out. That
implies that you have a high-priority thread running to poll for MIDI,
but if you have that, you probably don't need timestamped messages and
merging.
It was my impression that every sequencer uses a user-level thread to do
MIDI scheduling and merging. In particular, most sequencers have some
options on THRU, e.g. filter certain channels, and unless everything the
sequencer wants to offer is supported by some underlying THRU API, the
application has to do the work.
I guess you would consider it beyond the portmidi mandate to use the
OS's scheduler for the core midi, alsa, and maybe vista-whatever, and
fall back to creating a high prio thread to schedule msgs in windows
mme? This is after all the system specific hackery that portmidi is
supposed to be saving me from.
You make a good point. The counter-argument is that a portmidi user
should be able to get the same quality of accurate timing as someone
going directly to the OS-specific API. If we "extended" the Win MME API
by putting the scheduling in user-land, this would make it impossible to
get timing at the driver level. (Or at least there would be a confusing
API offering two implementations.)
In short, timed messages are good when sent by a process that is doing
other things and does not have good timing accuracy, but if you are doing
anything interactive, you are probably better off just running a high
priority thread to do MIDI processing with "zero" latency and no timestamps.
Well, no process is going to have good timing accuracy on a
multitasking system. The only thing that will is a high-prio
scheduler thread, whether that be implemented by the application, as a
small loop in a separate thread, or by the OS driver, probably also as
a small loop in a separate thread. So it seems to me that *all* apps
fall into the "doing other things" camp, unless it really is just a
midi file player in which case the whole app *is* the high-prio
scheduler thread.
[Sorry, I was using "process" in a more generic sense. In this context,
I should have said "timed messages are good when sent by a thread that
is doing other things and does not have good timing accuracy...".]
I would not be violently opposed to adding a Pm_WriteThru() call that
sends messages immediately, in front of pending timestamped messages, but
this would not work for Windows unless we moved all the timestamp processing
into the application space, and I don't think that is a good idea. (Correct
me if I'm wrong about Windows -- I can't say that I've tried this, so maybe
I'm not interpreting the spec correctly.)
Wait, weren't you just advocating moving all the timestamp processing
into application space for all platforms (well, except non-interactive
"freeze while playing music" type ones I guess)? Wouldn't it be
strictly better to do the timestamp processing in the app for the
single relatively primitive driver that doesn't support it, and in the
driver for the rest of the drivers that can handle it?
I think the choices are: (1) retain the potential for driver-based
timing in Windows and possibly add a non-portable call like Pm_WriteThru
for non-windows users, or (2) than take away the best-possible timing
for Windows and make fully cross-platform API that supports merging
(even though most applications that need merging will probably do their
own merging anyway)
I stated these options in a biased way to indicate the reasons why (1)
seems preferable to me. I'm not enthusiastic about adding a
non-portable WriteThru call, but it seems pretty harmless and better
than encouraging you to maintain a separate version.
Anyway, I'll probably continue with the patch I have for a while
(portmidi is not exactly actively developing so it won't get out of
date), and then maybe send a patch for Pm_WriteThru once I have some
more experience with this. I still like the way I have it because it
requires no special support from the app, but a separate function
won't be a big burden either.
Here's a more minor issue:
Is there a particular reason Pm_Abort specifies that the ports will
have to be all reopened after use? This makes it awkward for me to
use. The CoreMIDI equivalent MIDIFlushOutput doesn't have that
requirement, and it looks like nor does alsa_abort, though it's not
even implemented in portmidi. It's actually silently not implemented
for CoreMIDI either, though doing so would be trivial. From looking
at the windows docs, it looks like midiStreamStop doesn't reset any
connections either. So how about if I implement abort for CoreMIDI,
and remove the stuff about "The caller should immediately close the
output port"? If I make it send EOX if a sysex is in progress, I can
probably remove the scary warning about partial midi messages too.
Apparently no one is using Pm_Abort anyway, except maybe on windows.
And hey, if abort has been not implemented on the majority of the
platforms for all this time, what's wrong with midi merge being not
implemented on one platform?
Well, abort should be implemented everywhere. I wasn't aware that it
wasn't, but it's not the most useful function since it risks leaving
note-on's, sending corrupt sysex messages, etc. I think it's only useful
if the latency is very high and you don't want to wait for pending
messages to be sent but instead need to shut down right away. I'm not
sure why you would want to leave the midi connection open, since after
Pm_Abort, messages may have been dropped, and you can't always know the
state of the MIDI receiver.
To answer your question, I think Pm_Abort() could be made to just drop
all pending messages but leave the stream in a usable state. The
implementations may assume the stream will be closed, so I'd have to
check very carefully that, e.g., the Windows stream buffers that get
returned after Pm_Abort() are retained and prepared for reuse, etc.
I'm curious how you would use this. E.g. Windows documentation says
after midiStopStream() that all on notes are turned off (so maybe they
send all-note-off commands on all channels). Would you expect all
implementations to do the same kind of clean-up, and what should they do?
_______________________________________________
media_api mailing list
media_api@create.ucsb.edu
http://lists.create.ucsb.edu/mailman/listinfo/media_api