The main data structures involved with the sound engine are found in both engine.h and engine.c:
typedef struct {
unsigned char type; /* state or single event */
unsigned char loc; /* stereo location (left - 0, right - 255) */
unsigned char prior; /* priority of the event */
unsigned char vol; /* volume of the event */
unsigned char dither; /* adjustable parameter for sound dithering
* 2 meanings:
* 1) Applies to states. sets the fade-in time
* when mixing between state sounds
* 2) Applies delay to handle events which
* occur in spurts - currently
* unimplemented
*/
char reserved[2]; /* reserved for future effects attributes */
int flags; /* effects flags */
int sound_len; /* Length of the sound string */
char *sound; /* sound to play, ref by name */
} EVENT;
typedef struct {
EVENT event;
struct timeval mix_time; /* time when an event was enqueued */
} ENGINE_EVENT;
struct engine_sched {
double startt; /* start times for each voicing */
long priorit; /* priority for currently playing sound */
double minendt; /* end time of the sound - currently unused */
};
/* Engine scheduling datastructure (calloc'd to half the number
* of channels
*/
struct engine_sched *sched = NULL;
/* Number of event and state channels */
static unsigned int no_ebuffs = 0;
static unsigned int no_sbuffs = 0;
engine.c also contains the sound table, whose purpose is to map names into the internal memory indices of the loaded sounds. Essentially, this is a hash table whose records are references to the sound data and descriptive data about the sound data.
typedef struct {
short **snds; /* array of event samples in mem */
unsigned int *lens; /* length of samples */
unsigned int snd_cnt; /* number of event sounds associated with
* an event
*/
} EVENT_ENTRY;
typedef struct {
int mixer_index; /* The index within the internal buffer of
* the mixer */
} STATE_ENTRY;
struct sound_entry {
struct sound_entry *next; /* Pointer to the next entry in the hash
* list
*/
char *name; /* Name of the sound to look up */
void *data; /* Pointer to the type of data. Depending on
* type, this can be a pointer to a:
* EVENT_ENTRY, which contains the data
* associated with an event.
* or
* STATE_BUF, which contains the index of
* the mixer buffer that holds the states
*/
EVENT_TYPE type; /* Type of the event stored */
};
/* For the sound table */
static struct sound_entry **sound_table = NULL;
The sound engine's main purpose is to assign incoming events appropriate channels in the voice mixer. For state events, this simply means making the appropriate changes in volume and stereo position of the voice mixer's state data structures. For singular events, the sound engine executes an algorithm to calculate the best available channel based on what's available, and the priority of the sound. If no channel is available, the event is put into a priority queue (literally based on event priority), to be played by the voice mixer as soon as a channel frees up.
The first data structure defined in the sound engine header is the event format. This is the format that Peep uses to represent an event internally and is also the same format that clients can send to the server if they do not wish to use XML representation. The event is then time stamped (stored in the mix_time field) to allow the sound engine to keep track of when the sound began playing.
During initialization, the engine creates the engine_sched data structure, which contains entries which correspond to each event channel in the mixer. Each entry stores the time at which the event started playing and the event's corresponding priority. This aids the sound engine in determining how to schedule sounds. The minendt field is currently not used but it's original purpose was to aid in deciding when a sound could safely be interrupted during playback and replaced with a higher priority one. The original idea was to interrupt a sound as soon as it was considered intelligeable, but not necessarily complete. It is possible this may be re-introduced to the algorithm at a later date.
The sound table is used for converting the symbolic names of sounds to their internal reference in memory. This is essentially a look up in a hash table, which returns a records containing a pointer to the sound data and any other information which is needed to play the sound. Two different record types exist in the sound table: a record type for events and a record type for states. Because states are already loaded into the mixer and are playing continuously, only the mixer channel used to hold the sound data needs to be kept in the sound table. For events, the pointers to the sound data are kept in the sound table and are fed to the mixer for playback. Other information kept is the length of the sounds in the entry and the number of sounds associated with the record.
The mixer_queue.c file contains the priority queue code, which begins filling up with events based on event priority if all channels are taken and uninterruptable. The mixer looks at this queue directly as soon as channels free up.