kobodl/sound/a_events.h
Ville Lindholm dbc223eb84
Initial commit
Import existing source tree; original VCS history is no longer available.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-28 16:35:31 +03:00

412 lines
9.2 KiB
C

/*(LGPL)
---------------------------------------------------------------------------
a_events.h - Internal Event System
---------------------------------------------------------------------------
* Copyright (C) 2002, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _A_EVENTS_H_
#define _A_EVENTS_H_
#define AEV_TRACKING
#include "a_globals.h"
#include "a_types.h"
/*----------------------------------------------------------
Open/Close
----------------------------------------------------------*/
int aev_open(int pool_size);
void aev_close(void);
/*----------------------------------------------------------
The global event timestamp time base
----------------------------------------------------------*/
#define AEV_TIMESTAMP_MASK 0xffff
typedef Uint16 aev_timestamp_t;
extern aev_timestamp_t aev_timer;
static inline void aev_reset_timer(void)
{
aev_timer = 0;
}
static inline void aev_advance_timer(unsigned frames)
{
aev_timer += (aev_timestamp_t)frames;
}
/*----------------------------------------------------------
Event
----------------------------------------------------------*/
#define AEV_ET_FREE 0xff
#define AEV_ET_ALLOCATED 0xfe
struct aev_port_t;
typedef struct aev_event_t
{
struct aev_event_t *next;
aev_timestamp_t frame; /* When */
Uint8 type; /* What */
Uint8 index; /* Qualifier */
Sint32 arg1; /* Argument 1 */
Sint32 arg2; /* Argument 2 */
Sint32 arg3; /* Argument 3 */
#ifdef AEV_TRACKING
const char *client; /* Client name */
struct aev_port_t *port; /* Receiver port */
#endif
} aev_event_t;
/* Macro for interpreting event timestamps. */
#define AEV_TIME(frame, offset) \
((unsigned)(frame - aev_timer - offset) & \
AEV_TIMESTAMP_MASK)
/* The global event pool. */
extern aev_event_t *aev_event_pool;
#ifdef AEV_TRACKING
extern int aev_event_counter; //Current number of events in use
extern int aev_event_counter_max; //Peak use tracker
extern const char *aev_current_client; //Name of current client
#endif
#ifdef AEV_TRACKING
#define aev_client(x) aev_current_client = x;
#else
#define aev_client(x)
#endif
/* Refill event pool. */
int _aev_refill_pool(void);
/*
* Allocate an event.
*
* Will try to refill the event pool if required.
* Returns the event, or NULL, if the pool was empty
* and could not be refilled.
*/
static inline aev_event_t *aev_new(void)
{
aev_event_t *ev;
if(!aev_event_pool)
if(_aev_refill_pool() < 0)
return NULL;
ev = aev_event_pool;
aev_event_pool = ev->next;
#ifdef AEV_TRACKING
ev->type = AEV_ET_ALLOCATED;
ev->client = aev_current_client;
++aev_event_counter;
if(aev_event_counter > aev_event_counter_max)
aev_event_counter_max = aev_event_counter;
#endif
return ev;
}
/* Free an event. */
static inline void aev_free(aev_event_t *ev)
{
ev->next = aev_event_pool;
aev_event_pool = ev;
#ifdef AEV_TRACKING
ev->type = AEV_ET_FREE;
--aev_event_counter;
#endif
}
/*----------------------------------------------------------
Event Input Port
----------------------------------------------------------*/
typedef struct aev_port_t
{
/*
* Note that when 'first' is NULL, 'last' is undefined!
* (If there's no first event, there can't be a last
* event either - makes sense, eh? :-)
*/
aev_event_t *first;
aev_event_t *last;
const char *name;
} aev_port_t;
/*
* Initialize a port.
*****************************************
* DO NOT FORGET TO USE THIS!
*****************************************
*/
static inline void aev_port_init(aev_port_t *evp, const char *name)
{
evp->first = evp->last = NULL;
evp->name = name;
}
/*
* Write one event to a port. The event will belong to the
* owner of the port, who is responsible for freeing or
* reusing the event after reading it.
*
* NOTE: Events must be queued in increasing timestamp order!
*/
static inline void aev_write(aev_port_t *evp, aev_event_t *ev)
{
ev->next = NULL;
if(!evp->first)
evp->first = evp->last = ev; /* Oops, empty port! */
else
{
evp->last->next = ev;
evp->last = ev;
}
#ifdef AEV_TRACKING
ev->port = evp;
#endif
}
/*
* Insert an event at the right position in the queue of
* the specified port. The event will be placed *after*
* any other events with the same timestamp.
*
* The event will belong to the owner of the port, who is
* responsible for freeing or reusing the event after
* reading it.
*/
void aev_insert(aev_port_t *evp, aev_event_t *ev);
/*
* Read one event from a port. The event will be removed
* from the port, and is effectively detached from the
* event system until you reuse it by sending it somewhere,
* or free it.
*
* WARNING: Events *MUST* be either freed or reused!
*
* Event leaks will result in severe timing issues, as the
* pool will need to be refilled regularly, which is *not*
* a real time compatible operation.
*
* Returns the event, or NULL, if no event was queued.
*/
static inline aev_event_t *aev_read(aev_port_t *evp)
{
aev_event_t *ev = evp->first;
if(!ev)
return NULL;
evp->first = ev->next;
return ev;
}
/*
* Return number of frames from the start of the current
* buffer (current value of 'aev_timer') + 'offset' to
* the next event queued on this port.
*
* If there's no event enqueued, the maximum possible
* number of frames is returned.
*/
static inline unsigned aev_next(aev_port_t *evp, unsigned offset)
{
aev_event_t *ev = evp->first;
if(ev)
return AEV_TIME(ev->frame, offset);
else
return AEV_TIMESTAMP_MASK;
}
/*
* Remove and free all events on the specified port.
*/
static inline void aev_flush(aev_port_t *evp)
{
#ifdef AEV_TRACKING
while(1)
{
aev_event_t *ev = evp->first;
if(!ev)
return;
evp->first = ev->next;
aev_free(ev);
}
#else
/*
* Silly me! I've pulled this simple trick
* before, but just forgot somehow... :-)
*/
if(!evp->first)
return;
evp->last->next = aev_event_pool;
aev_event_pool = evp->first;
evp->first = NULL;
#endif
}
/*
* Short and handy "send" functions.
*
* Allocates an event, fills it in, and sends it to the
* specified port. The address of the event used is
* returned - which means that you can detect an error
* by checking that the return is not NULL.
*/
static inline aev_event_t *aev_send0(aev_port_t *evp,
aev_timestamp_t delay, Uint8 type)
{
aev_event_t *ev = aev_new();
if(!ev)
return NULL;
ev->type = type;
ev->frame = delay + aev_timer;
aev_write(evp, ev);
return ev;
}
static inline aev_event_t *aev_send1(aev_port_t *evp,
aev_timestamp_t delay, Uint8 type, Sint32 arg1)
{
aev_event_t *ev = aev_new();
if(!ev)
return NULL;
ev->type = type;
ev->frame = delay + aev_timer;
ev->arg1 = arg1;
aev_write(evp, ev);
return ev;
}
static inline aev_event_t *aev_send2(aev_port_t *evp,
aev_timestamp_t delay, Uint8 type, Sint32 arg1, Sint32 arg2)
{
aev_event_t *ev = aev_new();
if(!ev)
return NULL;
ev->type = type;
ev->frame = delay + aev_timer;
ev->arg1 = arg1;
ev->arg2 = arg2;
aev_write(evp, ev);
return ev;
}
static inline aev_event_t *aev_sendi0(aev_port_t *evp,
aev_timestamp_t delay, Uint8 type, Uint8 index)
{
aev_event_t *ev = aev_new();
if(!ev)
return NULL;
ev->type = type;
ev->index = index;
ev->frame = delay + aev_timer;
aev_write(evp, ev);
return ev;
}
static inline aev_event_t *aev_sendi1(aev_port_t *evp,
aev_timestamp_t delay, Uint8 type,
Uint8 index, Sint32 arg1)
{
aev_event_t *ev = aev_new();
if(!ev)
return NULL;
ev->type = type;
ev->index = index;
ev->frame = delay + aev_timer;
ev->arg1 = arg1;
aev_write(evp, ev);
return ev;
}
static inline aev_event_t *aev_sendi2(aev_port_t *evp,
aev_timestamp_t delay, Uint8 type,
Uint8 index, Sint32 arg1, Sint32 arg2)
{
aev_event_t *ev = aev_new();
if(!ev)
return NULL;
ev->type = type;
ev->index = index;
ev->frame = delay + aev_timer;
ev->arg1 = arg1;
ev->arg2 = arg2;
aev_write(evp, ev);
return ev;
}
/*
* Like the above, but using aev_insert().
*/
static inline aev_event_t *aev_insert0(aev_port_t *evp,
aev_timestamp_t delay, Uint8 type)
{
aev_event_t *ev = aev_new();
if(!ev)
return NULL;
ev->type = type;
ev->frame = delay + aev_timer;
aev_insert(evp, ev);
return ev;
}
static inline aev_event_t *aev_insert1(aev_port_t *evp,
aev_timestamp_t delay, Uint8 type, Sint32 arg1)
{
aev_event_t *ev = aev_new();
if(!ev)
return NULL;
ev->type = type;
ev->frame = delay + aev_timer;
ev->arg1 = arg1;
aev_insert(evp, ev);
return ev;
}
static inline aev_event_t *aev_insert2(aev_port_t *evp,
aev_timestamp_t delay, Uint8 type,
Sint32 arg1, Sint32 arg2)
{
aev_event_t *ev = aev_new();
if(!ev)
return NULL;
ev->type = type;
ev->frame = delay + aev_timer;
ev->arg1 = arg1;
ev->arg2 = arg2;
aev_insert(evp, ev);
return ev;
}
#endif /*_A_EVENTS_H_*/