Re: [linux-audio-dev] Re: Some Event stuff for now...

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

Subject: Re: [linux-audio-dev] Re: Some Event stuff for now...
From: David Olofson (audiality_AT_swipnet.se)
Date: su syys   26 1999 - 12:56:41 EDT


Back again. It's coming together, I think...

On Sat, 25 Sep 1999, David Olofson wrote:
> > Do you need an additional buffer to queue up timestamped events ?
>
> Well, kind of, just as you need to queue audio buffers to handle the
> connections between sub nets with different cycle rates. So, if the sequencer
> really has to run in a different sub net that is out-of-phase, or running at a
> different cycle rate, the event timing code will have to take care of that,
> just as it will take care of translating time stamps into local buffer frame
> counts.

...and I have two changes that will make that a lot nicer, while also making
some event related operations more efficient. :-)

1) Time stamps should be ripped out of the events.
2) Dynamic memory allocation is handled by small heap buffers,
   moved from a global heap buffer list to the local context
   heap as needed.

---------------------------------
struct event_descriptor_t;

struct event_port_t {
        qm_heap_t *heap; /* For dynamic allocation */
        int events; /* Number of events
                                                 * currently in the buffer
                                                 */
        int maxevents; /* Size of the buffer */
        event_descriptor_t **buffer;
};

struct event_t {
        event_code_t code; /* What is this? */
        event_port_t **from; /* Who is this from? */
        int size; /* Number of data bytes */
};

struct event_descriptor_t {
        event_time_t time;
        event_t *event;
};
---------------------------------

qm_heap_t will look something like
---------------------------------
/*
 * Every context should have one of these for memory allocation.
 * All memory blocks that have been allocated from one heap will
 * be flushed when the context the heap belongs to reaches the
 * of a cycle. (That is, when all data is "old" WRT this context.)
 */
struct qm_heap_t {
        int size;
        int end; /* Where to grab a new block */
        void *buffer;
};

/*
 * Aaargh! A function call! ;-)
 * It's only used when the allocation macros
 * run out of local heap space.
 */
extern int qm_new_buffer(qm_heap_t *heap);
---------------------------------

Memory allocation works something like this:

inline void *qm_malloc(qm_heap_t *heap, int size)
{
        void *ret;
        if(heap->size - heap->end < size)
                if(!qm_new_buffer(heap))
                        return 0; /* A *REAL* OOM! --> */
        ret = heap->buffer + heap->end;
        heap->end += size;
        return ret;
};

(Note that qm_heap_t has some private stuff hidden from the API. That's what
qm_new_buffer() uses to keep track of things when handling the heap buffers.)

qm_new_buffer() will send the current buffer to the flush list of the context
the qm_heap belongs to, and will then ask the global memory manager for a new
buffer to throw in. When it's "flush time" for the context, all buffers are
returned to the global heap. (Should end up on top for free cache
optimization! :-)

> > > Ok, you can advance the heap pointer for non-timestamped data, but
> > for timed events you have to take an other buffer flushing mechanis.
>
> ...or, when you allocate memory for the event, make sure to pick a heap thhat
> will not have been flushed before the event is to be processed. (That doesn't
> mean that it has to be the destination sub net's heap...)

This will be handled by simply requireing that all event ports you can send to
(and thus recieve events from) belong to a *context* that's compatible with the
sub net you're in. That is, they do not necessarilly have the same cycle time,
but sending events to them will be handled correctly, and data will not be
flushed prematurely.

> [...]
> > therefore in the case that the event-receiver wants to query data about the
> > event-sender, there will be a function which will return PORT_NOT_PRESENT (=
> > plugin exited or so),
>
> Design goal: No function calls from plug-ins in the event system! :-)

...and it seems that I can get very close without presenting 70% of the
engine's implementation details through the API. The qm_new_buffer() call is
only performed when your event doesn't fit in the heap's current buffer.

BTW, is there much point in optimizing for mixed allocation sizes? That is,
should the allocation macro check the old heap buffer first, in case it was
replaced just because someone just allocated a huge block of memory? That would
mean a few instructions and one conditional jump (== frequent pipeline
flushes) to get a little more efficient memory management when you send a mix
of large and small events...

About the inter-sub-net interfacing
-----------------------------------
Implementation detail:
Sub nets could be executed by a built-in plug-in. That would require plug-ins
to be able to have multiple event ports in order to avoid one event port having
to use some kind of gateway protocol. Which starts to sound like the plug-in
API should be more similar to the client API or something... Not sure if this
is a good idea (with plug-in performance in mind). But OTOH, event ports only
have a cost when set up and when actually used, so I may not matter much. Will
hack a sub net scheduler plug-in model to see what it looks like...

Anyway, passing events from one sub net to another means setting up an extra
event port belonging to "this" sub net, to shadow the real destination on the
other sub net. The context the shadow port belongs to will be one that's under
control of the code that does the actual event passing/translation. (In order
to make sure that buffers aren't flushed before the events have been recieved.)

Event translation can now be done without even referencing the events
themselves, as the time stamps are moved up to the event pointer table level.

As a bonus, you can send events containing static data (just add events using
your own static data instead of allocating dynamically), send the same event
multiple times, send the same event to multiple recipients, and other cool
things - with no copying overhead for the event data.

//David

 ·A·U·D·I·A·L·I·T·Y· P r o f e s s i o n a l L i n u x A u d i o
- - ------------------------------------------------------------- - -
    ·Rock Solid David Olofson:
    ·Low Latency www.angelfire.com/or/audiality ·Audio Hacker
    ·Plug-Ins audiality_AT_swipnet.se ·Linux Advocate
    ·Open Source ·Singer/Composer


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

This archive was generated by hypermail 2b28 : pe maalis 10 2000 - 07:27:12 EST