Re: CoreMIDI question
Re: CoreMIDI question
- Subject: Re: CoreMIDI question
- From: Kurt Revis <email@hidden>
- Date: Sun, 28 Apr 2002 05:17:28 -0700
On Sunday, April 28, 2002, at 12:05 AM, robert <robert@apple-
audio.allyourbass.org> wrote:
However, the docs on MIDIPacketListAdd() tell me that this function can
be
given a timestamp at (as far as I can understand) which an every
(sending a
MIDI message) has to occur. The docs are not very clear about this,
though.
I'm afraid I can't quite parse that sentence, but here's an overview
anyway.
Each MIDIPacket structure contains some MIDI data, as well as a
timestamp. When you receive MIDI data, the data you get has a timestamp
indicating when the device received it. When you send MIDI data, you
supply the timestamp. If the timestamp is 0, or in the past, the data
will be delivered immediately. If it is in the future, CoreMIDI will
queue up the data and attempt to get it sent out the device as close to
the timestamp as it can. (Obviously the drivers have to cooperate in
doing this.)
Incidentally, this only works for data sent through MIDISend(). If you
are using MIDIReceived() to indicate that data is coming from a virtual
MIDI source, then CoreMIDI will not do any scheduling for you -- the
timestamps are passed through to destination programs unchanged. This
is kind of unfortunate, especially in your case:
I'm toying with the idea of a small application consisting of a BPM
counter
which drives a virtual MIDI source sending MIDI Clock messages (the 0xF8
message in the MIDI protocol).
If I were you, I would change your program around so that it uses
MIDISend() to send to endpoints, instead of being a virtual source. This
may require a rethink on your part, since it means that your users will
have to use your app to choose where to send the MIDI clock, instead of
letting them select it as a source in other applications. It will be
quite a bit easier to get working this way, though, since you can let
CoreMIDI worry about the hardest part of the scheduling. You just need
to give it events some reasonable amount of time in advance of when they
should be sent.
(This will also let you send MIDI clock to external MIDI devices, which
you couldn't do directly with a virtual source.)
If you really really want to use a virtual source, I imagine it can be
done. But getting the easier case working will teach you most of what
you need to know, so you might as well tackle that first.
Here's what to do, roughly: Your app should have a thread which wakes
up periodically, figures out what time it is, creates some MIDI clock
messages, and sends them. Each message should be timestamped with some
time in the future. It's up to you to decide how far into the future
you want to go, and how many clocks you want to generate at a time
(maybe just one). (If you can batch up the clock ticks, your thread
will need to wake up less frequently, which is easier to accomplish
reliably... but response will be worse when the clock starts or stops or
the tempo changes.)
Now you just need your thread to wake up with a somewhat consistent
period (it does not need to be perfect). There are an absurd number of
ways to do this. You suggested select() and setitimer(), both of which
are a bit too high level to be convenient. pthread_cond_timedwait() is
in the same category. There is also CFRunLoopTimer, and in Cocoa you
have NSTimer and -[NSThread sleepUntilDate:]. I don't recall the exact
results, but I don't remember the latter two methods being very accurate.
Instead, I suggest one of the following:
* usleep(). Ignore the warnings about setitimer() in the man page,
since they are completely wrong--Darwin doesn't actually use that method.
* nanosleep(). This is in the current Mac OS X, but there is no man
page or function declaration. However, you can declare it yourself, and
your program will link fine. Supposedly this will be fixed in the future.
* mach_wait_until(). Again, no man page, but at least it's declared.
This lets your thread sleep until a given time (it takes a Mach
timestamp which is exactly the same as a CoreAudio host time timestamp).
To get more details for all of these, I recommend Google, searching the
mailing list archives (this list, Darwin-development, and
Mac-Games-Dev), reading any comments in the headers, and looking at the
Darwin source code (check out Libc and xnu). I also just posted the URL
to some of my own code which uses usleep() or nanosleep() to wake up
periodically.
Note that you will pretty much never wake up exactly when you want--the
Mac OS X scheduler is just not that exact (it's not a real-time OS). So
you need to be prepared for a fair amount of variance. For ordinary
threads, the variance can in fact be quite high, especially if the
system is busy with other, more high-priority threads. (The worst thing
seems to be when the user is dragging a window around.) If you want less
variance, you should look into making your thread use the
time-constraint scheduling policy, using thread_policy_set(). Again,
this has been discussed a fair amount on the lists, and I expect more
info about it to be communicated by Apple as time goes on.
There's my brain dump--I hope it helps and was not overwhelming. As
always, any corrections from everyone else are welcome.
--
Kurt Revis
email@hidden
_______________________________________________
coreaudio-api mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/coreaudio-api
Do not post admin requests to the list. They will be ignored.