[gmpi] Re: Topic 6: Time representation

  • From: David Olofson <david@xxxxxxxxxxx>
  • To: gmpi@xxxxxxxxxxxxx
  • Date: Thu, 1 May 2003 23:14:40 +0200

On Thursday 01 May 2003 02.13, Chris Grigg wrote:
[...]
> >>  Well, that's kind of what a sequencer track is anyway, yeah?
> >
> >Yeah, but the event/control output of a plugin is not a sequencer
> >track, is it? (Well, it *could* be, but then we're not talking
> > about a real time system.)
>
> Interesting point.  I don't know where the idea that it's only OK
> for GMPI to be a real-time system came from.  We have input and
> output queues of timed events, after all.

If it's realistically possible to do, I don't think anyone would mind 
if GMPI does both, but it makes quite a difference if you use 
priority queues with random access operations, or plain LIFO queues. 
The latter are basically a structured alternative to audio rate 
control streams, while the former have lots of implications.


[...]
> >>  >  A side effect of that is that plugins must
> >>  >(obviously) send output events through the host, rather than
> >>  > directly to receivers.
> >>
> >>  I'd assumed it was pretty much going to work that way anyway.
> >>  Hosts would manage event stream routing.  Was there a different
> >>  proposal?
> >
> >Well, it's probably easier to leave it to the host, but it's not
> > the only way to do it. For XAP, we designed a system where every
> > control output of a plugin has a "target specification", in the
> > form of an event queue pointer and a cookie. These are created by
> > the target plugins upon connection of controls, which means that
> > a plugin gets to decide where the events for each input go, as
> > well as how to generate the cookies. (Object LUT index,
> > multidimensional indices or whatever - pick what does the job
> > fast and easy.)
>
> Cool.  But the host still has to stitch the pins together and
> manage the queues, yeh?

The host has to figure out what to connect where - or rather, let the 
user decide, one way or another. Next, the host asks the plugin that 
owns the input for a target spec. Finally, the host tells the source 
plugin to connect the desired output, passing that target spec as an 
argument.

The queues, however, are very simple linked lists, and need no real 
management. Events are allocated from, and returned to, a host 
managed pool. Each event queue operation is done with a few 
instructions of inline code. The only time a plugin calls the host is 
if/when the event pool is exhausted.

The only time a XAP host would touch events on the way between plugins 
is when events from two or more contexts (*) have to be 
sorted/merged, to guarantee that events arrive in timestamp order.

(*) Each plugin is at least one context. Each inner loop that
    sends or receives events is one context. Every control has
    a plugin local context id, so hosts know which controls can
    safely be routed directly to the same target without, and
    which need sorting.


[...]
> >Why this obsession with time slices? (We all do agree that we
> > should use timestamped events, right?) You can have one curve
> > segment per sample if you like. It doesn't have to be restricted
> > to block boundaries in any way.
>
> Just thinking about making it easy for novice plug developers...
> since ::process() by definition is about one timeslice at a time,
> and in a typical timeslice you'll be ramping parameters all the way
> across the timeslice, not ending in the middle or doing complex
> shapes.  So thinking about making that a default behavior.

I don't see why this makes anything easier - unless the idea is that 
your average plugin author shouldn't care about implementing sample 
accurate timing. Then what's the point in using timestamped events at 
all...?

As long as you're dealing with DSP algorithms that process one sample 
frame at a time in the inner loop, it's very easy to implement sample 
accurate event processing. Adding linear control ramping to that is 
trivial.

Adding quadratic or cubic curves isn't much harder, and it can be done 
in a way that allows the same receiver code to accept non-ramped 
input as well as linear, quadratic and cubic "ramping". We're still 
talking about code that's simpler and faster than any serious zipper 
noise elimination filter - which is something you'll still need in 
many cases, even with cubic curves.

However, as soon as you start specifying multiple choices on the API 
level (such as "a plugin MAY support cubic curve input"), it's no 
longer possible to implement it this way. You have to call the host, 
or some helper library to get the control data translated into 
something you can use. In that case, I can only think of two 
alternatives:

        1) Plugins ask for buffers of audio rate control data.

        2) Plugins ask for control values for specific points
           in time.

The first one seems rather pointless and inneffective to me. Why not 
just use audio rate control streams throughout, except possibly for 
controls that cannot be ramped at all?

Alternative 2) seems even more pointless. If you're only going to ask 
for a value every N samples or so, why not just send linear ramp 
events with sufficient density?


Considering that *anything* that isn't audio rate control data is just 
an approximation that may still need further anti-zipper filtering, 
why bother? What problem is it intended to solve, really?

IMHO, support for audio rate control data, as an optional alternative 
to events with linear ramps, would be simpler, more useful and more 
efficient than supporting non-linear ramps through helper functions.


[...]
> >Well, there is one problem: What to do if a plugin doesn't support
> > the shapes that some other plugin, or the host, generates? Do all
> > plugins with control outputs have to support multiple output
> > formats, or is it up to the host to insert some form of converter
> > elements as needed?
>
> The enums would be defined in the headers for a particular GMPI
> version, so if the host & plugs are the same GMPI version, there's
> no mismatch on the enum values.  I thought that interpolation for a
> given shape could be provided as a host function, so in process()
> you could call something like myVal = gmpiHost::getInterpValue(
> startVal, endVal, samplesInTimeslice, curve, sampIndex).  Something
> like this will be eventually be needed if we ever want to support
> nonlinear curves.

Well, I'm just not convinced that there is any reason to go beyond 
linear ramps with arbitrary density, without going directly for audio 
rate control data. What's the gain?

Most effects can't ramp the *actual* controls, for performance reason. 
(Expensive filter coefficient recalculations and whatnot.) They ramp 
the coefficients intstead, in a way that resembles the intended curve 
shape. Sounds like going from linear to cubic might be a good idea, 
because the calculations - slightly more complicated - still only 
have to be done twice per curve section.

Now, for these improved approximations to be possible, the plugins 
have to *understand* the curve shape in order to approximate it, 
right? So, just asking the host to calculate a few values won't give 
you a better approximation than linear ramp events with slightly 
higher density. The host can render the *control curve* for you, but 
it can't give you the coefficients you need for your internal 
approximated curves.


> Don't understand what you mean about output
> formats, ask again?

Plugins have to be able to *generate* control data as well receive it. 
This shouldn't be a problem, as you just need to make sure that no 
plugin generates something that the host can make sense of.

However, considering the above, I'm not sure there's much point in 
generating anything that cannot be interpreted directly by the 
receiving plugin.

The big deal with timestamped events; the very reason why anyone would 
think of using them for controlling DSP code, is that they're cheaper 
than audio rate controls. Doing something that actually makes the 
shortcut more expensive than the real thing, seems... well, you get 
the idea. :-)


[...]
> >Well, our idea was to send timestamped control events to these
> > "tempo" (for tempo changes or ramps) and "position" (for loops
> > and jumps) controls. Plugins will have to receive these events,
> > and apply tempo to position. It's trivial to do on a
> > sample-by-sample basis (something like tempo += dtempo; position
> > += tempo;)...
>
> If the curve is anything but linear, this will give a wrong answer.

Yes, but then you would be feeding the plugin with garbage. If tempo 
is a control that supports linear ramping, that's all it is. You may 
send a new tempo event for each sample frame, if piecewise linear 
approximation isn't sufficient.

Meanwhile, in the context of sequencers and tempo, it seems that some 
would even consider actual linear ramping of the tempo overkill...


> >...and easy enough
> >if you just want to do the calculations once per block and when
> > you actually receive the events. Then Plugin SDK could provide
> > some inlines or macros for the latter, along with something
> > similar to the call you suggest.
>
> I see.  But unless being able to set each plug's tempo & position
> independently is a design requirement -- something I hadn't thought
> of -- why make tempo & position 'controls' of the plug?

Well, for XAP, it just happened to be the most natural way. It turned 
out to be dead simple, and it cuts down the number of specialized 
interfaces. "Everything is a control" - and it's not because we think 
it sounds cool. It's because we hate APIs that you can't use without 
having the reference docs handy, even after using it for years.


> I've been
> thinking of tempo as a sort of global parameter, a property of
> managed by the host, that you go to the host to get, and that looks
> the same to all plugs in the graph.
>
> Though maybe tempo-as-parameter isn't so well-liked.

Either way, I don't really like the idea of considering tempo as 
something special enough to be host global. I'm not remotely into 
experimental music, but I've still found myself working against the 
tempo map at times. Some things would be so much easier if you could 
just throw in another tempo map that you can adjust as needed, 
instead of moving events around.

As an example of a more desperate need for multiple timelines, 
consider a multimedia audio engine, like the ones used in games. It 
has to be able to play multiple sequences independently. If it can't, 
you can't crossfade between background songs, you can't play fanfares 
over the music, and you can't use sequences for soundscapes and sound 
effects. Such limitations would strike me as ridiculous.


//David Olofson - Programmer, Composer, Open Source Advocate

.- The Return of Audiality! --------------------------------.
| Free/Open Source Audio Engine for use in Games or Studio. |
| RT and off-line synth. Scripting. Sample accurate timing. |
`-----------------------------------> http://audiality.org -'
   --- http://olofson.net --- http://www.reologica.se ---


----------------------------------------------------------------------
Generalized Music Plugin Interface (GMPI) public discussion list
Participation in this list is contingent upon your abiding by the
following rules:  Please stay on topic.  You are responsible for your own
words.  Please respect your fellow subscribers.  Please do not
redistribute anyone else's words without their permission.

Archive: //www.freelists.org/archives/gmpi
Email gmpi-request@xxxxxxxxxxxxx w/ subject "unsubscribe" to unsubscribe

Other related posts: