I think we're actually talking about *three* kinds
of controls, while
everyone is still talking about their own set of two kinds of
controls. What I'm thinking is:
Master Controls:
Channel/Part Controls:
Voice/Note Controls
So if we support multi-timbral (multi-part, multi-channel - too many words)
instruments, then yes, this is true.
Aside: I propose the following for naming stuff:
* Plugin: a chunk of code, loaded or not, that implements this API (a .so file
or a running instance).
* Instrument: an instance of a plugin that supports the instrument API bits
(analogous to a chunk of hardware from Roland).
* Channel: a 'part', 'channel' or 'instrument' from MIDI.
Instruments have
1 or more Channels.
* Control: a knob, button, slider, or virtual control withing a Channel or
Instrument. Controls can be master (e.g. master volume), per-Channel (e.g.
channel pressure) or per-Voice (e.g. aftertouch).
* Preset: a stored or loaded set of values for all Controls in a Channel
* Voice: a playing sound within a Channel. Channels may have multiple voices.
* Port: an input/output connection on a Plugin. Audio data goes over ports.
I don't think this has much to do with the API -
and I don't see the
possibility of a clash. If you take any two controls, they may have
any relation whatsoever, *defined by the synth*, and the host should
I agree with that IF we say you have to have a separate control struct for
per-voice and per-part controls. If we define a control and say it has a
flag which identifies it as MASTER, PART, VOICE, then we do need to suggest
a relationship. I like defining a control once, but it's becoming more
obvious to NOT do that. They range of valid values may be wildly different.
Do we want to provide a suggested range of values for MIDI compatible
controls. For example Controls that are labelled CONTROL_AFTERTOUCH should
expect values between 0 and 1.0, and the host can map MIDI onto that? Or 0
and 127, like MIDI? or 0 and MAX_INT and let the synth blow up when you
pass a VELOCITY of 10000, and it expects 127?
You're thinking entirely in keyboardist terms
here. :-) You're
forgetting that some instruments really don't have anything like
attack and release in real life, but rather "bow speed", "air speed"
and that kind of stuff.
umm, speed == velocity, no? But I get your point, and I concede this point.
Velocity is an event.
I think so, but I'm not 100% determined.
(Audiality is still mostly a
monolith, and I'm not completely sure how to best split it up into
real plugins.)
plugins from being multipart doesn't make much
sense. Just reserve
one control/event port for the whole instrument/plugin, and then
allow the instrument/plugin to have as many other control/event ports
as it likes - just like audio in and out ports.
I had been assuming a single event-queue per instance. Two questions: why
would a plugin (assuming not multi-timbral for now) have more than one
event-queue? Assuming we do support multi-timbral synths, is there an
advantage to having events per-channel event-queues? Do you envision each
Channel getting the ->run() method seperately, or does ->run() loop? If it
is looping anyway, is there an advantage to multiple event-queues?
> > ports. As of now, there is one event port
for each Channel, and
> > one event port for each physical Voice. "Virtual Voices" (ie
> > Voices as
I know you've done some exploring - do we really want one event-queue per
voice + one for each channel + one for master? or is one per instance
simpler?
As to the "gets a voice ID" thing, no, I
think this is a bad
idea. The host should *pick* a voice ID from a previously
allocated pool of Virtual Voice IDs. That way, you eliminate the
host-plugin or plugin-plugin (this is where it starts getting
interesting) roundtrip, which means you can, amond other things:
I'm still not clear on this. What plugin would trigger another
plugin?
Any plugin that processes events rather than (or in addition to)
audio. They're usually called "MIDI plugins".
I can see controller plugins (It's another extension to this API, perhaps)
which deal with sending events to other plugins. And I guess an arppegiator
plugin could be a controller..
If so, how do
you reconcile that they
will each have a pool of VVIDs - I suppose they can get VVIDs from
the host, but we're adding a fair bit of complexity now.
Where? They have to get the VVIDs from somewhere anyway. It's
probably not a great idea to just assume that plugins can accept any
integer number as a valid VVID, since that complicates/slows down
voice lookup.
you don't send just ANY integer, you get the integer to id a voice FROM the
plug. But if VOICE_ON is an event, it gets much uglier. Do let me see if I
get this right:
Host allocates the VVID pool (perhaps a bitmask or some other way of
indicating usage)
Host wants to send a VOICE_ON:
allocates an unused VVID
sends VOICE_ON with that VVID
when it sends a VOICE_OFF or receives a VOICE_OFF it can mark that
VVID as unused again.
Plugin wants to send a VOICE_ON:
calls host->get_vvids(32)
host allocates it 32 VVIDs
sends VOICE_ON with any of those, and must manage them itself
(alternatively, management is simpler if it just allocates one VVID
at a time and frees it when it is done)
Or does the HOST ask the INSTRUMENT for VVIDs? If so, why? Explain? :)
I'd much rather require that events sent to a port
are sent in
timestamp order, so the host can just merge sorted event lists. In
cases where you have multiple connections to the same event port, you
just use "shadow ports", so the host gets a number of ordered event
lists that it merges into one just before running the plugin that
owns the real input port.
I agree - this is the host's job, not the plugin's
host has to
handle syncronization issues (one recv event-queue per
thread, or it has to be plugin -> host callback, or something), but
that's OK.
Well, if your threads are not in sample sync, you're in trouble...
I meant threading sync issues. Host can't just pass one event-queue to all
plugins in a multi-thread environment. But again, that is the HOSTs
problem. So the host can pass the address of it's event-queue during
activate(), or the plugin can make a host->send_event() callback.