[audio] Allow output plugins to specify model

Output plugins can use either a push model (synchronous) or a pull
model (asynchronous). The ALSA plugin now uses the pull model. This
paves the way for making jack output a simple output plugin rather than
the combined render/output plugin it currently is (for #16) as now
snd_dma works with both models.
This commit is contained in:
Bill Currie 2021-06-25 11:41:42 +09:00
parent 79825db539
commit db7e99d842
3 changed files with 87 additions and 119 deletions

View file

@ -41,9 +41,15 @@ typedef struct snd_output_funcs_s {
void (*unblock_sound) (struct snd_s *snd);
} snd_output_funcs_t;
typedef enum {
som_push, // synchronous io (mixer pushes data to driver)
som_pull, // asynchronous io (driver pulls data from mixer)
} snd_output_model_t;
typedef struct snd_output_data_s {
unsigned *soundtime;
unsigned *paintedtime;
snd_output_model_t model;
} snd_output_data_t;
#endif // __QF_plugin_snd_output_h

View file

@ -68,6 +68,7 @@ static cvar_t *snd_show;
static general_data_t plugin_info_general_data;
static snd_output_funcs_t *snd_output_funcs;
static snd_output_data_t *snd_output_data;
static snd_t snd;
static int snd_shutdown = 0;
@ -147,17 +148,17 @@ s_stop_all_sounds (void)
//=============================================================================
/*static void
static void
s_get_soundtime (void)
{
int frames, framepos;
static int buffers, oldframepos;
frames = snd_shm->frames;
frames = snd.frames;
// it is possible to miscount buffers if it has wrapped twice between
// calls to s_update. Oh well.
if ((framepos = snd_output_funcs->pS_O_GetDMAPos ()) == -1)
if ((framepos = snd_output_funcs->get_dma_pos (&snd)) == -1)
return;
if (framepos < oldframepos) {
@ -192,14 +193,14 @@ s_update_ (void)
snd_paintedtime = soundtime;
}
// mix ahead of current position
endtime = soundtime + snd_mixahead->value * snd_shm->speed;
samps = snd_shm->frames;
endtime = soundtime + snd_mixahead->value * snd.speed;
samps = snd.frames;
if (endtime - soundtime > samps)
endtime = soundtime + samps;
SND_PaintChannels (endtime);
snd_output_funcs->pS_O_Submit ();
}*/
SND_PaintChannels (&snd, endtime);
snd_output_funcs->submit (&snd);
}
/*
s_update
@ -215,18 +216,20 @@ s_update (const vec3_t origin, const vec3_t forward, const vec3_t right,
SND_SetListener (&snd, origin, forward, right, up, ambient_sound_level);
// mix some sound
//s_update_ ();
//SND_ScanChannels (0);
if (snd_output_data->model == som_push) {
// mix some sound
s_update_ ();
SND_ScanChannels (&snd, 0);
}
}
//static void
//s_extra_update (void)
//{
// if (!sound_started || snd_noextraupdate->int_val)
// return; // don't pollute timings
// s_update_ ();
//}
static void
s_extra_update (void)
{
if (!sound_started || snd_noextraupdate->int_val)
return; // don't pollute timings
s_update_ ();
}
static void
s_block_sound (void)
@ -305,6 +308,7 @@ static void
s_init (void)
{
snd_output_funcs = snd_render_data.output->functions->snd_output;
snd_output_data = snd_render_data.output->data->snd_output;
snd_render_data.soundtime = &soundtime;
Sys_Printf ("\nSound Initialization\n");
@ -466,7 +470,7 @@ static snd_render_funcs_t plugin_info_render_funcs = {
.update = s_update,
.stop_all_sounds = s_stop_all_sounds,
//.extra_update = s_extra_update,
.extra_update = s_extra_update,
.block_sound = s_block_sound,
.unblock_sound = s_unblock_sound,
};

View file

@ -53,19 +53,15 @@ static void *alsa_handle;
static snd_pcm_t *pcm;
static snd_async_handler_t *async_handler;
static plugin_t plugin_info;
static plugin_data_t plugin_info_data;
static plugin_funcs_t plugin_info_funcs;
static general_data_t plugin_info_general_data;
static general_funcs_t plugin_info_general_funcs;
static snd_output_data_t plugin_info_snd_output_data;
static snd_output_funcs_t plugin_info_snd_output_funcs;
static cvar_t *snd_bits;
static cvar_t *snd_device;
static cvar_t *snd_rate;
static cvar_t *snd_stereo;
//FIXME xfer probably should not be touching this (such data should probably
//come through snd_t)
static snd_output_data_t plugin_info_snd_output_data;
#define QF_ALSA_NEED(ret, func, params) \
static ret (*qf##func) params;
#include "alsa_funcs_list.h"
@ -106,8 +102,6 @@ SNDDMA_Init_Cvars (void)
"sound sample depth. 0 is system default");
}
static int SNDDMA_GetDMAPos (snd_t *snd);
static __attribute__((const)) snd_pcm_uframes_t
round_buffer_size (snd_pcm_uframes_t sz)
{
@ -203,6 +197,7 @@ alsa_xfer (snd_t *snd, portable_samplepair_t *paintbuffer, int count,
{
int out_idx, out_max, step, val;
float *p;
alsa_pkt_t *packet = snd->xfer_data;;
p = (float *) paintbuffer;
count *= snd->channels;
@ -213,7 +208,7 @@ alsa_xfer (snd_t *snd, portable_samplepair_t *paintbuffer, int count,
step = 3 - snd->channels;
if (snd->samplebits == 16) {
short *out = (short *) snd->buffer;
short *out = (short *) packet->areas[0].addr;
while (count--) {
val = (*p * volume) * 0x8000;
@ -227,7 +222,7 @@ alsa_xfer (snd_t *snd, portable_samplepair_t *paintbuffer, int count,
out_idx = 0;
}
} else if (snd->samplebits == 8) {
unsigned char *out = (unsigned char *) snd->buffer;
unsigned char *out = (unsigned char *) packet->areas[0].addr;
while (count--) {
val = (*p * volume) * 128;
@ -292,6 +287,7 @@ alsa_process (snd_pcm_t *pcm, snd_t *snd)
}
ret = 0;
}
snd->buffer = packet.areas[0].addr;
SND_PaintChannels (snd, snd_paintedtime + packet.nframes);
if ((res = qfsnd_pcm_mmap_commit (pcm, packet.offset,
packet.nframes)) < 0
@ -630,7 +626,15 @@ SNDDMA_Init (snd_t *snd)
}
snd->frames = buffer_size;
SNDDMA_GetDMAPos (snd); //XXX sets snd->buffer
// send the first period to fill the buffer
// also sets snd->buffer
if (alsa_process (pcm, snd) < 0) {
goto error;
}
qfsnd_pcm_start (pcm);
Sys_Printf ("%5d channels %sinterleaved\n", snd->channels,
snd->xfer ? "non-" : "");
Sys_Printf ("%5d samples (%.1fms)\n", snd->frames,
@ -644,13 +648,6 @@ SNDDMA_Init (snd_t *snd)
snd_inited = 1;
// send the first period to fill the buffer
if (alsa_process (pcm, snd) < 0) {
goto error;
}
qfsnd_pcm_start (pcm);
return 1;
error:
qfsnd_pcm_close (pcm);
@ -662,21 +659,6 @@ error:
return 0;
}
static int
SNDDMA_GetDMAPos (snd_t *snd)
{
const snd_pcm_channel_area_t *areas;
snd_pcm_uframes_t offset;
snd_pcm_uframes_t nframes = snd->frames;
qfsnd_pcm_avail_update (pcm);
qfsnd_pcm_mmap_begin (pcm, &areas, &offset, &nframes);
snd->framepos = offset;
snd->buffer = areas->addr;
snd->xfer_data = (void *) areas;
return snd->framepos;
}
static void
SNDDMA_shutdown (snd_t *snd)
{
@ -688,44 +670,6 @@ SNDDMA_shutdown (snd_t *snd)
}
}
/*
SNDDMA_Submit
Send sound to device if buffer isn't really the dma buffer
*/
static void
SNDDMA_Submit (snd_t *snd)
{
int state;
int count = (*plugin_info_snd_output_data.paintedtime -
*plugin_info_snd_output_data.soundtime);
const snd_pcm_channel_area_t *areas;
snd_pcm_uframes_t nframes;
snd_pcm_uframes_t offset;
if (snd_blocked)
return;
nframes = count;
qfsnd_pcm_avail_update (pcm);
qfsnd_pcm_mmap_begin (pcm, &areas, &offset, &nframes);
state = qfsnd_pcm_state (pcm);
switch (state) {
case SND_PCM_STATE_PREPARED:
qfsnd_pcm_mmap_commit (pcm, offset, nframes);
qfsnd_pcm_start (pcm);
break;
case SND_PCM_STATE_RUNNING:
qfsnd_pcm_mmap_commit (pcm, offset, nframes);
break;
default:
break;
}
}
static void
SNDDMA_BlockSound (snd_t *snd)
{
@ -742,35 +686,49 @@ SNDDMA_UnblockSound (snd_t *snd)
qfsnd_pcm_pause (pcm, 0);
}
PLUGIN_INFO(snd_output, alsa)
{
plugin_info.type = qfp_snd_output;
plugin_info.api_version = QFPLUGIN_VERSION;
plugin_info.plugin_version = "0.1";
plugin_info.description = "ALSA digital output";
plugin_info.copyright = "Copyright (C) 1996-1997 id Software, Inc.\n"
static general_data_t plugin_info_general_data = {
};
static general_funcs_t plugin_info_general_funcs = {
.init = SNDDMA_Init_Cvars,
.shutdown = NULL,
};
static snd_output_data_t plugin_info_snd_output_data = {
.model = som_pull,
};
static snd_output_funcs_t plugin_info_snd_output_funcs = {
.init = SNDDMA_Init,
.shutdown = SNDDMA_shutdown,
.block_sound = SNDDMA_BlockSound,
.unblock_sound = SNDDMA_UnblockSound,
};
static plugin_data_t plugin_info_data = {
.general = &plugin_info_general_data,
.snd_output = &plugin_info_snd_output_data,
};
static plugin_funcs_t plugin_info_funcs = {
.general = &plugin_info_general_funcs,
.snd_output = &plugin_info_snd_output_funcs,
};
static plugin_t plugin_info = {
.type = qfp_snd_output,
.api_version = QFPLUGIN_VERSION,
.plugin_version = "0.1",
.description = "ALSA digital output",
.copyright = "Copyright (C) 1996-1997 id Software, Inc.\n"
"Copyright (C) 1999,2000,2001 contributors of the QuakeForge "
"project\n"
"Please see the file \"AUTHORS\" for a list of contributors";
plugin_info.functions = &plugin_info_funcs;
plugin_info.data = &plugin_info_data;
plugin_info_data.general = &plugin_info_general_data;
plugin_info_data.input = NULL;
plugin_info_data.snd_output = &plugin_info_snd_output_data;
plugin_info_funcs.general = &plugin_info_general_funcs;
plugin_info_funcs.input = NULL;
plugin_info_funcs.snd_output = &plugin_info_snd_output_funcs;
plugin_info_general_funcs.init = SNDDMA_Init_Cvars;
plugin_info_general_funcs.shutdown = NULL;
plugin_info_snd_output_funcs.init = SNDDMA_Init;
plugin_info_snd_output_funcs.shutdown = SNDDMA_shutdown;
plugin_info_snd_output_funcs.get_dma_pos = SNDDMA_GetDMAPos;
plugin_info_snd_output_funcs.submit = SNDDMA_Submit;
plugin_info_snd_output_funcs.block_sound = SNDDMA_BlockSound;
plugin_info_snd_output_funcs.unblock_sound = SNDDMA_UnblockSound;
"Please see the file \"AUTHORS\" for a list of contributors",
.functions = &plugin_info_funcs,
.data = &plugin_info_data,
};
PLUGIN_INFO(snd_output, alsa)
{
return &plugin_info;
}