Re: [linux-audio-dev] a port/buffer proposal

New Message Reply About this list Date view Thread view Subject view Author view Other groups

Subject: Re: [linux-audio-dev] a port/buffer proposal
From: Jim Peters (jim_AT_uazu.net)
Date: Tue May 22 2001 - 20:24:25 EEST


Paul Davis wrote:
> Even in the simplest signal graph, there are 3 kinds of connections
> between Ports. Here, I propose
>
> 1) One-to-One
> ::
> 2) One-to-Many
> ::
> 3) Many-to-One
> ::

All this looks good to me. This means that a plugin always writes
straight to its output buffer - always buf[i++] = val;

> Followup Comments
> ------------------
>
> * "request a memory buffer"
> ---------------------------
> This does not involve malloc(). Some other object (in my view, the
> engine) will maintain a pool of buffers that are the correct size for
> the current engine cycle.

Agreed.

> * how about the connection between i/o ports inside a Plugin?
> -------------------------------------------------------------
>
> this ... can always use zero-copy if there is no data
> modification. so, a function like:
>
> void
> port_tie (Port *dst, Port *src)
> {
> dst->buffer = src->buffer;
> }
>
> will work for Plugin's that want their outputs wired directly to their
> inputs.

Slight confusion here. Is this something that a plugin must call
every process() call, or is this something that the plugin sets up
once only ?

If it is called every process(), and the plugin has the option of not
calling it and writing to the output buffer normally, then this alters
which buffers may be freed during the processing of the graph, and
would probably necessitate a reference-counting scheme as described
for GLAME. (I can expand on the reasons why if this is what you were
thinking, but there is no point in writing more if you weren't.)

If it is called once only (i.e. it is a setup-level change, like
connections), then the engine automatically knows which buffers may be
released to the free list after the plugin has executed, and can
prepare beforehand to some extent, and optimise further.

> * when does all this stuff happen?
> ----------------------------------
> in a naive model, it happens on every engine cycle.
>
> in a more sophisticated model, the memory buffers are requested
> whenever the signal graph is modified. the rest of the stuff happens
> on every engine cycle.

The `naive' method might be necessary: I was thinking about how we
could optimise the original single-thread approach to work well on a
dual-processor machine - having two real-time threads both working
simultaneously through the graph, each taking the next available job.
This means that the precise order of execution depends on how long
individual plugins take - processor A might execute three plugins in
the same time that processor B executes just one. This means that
which buffers become free at what time varies according to execution
times. This is likely to apply if we are now moving towards a
multi-process-compatible scheme. In the original single-thread
method, everything could be decided beforehand (buffer addresses,
detailed plugin run-order, etc), but not now.

I don't think the naive method is too bad if implemented efficiently.
Basically, before the engine runs the plugin, buffers are gathered for
it (using either buffers from the free list, or existing buffers), and
once it is complete, unused input buffers are released to the free
list.

Using a FIFO free list also has the advantage that the most
recently-used buffers are also the ones re-used next, which will
automatically optimise cache usage.

> * what does it really look like?
> --------------------------------
> This is a mixture of pseudocode and real code for the naive model.
>
> struct Connection {
> Connection *next;
> Port *input;
> Port *output;
> gain_t gain;
> };

On the question of NxM matrices and so on (commented on in discussions
over the last few days), my take on this is that this model is a way
of understanding it, but not the way it is actually implemented.
There is no point in creating hundreds of connections with zero gain.
The only connections actually stored in the engine are ones with
non-zero gain. If we have a simple call:

  make_connection(char *src, char *dst, float gain);

which creates a connection between two ports (char* could be Port* if
there is some other way of identifying them than by name), then I
think this call should create a connection if it doesn't exist, and
just change the gain if it does exist, and delete the connection if
the gain is being set to 0.0. That way a huge 4096-knob GUI matrix
could be implemented in a simplistic way without impacting on the
efficiency of the engine.

  float gain= get_connection_gain(char *src, char *dst);

A call like this could return 0.0 if there is not a connection.

The reason the matrix idea is not feasible as an independent plugin,
nor as a simple `matrix multiplication' operation is that this would
cripple our ability to re-order the run-list and optimise things so
that feedback buffers are minimised. If we had a big matrix plugin,
then all the inputs to that big matrix would have to be generated
before the outputs are available. This is almost guaranteed to force
feedback buffers into a graph even when there is in fact no feedback
loop.

This is a good reason why we need to have gains on our connections
within the engine - the engine needs to be the one doing the mixing,
in order to have maximum flexibility to optimise execution.

Note that this simple model of one gain per connection still allows
something more complex like a mixer channel strip to be implemented.
In this case the GUI would adjust post-fade aux-send gain levels
automatically in proportion as the main fader is changed. The engine
provides the raw mixing functionality, whilst a GUI can arrange the
gain levels in a way more acceptable to the user.

Jim

-- 
 Jim Peters                  (_)/=\~/_(_)                        Uazú
                          (_)  /=\  ~/_  (_)
 jim@                  (_)    /=\    ~/_    (_)                  www.
 uazu.net           (_) ____ /=\ ____ ~/_ ____ (_)           uazu.net


New Message Reply About this list Date view Thread view Subject view Author view Other groups

This archive was generated by hypermail 2b28 : Tue May 22 2001 - 22:42:34 EEST