2001-04-10 09:40:09 +00:00
|
|
|
/*
|
2003-09-09 18:43:38 +00:00
|
|
|
snd_alsa.c
|
2001-04-10 09:40:09 +00:00
|
|
|
|
2004-01-17 22:03:58 +00:00
|
|
|
Support for the ALSA 1.0.1 sound driver
|
2001-04-10 09:40:09 +00:00
|
|
|
|
|
|
|
Copyright (C) 1999,2000 contributors of the QuakeForge project
|
|
|
|
Please see the file "AUTHORS" for a list of contributors
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
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 General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to:
|
|
|
|
|
|
|
|
Free Software Foundation, Inc.
|
|
|
|
59 Temple Place - Suite 330
|
|
|
|
Boston, MA 02111-1307, USA
|
|
|
|
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2002-04-19 20:08:54 +00:00
|
|
|
#include <dlfcn.h>
|
2001-11-24 08:05:44 +00:00
|
|
|
#include <alsa/asoundlib.h>
|
2001-04-10 09:40:09 +00:00
|
|
|
|
2001-08-22 20:26:25 +00:00
|
|
|
#include "QF/cvar.h"
|
|
|
|
#include "QF/qargs.h"
|
2001-09-21 04:22:46 +00:00
|
|
|
#include "QF/sys.h"
|
2001-04-10 09:40:09 +00:00
|
|
|
|
2011-07-23 06:56:22 +00:00
|
|
|
#include "snd_internal.h"
|
2007-03-10 04:21:32 +00:00
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
typedef struct {
|
|
|
|
const snd_pcm_channel_area_t *areas;
|
|
|
|
snd_pcm_uframes_t offset;
|
|
|
|
snd_pcm_uframes_t nframes;
|
|
|
|
} alsa_pkt_t;
|
|
|
|
|
2004-01-17 22:03:58 +00:00
|
|
|
static int snd_inited;
|
|
|
|
static int snd_blocked = 0;
|
|
|
|
static snd_pcm_uframes_t buffer_size;
|
2001-08-31 00:05:58 +00:00
|
|
|
|
2004-01-17 22:03:58 +00:00
|
|
|
static void *alsa_handle;
|
|
|
|
static snd_pcm_t *pcm;
|
2021-06-25 00:50:31 +00:00
|
|
|
static snd_async_handler_t *async_handler;
|
2001-08-31 00:05:58 +00:00
|
|
|
|
[cvar] Make cvars properly typed
This is an extremely extensive patch as it hits every cvar, and every
usage of the cvars. Cvars no longer store the value they control,
instead, they use a cexpr value object to reference the value and
specify the value's type (currently, a null type is used for strings).
Non-string cvars are passed through cexpr, allowing expressions in the
cvars' settings. Also, cvars have returned to an enhanced version of the
original (id quake) registration scheme.
As a minor benefit, relevant code having direct access to the
cvar-controlled variables is probably a slight optimization as it
removed a pointer dereference, and the variables can be located for data
locality.
The static cvar descriptors are made private as an additional safety
layer, though there's nothing stopping external modification via
Cvar_FindVar (which is needed for adding listeners).
While not used yet (partly due to working out the design), cvars can
have a validation function.
Registering a cvar allows a primary listener (and its data) to be
specified: it will always be called first when the cvar is modified. The
combination of proper listeners and direct access to the controlled
variable greatly simplifies the more complex cvar interactions as much
less null checking is required, and there's no need for one cvar's
callback to call another's.
nq-x11 is known to work at least well enough for the demos. More testing
will come.
2022-04-23 03:22:45 +00:00
|
|
|
static int snd_bits;
|
|
|
|
static cvar_t snd_bits_cvar = {
|
|
|
|
.name = "snd_bits",
|
|
|
|
.description =
|
|
|
|
"sound sample depth. 0 is system default",
|
|
|
|
.default_value = "0",
|
|
|
|
.flags = CVAR_ROM,
|
|
|
|
.value = { .type = &cexpr_int, .value = &snd_bits },
|
|
|
|
};
|
|
|
|
static char *snd_device;
|
|
|
|
static cvar_t snd_device_cvar = {
|
|
|
|
.name = "snd_device",
|
|
|
|
.description =
|
|
|
|
"sound device. \"\" is system default",
|
|
|
|
.default_value = "",
|
|
|
|
.flags = CVAR_ROM,
|
|
|
|
.value = { .type = 0, .value = &snd_device },
|
|
|
|
};
|
|
|
|
static int snd_rate;
|
|
|
|
static cvar_t snd_rate_cvar = {
|
|
|
|
.name = "snd_rate",
|
|
|
|
.description =
|
|
|
|
"sound playback rate. 0 is system default",
|
|
|
|
.default_value = "0",
|
|
|
|
.flags = CVAR_ROM,
|
|
|
|
.value = { .type = &cexpr_int, .value = &snd_rate },
|
|
|
|
};
|
|
|
|
static int snd_stereo;
|
|
|
|
static cvar_t snd_stereo_cvar = {
|
|
|
|
.name = "snd_stereo",
|
|
|
|
.description =
|
|
|
|
"sound stereo output",
|
|
|
|
.default_value = "1",
|
|
|
|
.flags = CVAR_ROM,
|
|
|
|
.value = { .type = &cexpr_int, .value = &snd_stereo },
|
|
|
|
};
|
2001-09-09 21:41:35 +00:00
|
|
|
|
2021-06-25 02:41:42 +00:00
|
|
|
//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;
|
|
|
|
|
2002-04-19 20:08:54 +00:00
|
|
|
#define QF_ALSA_NEED(ret, func, params) \
|
|
|
|
static ret (*qf##func) params;
|
|
|
|
#include "alsa_funcs_list.h"
|
|
|
|
#undef QF_ALSA_NEED
|
|
|
|
|
2023-06-13 09:06:11 +00:00
|
|
|
static bool
|
2002-04-19 20:08:54 +00:00
|
|
|
load_libasound (void)
|
|
|
|
{
|
|
|
|
if (!(alsa_handle = dlopen ("libasound.so.2", RTLD_GLOBAL | RTLD_NOW))) {
|
2003-12-22 01:07:49 +00:00
|
|
|
Sys_Printf ("Couldn't load libasound.so.2: %s\n", dlerror ());
|
2002-04-19 20:08:54 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#define QF_ALSA_NEED(ret, func, params) \
|
|
|
|
if (!(qf##func = dlsym (alsa_handle, #func))) { \
|
|
|
|
Sys_Printf ("Couldn't load ALSA function %s\n", #func); \
|
|
|
|
dlclose (alsa_handle); \
|
|
|
|
alsa_handle = 0; \
|
|
|
|
return false; \
|
|
|
|
}
|
|
|
|
#include "alsa_funcs_list.h"
|
|
|
|
#undef QF_ALSA_NEED
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define snd_pcm_hw_params_sizeof qfsnd_pcm_hw_params_sizeof
|
|
|
|
#define snd_pcm_sw_params_sizeof qfsnd_pcm_sw_params_sizeof
|
|
|
|
|
|
|
|
static void
|
|
|
|
SNDDMA_Init_Cvars (void)
|
2001-08-23 04:01:46 +00:00
|
|
|
{
|
[cvar] Make cvars properly typed
This is an extremely extensive patch as it hits every cvar, and every
usage of the cvars. Cvars no longer store the value they control,
instead, they use a cexpr value object to reference the value and
specify the value's type (currently, a null type is used for strings).
Non-string cvars are passed through cexpr, allowing expressions in the
cvars' settings. Also, cvars have returned to an enhanced version of the
original (id quake) registration scheme.
As a minor benefit, relevant code having direct access to the
cvar-controlled variables is probably a slight optimization as it
removed a pointer dereference, and the variables can be located for data
locality.
The static cvar descriptors are made private as an additional safety
layer, though there's nothing stopping external modification via
Cvar_FindVar (which is needed for adding listeners).
While not used yet (partly due to working out the design), cvars can
have a validation function.
Registering a cvar allows a primary listener (and its data) to be
specified: it will always be called first when the cvar is modified. The
combination of proper listeners and direct access to the controlled
variable greatly simplifies the more complex cvar interactions as much
less null checking is required, and there's no need for one cvar's
callback to call another's.
nq-x11 is known to work at least well enough for the demos. More testing
will come.
2022-04-23 03:22:45 +00:00
|
|
|
Cvar_Register (&snd_stereo_cvar, 0, 0);
|
|
|
|
Cvar_Register (&snd_rate_cvar, 0, 0);
|
|
|
|
Cvar_Register (&snd_device_cvar, 0, 0);
|
|
|
|
Cvar_Register (&snd_bits_cvar, 0, 0);
|
2001-08-23 04:01:46 +00:00
|
|
|
}
|
2001-05-24 17:00:08 +00:00
|
|
|
|
2018-10-09 03:35:01 +00:00
|
|
|
static __attribute__((const)) snd_pcm_uframes_t
|
2005-06-14 05:37:26 +00:00
|
|
|
round_buffer_size (snd_pcm_uframes_t sz)
|
|
|
|
{
|
|
|
|
snd_pcm_uframes_t mask = ~0;
|
|
|
|
|
|
|
|
while (sz & mask) {
|
|
|
|
sz &= mask;
|
|
|
|
mask <<= 1;
|
|
|
|
}
|
|
|
|
return sz;
|
|
|
|
}
|
|
|
|
|
2011-09-07 08:00:57 +00:00
|
|
|
static inline int
|
|
|
|
clamp_16 (int val)
|
|
|
|
{
|
|
|
|
if (val > 0x7fff)
|
|
|
|
val = 0x7fff;
|
|
|
|
else if (val < -0x8000)
|
|
|
|
val = -0x8000;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
clamp_8 (int val)
|
|
|
|
{
|
|
|
|
if (val > 0x7f)
|
|
|
|
val = 0x7f;
|
|
|
|
else if (val < -0x80)
|
|
|
|
val = -0x80;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2021-06-23 15:04:02 +00:00
|
|
|
alsa_ni_xfer (snd_t *snd, portable_samplepair_t *paintbuffer, int count,
|
|
|
|
float volume)
|
2011-09-07 08:00:57 +00:00
|
|
|
{
|
|
|
|
const snd_pcm_channel_area_t *areas;
|
2011-09-08 09:10:09 +00:00
|
|
|
int out_idx, out_max;
|
2011-09-07 08:00:57 +00:00
|
|
|
float *p;
|
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
areas = snd->xfer_data;
|
2011-09-07 08:00:57 +00:00
|
|
|
|
2011-09-08 09:10:09 +00:00
|
|
|
p = (float *) paintbuffer;
|
2021-06-23 15:04:02 +00:00
|
|
|
out_max = snd->frames - 1;
|
2011-12-08 02:02:19 +00:00
|
|
|
out_idx = *plugin_info_snd_output_data.paintedtime;
|
2011-09-07 08:00:57 +00:00
|
|
|
while (out_idx > out_max)
|
|
|
|
out_idx -= out_max + 1;
|
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
if (snd->samplebits == 16) {
|
2011-09-07 08:00:57 +00:00
|
|
|
short *out_0 = (short *) areas[0].addr;
|
|
|
|
short *out_1 = (short *) areas[1].addr;
|
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
if (snd->channels == 2) {
|
2011-09-07 08:00:57 +00:00
|
|
|
while (count--) {
|
2011-09-08 09:10:09 +00:00
|
|
|
out_0[out_idx] = clamp_16 ((*p++ * volume) * 0x8000);
|
|
|
|
out_1[out_idx] = clamp_16 ((*p++ * volume) * 0x8000);
|
2011-09-07 08:00:57 +00:00
|
|
|
if (out_idx++ > out_max)
|
|
|
|
out_idx = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while (count--) {
|
2011-09-08 09:10:09 +00:00
|
|
|
out_0[out_idx] = clamp_16 ((*p++ * volume) * 0x8000);
|
2011-09-07 08:00:57 +00:00
|
|
|
p++; // skip right channel
|
|
|
|
if (out_idx++ > out_max)
|
|
|
|
out_idx = 0;
|
|
|
|
}
|
|
|
|
}
|
2021-06-23 15:04:02 +00:00
|
|
|
} else if (snd->samplebits == 8) {
|
2011-09-07 08:00:57 +00:00
|
|
|
byte *out_0 = (byte *) areas[0].addr;
|
|
|
|
byte *out_1 = (byte *) areas[1].addr;
|
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
if (snd->channels == 2) {
|
2011-09-07 08:00:57 +00:00
|
|
|
while (count--) {
|
2011-09-08 09:10:09 +00:00
|
|
|
out_0[out_idx] = clamp_8 ((*p++ * volume) * 0x80);
|
|
|
|
out_1[out_idx] = clamp_8 ((*p++ * volume) * 0x80);
|
2011-09-07 08:00:57 +00:00
|
|
|
if (out_idx++ > out_max)
|
|
|
|
out_idx = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while (count--) {
|
2011-09-08 09:10:09 +00:00
|
|
|
out_0[out_idx] = clamp_8 ((*p++ * volume) * 0x8000);
|
2011-09-07 08:00:57 +00:00
|
|
|
p++; // skip right channel
|
|
|
|
if (out_idx++ > out_max)
|
|
|
|
out_idx = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
static void
|
|
|
|
alsa_xfer (snd_t *snd, portable_samplepair_t *paintbuffer, int count,
|
|
|
|
float volume)
|
|
|
|
{
|
|
|
|
int out_idx, out_max, step, val;
|
|
|
|
float *p;
|
2021-06-25 02:41:42 +00:00
|
|
|
alsa_pkt_t *packet = snd->xfer_data;;
|
2021-06-23 15:04:02 +00:00
|
|
|
|
|
|
|
p = (float *) paintbuffer;
|
|
|
|
count *= snd->channels;
|
|
|
|
out_max = (snd->frames * snd->channels) - 1;
|
2021-06-26 07:18:18 +00:00
|
|
|
out_idx = snd->paintedtime * snd->channels;
|
2021-06-23 15:04:02 +00:00
|
|
|
while (out_idx > out_max)
|
|
|
|
out_idx -= out_max + 1;
|
|
|
|
step = 3 - snd->channels;
|
|
|
|
|
|
|
|
if (snd->samplebits == 16) {
|
2021-06-25 02:41:42 +00:00
|
|
|
short *out = (short *) packet->areas[0].addr;
|
2021-06-23 15:04:02 +00:00
|
|
|
|
|
|
|
while (count--) {
|
|
|
|
val = (*p * volume) * 0x8000;
|
|
|
|
p += step;
|
|
|
|
if (val > 0x7fff)
|
|
|
|
val = 0x7fff;
|
|
|
|
else if (val < -0x8000)
|
|
|
|
val = -0x8000;
|
|
|
|
out[out_idx++] = val;
|
|
|
|
if (out_idx > out_max)
|
|
|
|
out_idx = 0;
|
|
|
|
}
|
|
|
|
} else if (snd->samplebits == 8) {
|
2021-06-25 02:41:42 +00:00
|
|
|
unsigned char *out = (unsigned char *) packet->areas[0].addr;
|
2021-06-23 15:04:02 +00:00
|
|
|
|
|
|
|
while (count--) {
|
|
|
|
val = (*p * volume) * 128;
|
|
|
|
p += step;
|
|
|
|
if (val > 0x7f)
|
|
|
|
val = 0x7f;
|
|
|
|
else if (val < -0x80)
|
|
|
|
val = -0x80;
|
|
|
|
out[out_idx++] = val + 0x80;
|
|
|
|
if (out_idx > out_max)
|
|
|
|
out_idx = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
static int
|
|
|
|
alsa_recover (snd_pcm_t *pcm, int err)
|
|
|
|
{
|
|
|
|
if (err == -EPIPE) {
|
|
|
|
Sys_Printf ("snd_alsa: xrun\n");
|
|
|
|
// xrun
|
|
|
|
if ((err = qfsnd_pcm_prepare (pcm)) < 0) {
|
|
|
|
Sys_MaskPrintf (SYS_snd, "snd_alsa: recover from xrun failed: %s\n",
|
|
|
|
qfsnd_strerror (err));
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
} else if (err == -ESTRPIPE) {
|
|
|
|
Sys_Printf ("snd_alsa: suspend\n");
|
|
|
|
// suspend
|
|
|
|
while ((err = qfsnd_pcm_resume(pcm)) == -EAGAIN) {
|
|
|
|
usleep (20 * 1000);
|
|
|
|
}
|
|
|
|
if (err < 0 && (err = qfsnd_pcm_prepare (pcm)) < 0) {
|
|
|
|
Sys_MaskPrintf (SYS_snd,
|
|
|
|
"snd_alsa: recover from suspend failed: %s\n",
|
|
|
|
qfsnd_strerror (err));
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
alsa_process (snd_pcm_t *pcm, snd_t *snd)
|
|
|
|
{
|
|
|
|
alsa_pkt_t packet;
|
|
|
|
int res;
|
|
|
|
int ret = 1;
|
|
|
|
snd_pcm_uframes_t size = snd->submission_chunk;
|
|
|
|
|
|
|
|
snd->xfer_data = &packet;
|
|
|
|
while (size > 0) {
|
|
|
|
packet.nframes = size;
|
|
|
|
if ((res = qfsnd_pcm_mmap_begin (pcm, &packet.areas, &packet.offset,
|
|
|
|
&packet.nframes)) < 0) {
|
|
|
|
if ((res = alsa_recover (pcm, -EPIPE)) < 0) {
|
|
|
|
Sys_Printf ("snd_alsa: XRUN recovery failed: %s\n",
|
|
|
|
qfsnd_strerror (res));
|
2022-07-26 12:14:28 +00:00
|
|
|
snd->xfer_data = 0;
|
2021-06-25 00:50:31 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
}
|
2021-06-25 02:41:42 +00:00
|
|
|
snd->buffer = packet.areas[0].addr;
|
2021-06-26 07:18:18 +00:00
|
|
|
snd->paint_channels (snd, snd->paintedtime + packet.nframes);
|
2021-06-25 00:50:31 +00:00
|
|
|
if ((res = qfsnd_pcm_mmap_commit (pcm, packet.offset,
|
|
|
|
packet.nframes)) < 0
|
|
|
|
|| (snd_pcm_uframes_t) res != packet.nframes) {
|
|
|
|
if ((res = alsa_recover (pcm, res >= 0 ? -EPIPE : res)) < 0) {
|
|
|
|
Sys_Printf ("snd_alsa: XRUN recovery failed: %s\n",
|
|
|
|
qfsnd_strerror (res));
|
2022-07-26 12:14:28 +00:00
|
|
|
snd->xfer_data = 0;
|
2021-06-25 00:50:31 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
size -= packet.nframes;
|
|
|
|
}
|
|
|
|
|
2022-07-26 12:14:28 +00:00
|
|
|
snd->xfer_data = 0;
|
2021-06-25 00:50:31 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
static void
|
|
|
|
alsa_callback (snd_async_handler_t *handler)
|
|
|
|
{
|
|
|
|
snd_pcm_t *pcm = qfsnd_async_handler_get_pcm (handler);
|
|
|
|
snd_t *snd = qfsnd_async_handler_get_callback_private (handler);
|
2021-06-25 00:50:31 +00:00
|
|
|
int res;
|
|
|
|
int avail;
|
|
|
|
int first = 0;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
snd_pcm_state_t state = qfsnd_pcm_state (pcm);
|
|
|
|
if (state == SND_PCM_STATE_XRUN) {
|
|
|
|
if ((res = alsa_recover (pcm, -EPIPE)) < 0) {
|
|
|
|
Sys_Printf ("snd_alsa: XRUN recovery failed: %s\n",
|
|
|
|
qfsnd_strerror (res));
|
|
|
|
//FIXME disable/restart sound
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (state == SND_PCM_STATE_SUSPENDED) {
|
|
|
|
if ((res = alsa_recover (pcm, -EPIPE)) < 0) {
|
|
|
|
Sys_Printf ("snd_alsa: suspend recovery failed: %s\n",
|
|
|
|
qfsnd_strerror (res));
|
|
|
|
//FIXME disable/restart sound
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((avail = qfsnd_pcm_avail_update (pcm)) < 0) {
|
|
|
|
if ((res = alsa_recover (pcm, -EPIPE)) < 0) {
|
|
|
|
Sys_Printf ("snd_alsa: avail update failed: %s\n",
|
|
|
|
qfsnd_strerror (res));
|
|
|
|
//FIXME disable/restart sound
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
first = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (avail < snd->submission_chunk) {
|
|
|
|
if (first) {
|
|
|
|
first = 0;
|
|
|
|
if ((res = qfsnd_pcm_start (pcm)) < 0) {
|
|
|
|
Sys_Printf ("snd_alsa: start failed: %s\n",
|
|
|
|
qfsnd_strerror (res));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2021-06-23 15:04:02 +00:00
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
if ((res = alsa_process (pcm, snd))) {
|
|
|
|
if (res < 0) {
|
|
|
|
//FIXME disable/restart sound
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
first = 1;
|
|
|
|
}
|
|
|
|
}
|
2021-06-23 15:04:02 +00:00
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
static int
|
|
|
|
alsa_open_playback (snd_t *snd, const char *device)
|
|
|
|
{
|
|
|
|
if (!*device) {
|
|
|
|
device = "default";
|
|
|
|
}
|
|
|
|
Sys_Printf ("Using PCM %s.\n", device);
|
2021-06-23 15:04:02 +00:00
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
int res = qfsnd_pcm_open (&pcm, device, SND_PCM_STREAM_PLAYBACK,
|
|
|
|
SND_PCM_NONBLOCK);
|
|
|
|
if (res < 0) {
|
|
|
|
Sys_Printf ("snd_alsa: audio open error: %s\n", qfsnd_strerror (res));
|
|
|
|
return 0;
|
|
|
|
}
|
2021-06-23 15:04:02 +00:00
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
return 1;
|
2021-06-23 15:04:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2021-06-25 00:50:31 +00:00
|
|
|
alsa_playback_set_mmap (snd_t *snd, snd_pcm_hw_params_t *hw)
|
2001-04-10 09:40:09 +00:00
|
|
|
{
|
2021-06-25 00:50:31 +00:00
|
|
|
int res;
|
2001-04-10 09:40:09 +00:00
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
res = qfsnd_pcm_hw_params_set_access (pcm, hw,
|
|
|
|
SND_PCM_ACCESS_MMAP_INTERLEAVED);
|
|
|
|
if (res == 0) {
|
|
|
|
snd->xfer = alsa_xfer;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
Sys_MaskPrintf (SYS_snd, "snd_alsa: Failure to set interleaved PCM "
|
|
|
|
"access. (%d) %s\n", res, qfsnd_strerror (res));
|
|
|
|
res = qfsnd_pcm_hw_params_set_access (pcm, hw,
|
|
|
|
SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
|
|
|
|
if (res == 0) {
|
|
|
|
snd->xfer = alsa_ni_xfer;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
Sys_MaskPrintf (SYS_snd, "snd_alsa: Failure to set noninterleaved PCM "
|
|
|
|
"access. (%d) %s\n", res, qfsnd_strerror (res));
|
|
|
|
Sys_Printf ("snd_alsa: could not set mmap access\n");
|
2002-04-19 20:08:54 +00:00
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2001-04-10 09:40:09 +00:00
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
static int
|
|
|
|
alsa_playback_set_bps (snd_t *snd, snd_pcm_hw_params_t *hw)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
int bps = 0;
|
|
|
|
|
[cvar] Make cvars properly typed
This is an extremely extensive patch as it hits every cvar, and every
usage of the cvars. Cvars no longer store the value they control,
instead, they use a cexpr value object to reference the value and
specify the value's type (currently, a null type is used for strings).
Non-string cvars are passed through cexpr, allowing expressions in the
cvars' settings. Also, cvars have returned to an enhanced version of the
original (id quake) registration scheme.
As a minor benefit, relevant code having direct access to the
cvar-controlled variables is probably a slight optimization as it
removed a pointer dereference, and the variables can be located for data
locality.
The static cvar descriptors are made private as an additional safety
layer, though there's nothing stopping external modification via
Cvar_FindVar (which is needed for adding listeners).
While not used yet (partly due to working out the design), cvars can
have a validation function.
Registering a cvar allows a primary listener (and its data) to be
specified: it will always be called first when the cvar is modified. The
combination of proper listeners and direct access to the controlled
variable greatly simplifies the more complex cvar interactions as much
less null checking is required, and there's no need for one cvar's
callback to call another's.
nq-x11 is known to work at least well enough for the demos. More testing
will come.
2022-04-23 03:22:45 +00:00
|
|
|
if (snd_bits == 16) {
|
2021-06-25 00:50:31 +00:00
|
|
|
bps = SND_PCM_FORMAT_S16_LE;
|
|
|
|
snd->samplebits = 16;
|
[cvar] Make cvars properly typed
This is an extremely extensive patch as it hits every cvar, and every
usage of the cvars. Cvars no longer store the value they control,
instead, they use a cexpr value object to reference the value and
specify the value's type (currently, a null type is used for strings).
Non-string cvars are passed through cexpr, allowing expressions in the
cvars' settings. Also, cvars have returned to an enhanced version of the
original (id quake) registration scheme.
As a minor benefit, relevant code having direct access to the
cvar-controlled variables is probably a slight optimization as it
removed a pointer dereference, and the variables can be located for data
locality.
The static cvar descriptors are made private as an additional safety
layer, though there's nothing stopping external modification via
Cvar_FindVar (which is needed for adding listeners).
While not used yet (partly due to working out the design), cvars can
have a validation function.
Registering a cvar allows a primary listener (and its data) to be
specified: it will always be called first when the cvar is modified. The
combination of proper listeners and direct access to the controlled
variable greatly simplifies the more complex cvar interactions as much
less null checking is required, and there's no need for one cvar's
callback to call another's.
nq-x11 is known to work at least well enough for the demos. More testing
will come.
2022-04-23 03:22:45 +00:00
|
|
|
} else if (snd_bits == 8) {
|
2021-06-25 00:50:31 +00:00
|
|
|
bps = SND_PCM_FORMAT_U8;
|
|
|
|
snd->samplebits = 8;
|
[cvar] Make cvars properly typed
This is an extremely extensive patch as it hits every cvar, and every
usage of the cvars. Cvars no longer store the value they control,
instead, they use a cexpr value object to reference the value and
specify the value's type (currently, a null type is used for strings).
Non-string cvars are passed through cexpr, allowing expressions in the
cvars' settings. Also, cvars have returned to an enhanced version of the
original (id quake) registration scheme.
As a minor benefit, relevant code having direct access to the
cvar-controlled variables is probably a slight optimization as it
removed a pointer dereference, and the variables can be located for data
locality.
The static cvar descriptors are made private as an additional safety
layer, though there's nothing stopping external modification via
Cvar_FindVar (which is needed for adding listeners).
While not used yet (partly due to working out the design), cvars can
have a validation function.
Registering a cvar allows a primary listener (and its data) to be
specified: it will always be called first when the cvar is modified. The
combination of proper listeners and direct access to the controlled
variable greatly simplifies the more complex cvar interactions as much
less null checking is required, and there's no need for one cvar's
callback to call another's.
nq-x11 is known to work at least well enough for the demos. More testing
will come.
2022-04-23 03:22:45 +00:00
|
|
|
} else if (snd_bits) {
|
2021-06-25 00:50:31 +00:00
|
|
|
Sys_Printf ("snd_alsa: invalid sample bits: %d\n", bps);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bps) {
|
|
|
|
if ((res = qfsnd_pcm_hw_params_set_format (pcm, hw, bps)) == 0) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bps = SND_PCM_FORMAT_S16_LE;
|
|
|
|
if ((res = qfsnd_pcm_hw_params_set_format (pcm, hw, bps)) == 0) {
|
|
|
|
snd->samplebits = 16;
|
|
|
|
return 1;
|
2001-04-10 09:40:09 +00:00
|
|
|
}
|
2021-06-25 00:50:31 +00:00
|
|
|
bps = SND_PCM_FORMAT_U8;
|
|
|
|
if ((res = qfsnd_pcm_hw_params_set_format (pcm, hw, bps)) == 0) {
|
|
|
|
snd->samplebits = 8;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
Sys_Printf ("snd_alsa: no usable formats. %s\n", qfsnd_strerror (res));
|
2001-04-10 09:40:09 +00:00
|
|
|
}
|
2021-06-25 00:50:31 +00:00
|
|
|
snd->samplebits = -1;
|
|
|
|
Sys_Printf ("snd_alsa: desired format not supported\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
alsa_playback_set_channels (snd_t *snd, snd_pcm_hw_params_t *hw)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
int channels = 1;
|
|
|
|
|
[cvar] Make cvars properly typed
This is an extremely extensive patch as it hits every cvar, and every
usage of the cvars. Cvars no longer store the value they control,
instead, they use a cexpr value object to reference the value and
specify the value's type (currently, a null type is used for strings).
Non-string cvars are passed through cexpr, allowing expressions in the
cvars' settings. Also, cvars have returned to an enhanced version of the
original (id quake) registration scheme.
As a minor benefit, relevant code having direct access to the
cvar-controlled variables is probably a slight optimization as it
removed a pointer dereference, and the variables can be located for data
locality.
The static cvar descriptors are made private as an additional safety
layer, though there's nothing stopping external modification via
Cvar_FindVar (which is needed for adding listeners).
While not used yet (partly due to working out the design), cvars can
have a validation function.
Registering a cvar allows a primary listener (and its data) to be
specified: it will always be called first when the cvar is modified. The
combination of proper listeners and direct access to the controlled
variable greatly simplifies the more complex cvar interactions as much
less null checking is required, and there's no need for one cvar's
callback to call another's.
nq-x11 is known to work at least well enough for the demos. More testing
will come.
2022-04-23 03:22:45 +00:00
|
|
|
if (snd_stereo) {
|
2021-06-25 00:50:31 +00:00
|
|
|
channels = 2;
|
|
|
|
}
|
|
|
|
if ((res = qfsnd_pcm_hw_params_set_channels (pcm, hw, channels)) == 0) {
|
|
|
|
snd->channels = channels;
|
|
|
|
return 1;
|
2001-04-10 09:40:09 +00:00
|
|
|
}
|
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
Sys_Printf ("snd_alsa: desired channels not supported\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
alsa_playback_set_rate (snd_t *snd, snd_pcm_hw_params_t *hw)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
unsigned rate = 0;
|
|
|
|
static int default_rates[] = { 48000, 44100, 22050, 11025, 0 };
|
|
|
|
|
[cvar] Make cvars properly typed
This is an extremely extensive patch as it hits every cvar, and every
usage of the cvars. Cvars no longer store the value they control,
instead, they use a cexpr value object to reference the value and
specify the value's type (currently, a null type is used for strings).
Non-string cvars are passed through cexpr, allowing expressions in the
cvars' settings. Also, cvars have returned to an enhanced version of the
original (id quake) registration scheme.
As a minor benefit, relevant code having direct access to the
cvar-controlled variables is probably a slight optimization as it
removed a pointer dereference, and the variables can be located for data
locality.
The static cvar descriptors are made private as an additional safety
layer, though there's nothing stopping external modification via
Cvar_FindVar (which is needed for adding listeners).
While not used yet (partly due to working out the design), cvars can
have a validation function.
Registering a cvar allows a primary listener (and its data) to be
specified: it will always be called first when the cvar is modified. The
combination of proper listeners and direct access to the controlled
variable greatly simplifies the more complex cvar interactions as much
less null checking is required, and there's no need for one cvar's
callback to call another's.
nq-x11 is known to work at least well enough for the demos. More testing
will come.
2022-04-23 03:22:45 +00:00
|
|
|
if (snd_rate) {
|
|
|
|
rate = snd_rate;
|
2004-01-17 22:03:58 +00:00
|
|
|
}
|
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
if (rate) {
|
|
|
|
if ((res = qfsnd_pcm_hw_params_set_rate (pcm, hw, rate, 0)) == 0) {
|
|
|
|
snd->speed = rate;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
Sys_Printf ("snd_alsa: desired rate %i not supported. %s\n", rate,
|
|
|
|
qfsnd_strerror (res));
|
|
|
|
} else {
|
|
|
|
// use default rate
|
|
|
|
int dir = 0;
|
|
|
|
for (int *def_rate = default_rates; *def_rate; def_rate++) {
|
|
|
|
rate = *def_rate;
|
|
|
|
res = qfsnd_pcm_hw_params_set_rate_near (pcm, hw, &rate, &dir);
|
|
|
|
if (res == 0) {
|
|
|
|
snd->speed = rate;
|
|
|
|
return 1;
|
2011-12-08 02:32:33 +00:00
|
|
|
}
|
2011-09-07 08:00:57 +00:00
|
|
|
}
|
2021-06-25 00:50:31 +00:00
|
|
|
Sys_Printf ("snd_alsa: no usable rate\n");
|
2001-04-10 09:40:09 +00:00
|
|
|
}
|
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-09-07 08:00:57 +00:00
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
static int
|
|
|
|
alsa_playback_set_period_size (snd_t *snd, snd_pcm_hw_params_t *hw)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
snd_pcm_uframes_t period_size;
|
|
|
|
|
|
|
|
// works out to about 5.5ms (5.3 for 48k, 5.8 for 44k1) but consistent for
|
|
|
|
// different sample rates give or take rounding
|
|
|
|
period_size = 64 * (snd->speed / 11025);
|
|
|
|
res = qfsnd_pcm_hw_params_set_period_size_near (pcm, hw, &period_size, 0);
|
|
|
|
if (res == 0) {
|
|
|
|
// don't mix less than this in frames:
|
|
|
|
res = qfsnd_pcm_hw_params_get_period_size (hw, &period_size, 0);
|
|
|
|
if (res == 0) {
|
|
|
|
snd->submission_chunk = period_size;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
Sys_Printf ("snd_alsa: unable to get period size. %s\n",
|
|
|
|
qfsnd_strerror (res));
|
|
|
|
} else {
|
|
|
|
Sys_Printf ("snd_alsa: unable to set period size near %i. %s\n",
|
|
|
|
(int) period_size, qfsnd_strerror (res));
|
2001-04-10 09:40:09 +00:00
|
|
|
}
|
2021-06-25 00:50:31 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2001-04-10 09:40:09 +00:00
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
static int
|
|
|
|
SNDDMA_Init (snd_t *snd)
|
|
|
|
{
|
|
|
|
int res;
|
[cvar] Make cvars properly typed
This is an extremely extensive patch as it hits every cvar, and every
usage of the cvars. Cvars no longer store the value they control,
instead, they use a cexpr value object to reference the value and
specify the value's type (currently, a null type is used for strings).
Non-string cvars are passed through cexpr, allowing expressions in the
cvars' settings. Also, cvars have returned to an enhanced version of the
original (id quake) registration scheme.
As a minor benefit, relevant code having direct access to the
cvar-controlled variables is probably a slight optimization as it
removed a pointer dereference, and the variables can be located for data
locality.
The static cvar descriptors are made private as an additional safety
layer, though there's nothing stopping external modification via
Cvar_FindVar (which is needed for adding listeners).
While not used yet (partly due to working out the design), cvars can
have a validation function.
Registering a cvar allows a primary listener (and its data) to be
specified: it will always be called first when the cvar is modified. The
combination of proper listeners and direct access to the controlled
variable greatly simplifies the more complex cvar interactions as much
less null checking is required, and there's no need for one cvar's
callback to call another's.
nq-x11 is known to work at least well enough for the demos. More testing
will come.
2022-04-23 03:22:45 +00:00
|
|
|
const char *device = snd_device;
|
2021-06-25 00:50:31 +00:00
|
|
|
|
|
|
|
snd_pcm_hw_params_t *hw;
|
|
|
|
snd_pcm_sw_params_t *sw;
|
|
|
|
|
|
|
|
if (!load_libasound ())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
snd_pcm_hw_params_alloca (&hw);
|
|
|
|
snd_pcm_sw_params_alloca (&sw);
|
|
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
if (!alsa_open_playback (snd, device)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if ((res = qfsnd_pcm_hw_params_any (pcm, hw)) < 0) {
|
|
|
|
Sys_Printf ("snd_alsa: error setting hw_params_any. %s\n",
|
|
|
|
qfsnd_strerror (res));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (alsa_playback_set_mmap (snd, hw)) {
|
2004-01-17 22:03:58 +00:00
|
|
|
break;
|
2021-06-25 00:50:31 +00:00
|
|
|
}
|
|
|
|
if (*device) {
|
2001-04-10 09:40:09 +00:00
|
|
|
goto error;
|
2021-06-25 00:50:31 +00:00
|
|
|
}
|
|
|
|
qfsnd_pcm_close (pcm);
|
|
|
|
device = "plughw";
|
2001-04-10 09:40:09 +00:00
|
|
|
}
|
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
if (!alsa_playback_set_bps (snd, hw)) {
|
|
|
|
goto error;
|
2003-12-22 01:07:49 +00:00
|
|
|
}
|
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
if (!alsa_playback_set_channels (snd, hw)) {
|
2004-01-17 22:03:58 +00:00
|
|
|
goto error;
|
|
|
|
}
|
2021-06-25 00:50:31 +00:00
|
|
|
|
|
|
|
if (!alsa_playback_set_rate (snd, hw)) {
|
2004-01-17 22:03:58 +00:00
|
|
|
goto error;
|
|
|
|
}
|
2021-06-25 00:50:31 +00:00
|
|
|
|
|
|
|
if (!alsa_playback_set_period_size (snd, hw)) {
|
2004-01-17 22:03:58 +00:00
|
|
|
goto error;
|
|
|
|
}
|
2021-06-25 00:50:31 +00:00
|
|
|
|
|
|
|
if ((res = qfsnd_pcm_hw_params (pcm, hw)) < 0) {
|
|
|
|
Sys_Printf ("snd_alsa: unable to install hw params: %s\n",
|
|
|
|
qfsnd_strerror (res));
|
2004-01-17 22:03:58 +00:00
|
|
|
goto error;
|
|
|
|
}
|
2021-06-25 00:50:31 +00:00
|
|
|
|
|
|
|
if ((res = qfsnd_pcm_sw_params_current (pcm, sw)) < 0) {
|
|
|
|
Sys_Printf ("snd_alsa: unable to determine current sw params. %s\n",
|
|
|
|
qfsnd_strerror (res));
|
2001-04-10 09:40:09 +00:00
|
|
|
goto error;
|
|
|
|
}
|
2021-06-25 00:50:31 +00:00
|
|
|
if ((res = qfsnd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U)) < 0) {
|
|
|
|
Sys_Printf ("snd_alsa: unable to set playback threshold. %s\n",
|
|
|
|
qfsnd_strerror (res));
|
2001-04-10 09:40:09 +00:00
|
|
|
goto error;
|
|
|
|
}
|
2021-06-25 00:50:31 +00:00
|
|
|
if ((res = qfsnd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U)) < 0) {
|
|
|
|
Sys_Printf ("snd_alsa: unable to set playback stop threshold. %s\n",
|
|
|
|
qfsnd_strerror (res));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if ((res = qfsnd_pcm_sw_params (pcm, sw)) < 0) {
|
|
|
|
Sys_Printf ("snd_alsa: unable to install sw params. %s\n",
|
|
|
|
qfsnd_strerror (res));
|
2004-01-17 22:03:58 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
snd->framepos = 0;
|
2004-01-17 22:03:58 +00:00
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
if ((res = qfsnd_pcm_hw_params_get_buffer_size (hw, &buffer_size)) < 0) {
|
|
|
|
Sys_Printf ("snd_alsa: unable to get buffer size. %s\n",
|
|
|
|
qfsnd_strerror (res));
|
2004-01-17 22:03:58 +00:00
|
|
|
goto error;
|
|
|
|
}
|
2005-06-14 05:37:26 +00:00
|
|
|
if (buffer_size != round_buffer_size (buffer_size)) {
|
2021-06-25 00:50:31 +00:00
|
|
|
Sys_Printf ("snd_alsa: WARNING: non-power of 2 buffer size. sound may be unsatisfactory\n");
|
2005-08-12 02:39:37 +00:00
|
|
|
Sys_Printf ("recommend using either the plughw, or hw devices or adjusting dmix\n");
|
|
|
|
Sys_Printf ("to have a power of 2 buffer size\n");
|
2005-06-14 05:37:26 +00:00
|
|
|
}
|
2004-01-17 22:03:58 +00:00
|
|
|
|
2021-06-25 00:50:31 +00:00
|
|
|
if ((res = qfsnd_async_add_pcm_handler (&async_handler, pcm,
|
|
|
|
alsa_callback, snd)) < 0) {
|
|
|
|
Sys_Printf ("snd_alsa: unable to register async handler: %s",
|
|
|
|
qfsnd_strerror (res));
|
|
|
|
goto error;
|
|
|
|
}
|
2021-06-23 15:04:02 +00:00
|
|
|
|
|
|
|
snd->frames = buffer_size;
|
2021-06-25 02:41:42 +00:00
|
|
|
|
2022-06-05 09:08:49 +00:00
|
|
|
snd->threaded = 1;//XXX FIXME double check whether it's always true
|
|
|
|
|
2021-06-25 02:41:42 +00:00
|
|
|
// send the first period to fill the buffer
|
|
|
|
// also sets snd->buffer
|
|
|
|
if (alsa_process (pcm, snd) < 0) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
qfsnd_pcm_start (pcm);
|
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
Sys_Printf ("%5d channels %sinterleaved\n", snd->channels,
|
|
|
|
snd->xfer ? "non-" : "");
|
|
|
|
Sys_Printf ("%5d samples (%.1fms)\n", snd->frames,
|
|
|
|
1000.0 * snd->frames / snd->speed);
|
|
|
|
Sys_Printf ("%5d samplepos\n", snd->framepos);
|
|
|
|
Sys_Printf ("%5d samplebits\n", snd->samplebits);
|
|
|
|
Sys_Printf ("%5d submission_chunk (%.1fms)\n", snd->submission_chunk,
|
|
|
|
1000.0 * snd->submission_chunk / snd->speed);
|
|
|
|
Sys_Printf ("%5d speed\n", snd->speed);
|
|
|
|
Sys_Printf ("0x%lx dma buffer\n", (long) snd->buffer);
|
2001-04-10 09:40:09 +00:00
|
|
|
|
|
|
|
snd_inited = 1;
|
2021-06-23 15:04:02 +00:00
|
|
|
|
|
|
|
return 1;
|
2001-08-22 20:26:25 +00:00
|
|
|
error:
|
2002-04-19 20:08:54 +00:00
|
|
|
qfsnd_pcm_close (pcm);
|
2021-06-25 00:50:31 +00:00
|
|
|
snd->channels = 0;
|
|
|
|
snd->frames = 0;
|
|
|
|
snd->samplebits = 0;
|
|
|
|
snd->submission_chunk = 0;
|
|
|
|
snd->speed = 0;
|
2001-04-10 09:40:09 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-08-31 00:05:58 +00:00
|
|
|
static void
|
2021-06-23 15:04:02 +00:00
|
|
|
SNDDMA_shutdown (snd_t *snd)
|
2001-04-10 09:40:09 +00:00
|
|
|
{
|
|
|
|
if (snd_inited) {
|
2021-06-25 00:50:31 +00:00
|
|
|
qfsnd_async_del_handler (async_handler);
|
|
|
|
async_handler = 0;
|
2002-04-19 20:08:54 +00:00
|
|
|
qfsnd_pcm_close (pcm);
|
2001-04-10 09:40:09 +00:00
|
|
|
snd_inited = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-08-31 00:05:58 +00:00
|
|
|
static void
|
2021-06-23 15:04:02 +00:00
|
|
|
SNDDMA_BlockSound (snd_t *snd)
|
2001-08-23 04:01:46 +00:00
|
|
|
{
|
2002-09-30 03:59:55 +00:00
|
|
|
if (snd_inited && ++snd_blocked == 1)
|
2002-04-19 20:08:54 +00:00
|
|
|
qfsnd_pcm_pause (pcm, 1);
|
2001-08-23 04:01:46 +00:00
|
|
|
}
|
|
|
|
|
2001-08-31 00:05:58 +00:00
|
|
|
static void
|
2021-06-23 15:04:02 +00:00
|
|
|
SNDDMA_UnblockSound (snd_t *snd)
|
2001-08-23 04:01:46 +00:00
|
|
|
{
|
2002-09-30 03:59:55 +00:00
|
|
|
if (!snd_inited || !snd_blocked)
|
2001-08-23 04:01:46 +00:00
|
|
|
return;
|
|
|
|
if (!--snd_blocked)
|
2002-04-19 20:08:54 +00:00
|
|
|
qfsnd_pcm_pause (pcm, 0);
|
2001-08-23 04:01:46 +00:00
|
|
|
}
|
|
|
|
|
2021-06-25 02:41:42 +00:00
|
|
|
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"
|
2001-09-09 21:41:35 +00:00
|
|
|
"Copyright (C) 1999,2000,2001 contributors of the QuakeForge "
|
|
|
|
"project\n"
|
2021-06-25 02:41:42 +00:00
|
|
|
"Please see the file \"AUTHORS\" for a list of contributors",
|
|
|
|
.functions = &plugin_info_funcs,
|
|
|
|
.data = &plugin_info_data,
|
|
|
|
};
|
2001-05-19 00:05:35 +00:00
|
|
|
|
2021-06-25 02:41:42 +00:00
|
|
|
PLUGIN_INFO(snd_output, alsa)
|
|
|
|
{
|
2001-09-09 21:41:35 +00:00
|
|
|
return &plugin_info;
|
2001-05-19 00:05:35 +00:00
|
|
|
}
|