2001-04-10 09:40:09 +00:00
|
|
|
/*
|
2021-06-23 15:04:02 +00:00
|
|
|
snd_default.c
|
2001-04-10 09:40:09 +00:00
|
|
|
|
|
|
|
main control for any streaming sound output device
|
|
|
|
|
|
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
|
|
|
|
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
|
2001-08-23 04:01:46 +00:00
|
|
|
Boston, MA 02111-1307, USA
|
2001-04-10 09:40:09 +00:00
|
|
|
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
2003-01-15 15:31:36 +00:00
|
|
|
|
2001-04-10 09:40:09 +00:00
|
|
|
#ifdef HAVE_STRING_H
|
|
|
|
# include <string.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_STRINGS_H
|
|
|
|
# include <strings.h>
|
|
|
|
#endif
|
2013-01-22 04:27:39 +00:00
|
|
|
#include <inttypes.h>
|
2001-04-10 09:40:09 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
2001-10-16 21:40:45 +00:00
|
|
|
#include "winquake.h"
|
2001-04-10 20:07:11 +00:00
|
|
|
|
2001-04-10 09:40:09 +00:00
|
|
|
#include "QF/cmd.h"
|
2001-05-31 03:41:35 +00:00
|
|
|
#include "QF/cvar.h"
|
2003-05-08 23:24:02 +00:00
|
|
|
#include "QF/dstring.h"
|
2001-04-10 09:40:09 +00:00
|
|
|
#include "QF/model.h"
|
|
|
|
#include "QF/qargs.h"
|
|
|
|
#include "QF/sys.h"
|
2001-04-10 21:45:42 +00:00
|
|
|
#include "QF/sound.h"
|
2001-05-19 00:05:35 +00:00
|
|
|
#include "QF/plugin.h"
|
2004-01-21 02:52:12 +00:00
|
|
|
#include "QF/va.h"
|
2004-02-03 03:01:06 +00:00
|
|
|
#include "QF/quakefs.h"
|
2001-04-10 09:40:09 +00:00
|
|
|
|
2011-07-23 06:56:22 +00:00
|
|
|
#include "snd_internal.h"
|
2003-01-31 20:51:04 +00:00
|
|
|
|
2004-01-08 01:48:02 +00:00
|
|
|
static int snd_blocked = 0;
|
2001-04-10 09:40:09 +00:00
|
|
|
|
2004-01-08 01:48:02 +00:00
|
|
|
static unsigned soundtime; // sample PAIRS
|
2001-04-10 09:40:09 +00:00
|
|
|
|
2004-01-08 01:48:02 +00:00
|
|
|
static int sound_started = 0;
|
2001-04-10 09:40:09 +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
|
|
|
float snd_volume;
|
|
|
|
static cvar_t snd_volume_cvar = {
|
|
|
|
.name = "volume",
|
|
|
|
.description =
|
|
|
|
"Set the volume for sound playback",
|
|
|
|
.default_value = "0.7",
|
|
|
|
.flags = CVAR_ARCHIVE,
|
|
|
|
.value = { .type = &cexpr_float, .value = &snd_volume },
|
|
|
|
};
|
2022-04-24 11:04:06 +00:00
|
|
|
static int nosound;
|
[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 cvar_t nosound_cvar = {
|
|
|
|
.name = "nosound",
|
|
|
|
.description =
|
|
|
|
"Set to turn sound off",
|
|
|
|
.default_value = "0",
|
|
|
|
.flags = CVAR_NONE,
|
2022-04-24 11:04:06 +00:00
|
|
|
.value = { .type = &cexpr_int, .value = &nosound },
|
[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 float snd_mixahead;
|
|
|
|
static cvar_t snd_mixahead_cvar = {
|
|
|
|
.name = "snd_mixahead",
|
|
|
|
.description =
|
|
|
|
"Delay time for sounds",
|
|
|
|
.default_value = "0.1",
|
|
|
|
.flags = CVAR_ARCHIVE,
|
|
|
|
.value = { .type = &cexpr_float, .value = &snd_mixahead },
|
|
|
|
};
|
|
|
|
static int snd_noextraupdate;
|
|
|
|
static cvar_t snd_noextraupdate_cvar = {
|
|
|
|
.name = "snd_noextraupdate",
|
|
|
|
.description =
|
|
|
|
"Toggles the correct value display in host_speeds. Usually messes up "
|
|
|
|
"sound playback when in effect",
|
|
|
|
.default_value = "0",
|
|
|
|
.flags = CVAR_NONE,
|
|
|
|
.value = { .type = &cexpr_int, .value = &snd_noextraupdate },
|
|
|
|
};
|
2022-04-24 11:04:06 +00:00
|
|
|
static int snd_show;
|
[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 cvar_t snd_show_cvar = {
|
|
|
|
.name = "snd_show",
|
|
|
|
.description =
|
|
|
|
"Toggles display of sounds currently being played",
|
|
|
|
.default_value = "0",
|
|
|
|
.flags = CVAR_NONE,
|
2022-04-24 11:04:06 +00:00
|
|
|
.value = { .type = &cexpr_int, .value = &snd_show },
|
[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
|
|
|
};
|
2001-08-23 04:01:46 +00:00
|
|
|
|
2004-01-08 01:48:02 +00:00
|
|
|
static general_data_t plugin_info_general_data;
|
2001-08-04 18:18:33 +00:00
|
|
|
|
2004-01-08 01:48:02 +00:00
|
|
|
static snd_output_funcs_t *snd_output_funcs;
|
2021-06-25 02:41:42 +00:00
|
|
|
static snd_output_data_t *snd_output_data;
|
2003-01-31 20:19:42 +00:00
|
|
|
|
2021-06-26 07:18:18 +00:00
|
|
|
static snd_t snd = {
|
|
|
|
.finish_channels = SND_FinishChannels,
|
|
|
|
.paint_channels = SND_PaintChannels,
|
|
|
|
};
|
2021-06-23 15:04:02 +00:00
|
|
|
static int snd_shutdown = 0;
|
|
|
|
|
2007-03-17 07:05:24 +00:00
|
|
|
static void
|
2021-06-23 15:04:02 +00:00
|
|
|
s_xfer_paint_buffer (snd_t *snd, portable_samplepair_t *paintbuffer, int count,
|
2011-09-08 09:10:09 +00:00
|
|
|
float volume)
|
2007-03-17 07:05:24 +00:00
|
|
|
{
|
2011-09-08 09:10:09 +00:00
|
|
|
int out_idx, out_max, step, val;
|
2010-08-11 23:44:34 +00:00
|
|
|
float *p;
|
2007-03-17 07:05:24 +00:00
|
|
|
|
2011-09-08 09:10:09 +00:00
|
|
|
p = (float *) paintbuffer;
|
2021-06-23 15:04:02 +00:00
|
|
|
count *= snd->channels;
|
|
|
|
out_max = (snd->frames * snd->channels) - 1;
|
2021-06-26 07:18:18 +00:00
|
|
|
out_idx = snd->paintedtime * snd->channels;
|
2011-06-04 06:47:15 +00:00
|
|
|
while (out_idx > out_max)
|
|
|
|
out_idx -= out_max + 1;
|
2021-06-23 15:04:02 +00:00
|
|
|
step = 3 - snd->channels;
|
2007-03-17 07:05:24 +00:00
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
if (snd->samplebits == 16) {
|
|
|
|
short *out = (short *) snd->buffer;
|
2007-03-17 07:05:24 +00:00
|
|
|
|
|
|
|
while (count--) {
|
2011-09-08 09:10:09 +00:00
|
|
|
val = (*p * volume) * 0x8000;
|
2007-03-17 07:05:24 +00:00
|
|
|
p += step;
|
|
|
|
if (val > 0x7fff)
|
|
|
|
val = 0x7fff;
|
2010-08-11 23:44:34 +00:00
|
|
|
else if (val < -0x8000)
|
|
|
|
val = -0x8000;
|
2011-06-04 06:47:15 +00:00
|
|
|
out[out_idx++] = val;
|
|
|
|
if (out_idx > out_max)
|
|
|
|
out_idx = 0;
|
2007-03-17 07:05:24 +00:00
|
|
|
}
|
2021-06-23 15:04:02 +00:00
|
|
|
} else if (snd->samplebits == 8) {
|
|
|
|
unsigned char *out = (unsigned char *) snd->buffer;
|
2007-03-17 07:05:24 +00:00
|
|
|
|
|
|
|
while (count--) {
|
2011-09-08 09:10:09 +00:00
|
|
|
val = (*p * volume) * 128;
|
2007-03-17 07:05:24 +00:00
|
|
|
p += step;
|
2010-08-11 23:44:34 +00:00
|
|
|
if (val > 0x7f)
|
|
|
|
val = 0x7f;
|
|
|
|
else if (val < -0x80)
|
|
|
|
val = -0x80;
|
2011-06-04 06:47:15 +00:00
|
|
|
out[out_idx++] = val + 0x80;
|
|
|
|
if (out_idx > out_max)
|
|
|
|
out_idx = 0;
|
2007-03-17 07:05:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-01-08 01:48:02 +00:00
|
|
|
static void
|
2021-06-23 15:04:02 +00:00
|
|
|
s_clear_buffer (snd_t *snd)
|
2004-01-08 01:48:02 +00:00
|
|
|
{
|
2004-03-01 11:51:01 +00:00
|
|
|
int clear, i;
|
2010-08-11 23:43:35 +00:00
|
|
|
int count;
|
2004-01-08 01:48:02 +00:00
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
if (!sound_started || !snd || !snd->buffer)
|
2004-01-08 01:48:02 +00:00
|
|
|
return;
|
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
if (snd->samplebits == 8)
|
2004-01-08 01:48:02 +00:00
|
|
|
clear = 0x80;
|
|
|
|
else
|
|
|
|
clear = 0;
|
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
count = snd->frames * snd->channels * snd->samplebits / 8;
|
2010-08-11 23:43:35 +00:00
|
|
|
for (i = 0; i < count; i++)
|
2021-06-23 15:04:02 +00:00
|
|
|
snd->buffer[i] = clear;
|
2001-04-10 09:40:09 +00:00
|
|
|
}
|
|
|
|
|
2004-01-08 01:48:02 +00:00
|
|
|
static void
|
2007-03-17 03:10:45 +00:00
|
|
|
s_stop_all_sounds (void)
|
2001-04-10 09:40:09 +00:00
|
|
|
{
|
2021-06-23 15:04:02 +00:00
|
|
|
SND_StopAllSounds (&snd);
|
|
|
|
SND_ScanChannels (&snd, 0);
|
|
|
|
s_clear_buffer (&snd);
|
2001-04-10 09:40:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
2021-06-25 02:41:42 +00:00
|
|
|
static void
|
2004-01-09 03:48:00 +00:00
|
|
|
s_get_soundtime (void)
|
2002-11-05 19:12:51 +00:00
|
|
|
{
|
2010-08-11 23:43:35 +00:00
|
|
|
int frames, framepos;
|
|
|
|
static int buffers, oldframepos;
|
2002-11-05 19:12:51 +00:00
|
|
|
|
2021-06-25 02:41:42 +00:00
|
|
|
frames = snd.frames;
|
2002-11-05 19:12:51 +00:00
|
|
|
|
|
|
|
// it is possible to miscount buffers if it has wrapped twice between
|
2004-01-09 03:48:00 +00:00
|
|
|
// calls to s_update. Oh well.
|
2021-06-25 02:41:42 +00:00
|
|
|
if ((framepos = snd_output_funcs->get_dma_pos (&snd)) == -1)
|
2003-03-10 21:28:18 +00:00
|
|
|
return;
|
2012-05-21 23:23:22 +00:00
|
|
|
|
2010-08-11 23:43:35 +00:00
|
|
|
if (framepos < oldframepos) {
|
2002-11-05 19:12:51 +00:00
|
|
|
buffers++; // buffer wrapped
|
|
|
|
|
2021-06-26 07:18:18 +00:00
|
|
|
if (snd.paintedtime > 0x40000000) { // time to chop things off to avoid
|
2002-11-05 19:12:51 +00:00
|
|
|
// 32 bit limits
|
|
|
|
buffers = 0;
|
2021-06-26 07:18:18 +00:00
|
|
|
snd.paintedtime = frames;
|
2007-03-17 03:10:45 +00:00
|
|
|
s_stop_all_sounds ();
|
2002-11-05 19:12:51 +00:00
|
|
|
}
|
|
|
|
}
|
2010-08-11 23:43:35 +00:00
|
|
|
oldframepos = framepos;
|
2002-11-05 19:12:51 +00:00
|
|
|
|
2010-08-11 23:43:35 +00:00
|
|
|
soundtime = buffers * frames + framepos;
|
2002-11-05 19:12:51 +00:00
|
|
|
}
|
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static void
|
2004-01-09 03:48:00 +00:00
|
|
|
s_update_ (void)
|
2002-11-05 19:12:51 +00:00
|
|
|
{
|
2003-04-17 00:01:48 +00:00
|
|
|
unsigned int endtime, samps;
|
2002-11-05 19:12:51 +00:00
|
|
|
|
|
|
|
if (!sound_started || (snd_blocked > 0))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Updates DMA time
|
2004-01-09 03:48:00 +00:00
|
|
|
s_get_soundtime ();
|
2002-11-05 19:12:51 +00:00
|
|
|
|
|
|
|
// check to make sure that we haven't overshot
|
2021-06-26 07:18:18 +00:00
|
|
|
if (snd.paintedtime < soundtime) {
|
2002-11-05 19:12:51 +00:00
|
|
|
// Sys_Printf ("S_Update_ : overflow\n");
|
2021-06-26 07:18:18 +00:00
|
|
|
snd.paintedtime = soundtime;
|
2002-11-05 19:12:51 +00:00
|
|
|
}
|
|
|
|
// mix ahead of current position
|
[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
|
|
|
endtime = soundtime + snd_mixahead * snd.speed;
|
2021-06-25 02:41:42 +00:00
|
|
|
samps = snd.frames;
|
2002-11-05 19:12:51 +00:00
|
|
|
if (endtime - soundtime > samps)
|
|
|
|
endtime = soundtime + samps;
|
|
|
|
|
2021-06-25 02:41:42 +00:00
|
|
|
SND_PaintChannels (&snd, endtime);
|
|
|
|
snd_output_funcs->submit (&snd);
|
|
|
|
}
|
2002-11-05 19:12:51 +00:00
|
|
|
|
2001-04-10 09:40:09 +00:00
|
|
|
/*
|
2004-01-09 03:48:00 +00:00
|
|
|
s_update
|
2001-04-10 09:40:09 +00:00
|
|
|
|
|
|
|
Called once each time through the main loop
|
|
|
|
*/
|
2004-01-08 01:48:02 +00:00
|
|
|
static void
|
2022-02-28 07:59:38 +00:00
|
|
|
s_update (struct transform_s *ear, const byte *ambient_sound_level)
|
2001-04-10 09:40:09 +00:00
|
|
|
{
|
|
|
|
if (!sound_started || (snd_blocked > 0))
|
|
|
|
return;
|
|
|
|
|
2021-06-25 04:52:50 +00:00
|
|
|
if (snd_output_funcs->on_update) {
|
|
|
|
snd_output_funcs->on_update (&snd);
|
|
|
|
}
|
|
|
|
|
2022-02-28 07:59:38 +00:00
|
|
|
SND_SetListener (&snd, ear, ambient_sound_level);
|
2001-05-22 12:31:43 +00:00
|
|
|
|
2021-06-25 02:41:42 +00:00
|
|
|
if (snd_output_data->model == som_push) {
|
|
|
|
// mix some sound
|
|
|
|
s_update_ ();
|
|
|
|
SND_ScanChannels (&snd, 0);
|
|
|
|
}
|
2001-04-10 09:40:09 +00:00
|
|
|
}
|
|
|
|
|
2021-06-25 02:41:42 +00:00
|
|
|
static void
|
|
|
|
s_extra_update (void)
|
|
|
|
{
|
2021-06-25 05:23:55 +00:00
|
|
|
if (snd_output_data->model == som_push) {
|
[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 (!sound_started || snd_noextraupdate)
|
2021-06-25 05:23:55 +00:00
|
|
|
return; // don't pollute timings
|
|
|
|
s_update_ ();
|
|
|
|
}
|
2021-06-25 02:41:42 +00:00
|
|
|
}
|
2001-04-10 09:40:09 +00:00
|
|
|
|
2007-03-10 14:17:52 +00:00
|
|
|
static void
|
|
|
|
s_block_sound (void)
|
|
|
|
{
|
|
|
|
if (++snd_blocked == 1) {
|
2021-06-23 15:04:02 +00:00
|
|
|
snd_output_funcs->block_sound (&snd);
|
|
|
|
s_clear_buffer (&snd);
|
2007-03-10 14:17:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
s_unblock_sound (void)
|
|
|
|
{
|
|
|
|
if (!snd_blocked)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!--snd_blocked) {
|
2021-06-23 15:04:02 +00:00
|
|
|
s_clear_buffer (&snd);
|
|
|
|
snd_output_funcs->unblock_sound (&snd);
|
2007-03-10 14:17:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-08-27 01:00:03 +00:00
|
|
|
/* console functions */
|
2001-04-10 09:40:09 +00:00
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static void
|
2007-03-10 14:17:52 +00:00
|
|
|
s_soundinfo_f (void)
|
|
|
|
{
|
2021-06-23 15:04:02 +00:00
|
|
|
if (!sound_started) {
|
2007-03-10 14:17:52 +00:00
|
|
|
Sys_Printf ("sound system not started\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
Sys_Printf ("%5d channels\n", snd.channels);
|
|
|
|
Sys_Printf ("%5d frames (%.1fms)\n", snd.frames,
|
|
|
|
1000.0 * snd.frames / snd.speed);
|
|
|
|
Sys_Printf ("%5d framepos\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%"PRIxPTR" dma buffer\n", (intptr_t) snd.buffer);
|
2007-03-10 14:17:52 +00:00
|
|
|
Sys_Printf ("%5d total_channels\n", snd_total_channels);
|
|
|
|
}
|
|
|
|
|
2004-01-08 01:48:02 +00:00
|
|
|
static void
|
2007-03-10 14:17:52 +00:00
|
|
|
s_stop_all_sounds_f (void)
|
2001-04-10 09:40:09 +00:00
|
|
|
{
|
2007-03-17 03:10:45 +00:00
|
|
|
s_stop_all_sounds ();
|
2001-04-10 09:40:09 +00:00
|
|
|
}
|
|
|
|
|
2004-01-08 01:48:02 +00:00
|
|
|
static void
|
2007-03-10 14:17:52 +00:00
|
|
|
s_startup (void)
|
2001-04-10 09:40:09 +00:00
|
|
|
{
|
2022-06-04 09:45:11 +00:00
|
|
|
if (!SND_Memory_Init ()) {
|
2007-03-10 14:17:52 +00:00
|
|
|
return;
|
2022-06-04 09:45:11 +00:00
|
|
|
}
|
2021-06-23 15:04:02 +00:00
|
|
|
if (!snd_output_funcs->init (&snd)) {
|
2022-06-04 09:45:11 +00:00
|
|
|
Sys_Printf ("S_Startup: output init failed.\n");
|
2001-07-05 20:18:23 +00:00
|
|
|
return;
|
2003-01-31 19:55:21 +00:00
|
|
|
}
|
2021-06-23 15:04:02 +00:00
|
|
|
if (!snd.xfer)
|
|
|
|
snd.xfer = s_xfer_paint_buffer;
|
2001-08-23 04:01:46 +00:00
|
|
|
|
2007-03-10 14:17:52 +00:00
|
|
|
sound_started = 1;
|
2004-02-03 03:01:06 +00:00
|
|
|
}
|
|
|
|
|
2007-03-19 22:20:13 +00:00
|
|
|
static void
|
|
|
|
s_snd_force_unblock (void)
|
|
|
|
{
|
|
|
|
snd_blocked = 1;
|
|
|
|
s_unblock_sound ();
|
|
|
|
}
|
|
|
|
|
2021-06-26 06:43:17 +00:00
|
|
|
static void
|
|
|
|
s_init_cvars (void)
|
|
|
|
{
|
[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 (&nosound_cvar, 0, 0);
|
|
|
|
Cvar_Register (&snd_volume_cvar, 0, 0);
|
|
|
|
Cvar_Register (&snd_mixahead_cvar, 0, 0);
|
|
|
|
Cvar_Register (&snd_noextraupdate_cvar, 0, 0);
|
|
|
|
Cvar_Register (&snd_show_cvar, 0, 0);
|
2022-06-04 09:45:11 +00:00
|
|
|
|
|
|
|
SND_Memory_Init_Cvars ();
|
2021-06-26 06:43:17 +00:00
|
|
|
}
|
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static void
|
2004-01-09 03:48:00 +00:00
|
|
|
s_init (void)
|
2002-11-05 19:12:51 +00:00
|
|
|
{
|
2007-03-17 03:10:45 +00:00
|
|
|
snd_output_funcs = snd_render_data.output->functions->snd_output;
|
2021-06-25 02:41:42 +00:00
|
|
|
snd_output_data = snd_render_data.output->data->snd_output;
|
2007-03-17 06:20:52 +00:00
|
|
|
snd_render_data.soundtime = &soundtime;
|
2004-01-09 03:48:00 +00:00
|
|
|
Sys_Printf ("\nSound Initialization\n");
|
|
|
|
|
|
|
|
Cmd_AddCommand ("stopsound", s_stop_all_sounds_f,
|
|
|
|
"Stops all sounds currently being played");
|
|
|
|
Cmd_AddCommand ("soundinfo", s_soundinfo_f,
|
|
|
|
"Report information on the sound system");
|
2007-03-19 22:20:13 +00:00
|
|
|
Cmd_AddCommand ("snd_force_unblock", s_snd_force_unblock,
|
|
|
|
"fix permanently blocked sound");
|
2004-01-09 03:48:00 +00:00
|
|
|
|
|
|
|
s_startup ();
|
2002-11-05 19:12:51 +00:00
|
|
|
|
|
|
|
if (sound_started == 0) // sound startup failed? Bail out.
|
|
|
|
return;
|
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
SND_SFX_Init (&snd);
|
|
|
|
SND_Channels_Init (&snd);
|
2002-11-05 19:12:51 +00:00
|
|
|
|
2007-03-17 03:10:45 +00:00
|
|
|
s_stop_all_sounds ();
|
2002-11-05 19:12:51 +00:00
|
|
|
}
|
|
|
|
|
2004-01-08 01:48:02 +00:00
|
|
|
static void
|
2004-01-09 03:48:00 +00:00
|
|
|
s_shutdown (void)
|
2002-11-05 19:12:51 +00:00
|
|
|
{
|
|
|
|
if (!sound_started)
|
|
|
|
return;
|
|
|
|
|
|
|
|
sound_started = 0;
|
2021-06-23 15:04:02 +00:00
|
|
|
snd_shutdown = 1;
|
|
|
|
|
|
|
|
snd_output_funcs->shutdown (&snd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
s_ambient_off (void)
|
|
|
|
{
|
|
|
|
if (!sound_started)
|
|
|
|
return;
|
|
|
|
SND_AmbientOff (&snd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
s_ambient_on (void)
|
|
|
|
{
|
|
|
|
if (!sound_started)
|
|
|
|
return;
|
|
|
|
SND_AmbientOn (&snd);
|
|
|
|
}
|
2002-11-05 19:12:51 +00:00
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
static void
|
2022-03-30 14:38:01 +00:00
|
|
|
s_static_sound (sfx_t *sfx, vec4f_t origin, float vol,
|
2021-06-23 15:04:02 +00:00
|
|
|
float attenuation)
|
|
|
|
{
|
|
|
|
if (!sound_started)
|
|
|
|
return;
|
|
|
|
SND_StaticSound (&snd, sfx, origin, vol, attenuation);
|
|
|
|
}
|
2002-11-05 19:12:51 +00:00
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
static void
|
2022-03-30 14:38:01 +00:00
|
|
|
s_start_sound (int entnum, int entchannel, sfx_t *sfx, vec4f_t origin,
|
2021-06-25 07:52:09 +00:00
|
|
|
float vol, float attenuation)
|
2021-06-23 15:04:02 +00:00
|
|
|
{
|
|
|
|
if (!sound_started)
|
|
|
|
return;
|
|
|
|
if (!snd_shutdown)
|
2021-06-25 07:52:09 +00:00
|
|
|
SND_StartSound (&snd, entnum, entchannel, sfx, origin, vol,
|
2021-06-23 15:04:02 +00:00
|
|
|
attenuation);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
s_stop_sound (int entnum, int entchannel)
|
|
|
|
{
|
|
|
|
if (!sound_started)
|
|
|
|
return;
|
|
|
|
SND_StopSound (&snd, entnum, entchannel);
|
|
|
|
}
|
|
|
|
|
|
|
|
static sfx_t *
|
|
|
|
s_precache_sound (const char *name)
|
|
|
|
{
|
|
|
|
if (!sound_started)
|
|
|
|
return 0;
|
|
|
|
return SND_PrecacheSound (&snd, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static sfx_t *
|
|
|
|
s_load_sound (const char *name)
|
|
|
|
{
|
|
|
|
if (!sound_started)
|
|
|
|
return 0;
|
|
|
|
return SND_LoadSound (&snd, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2022-06-03 06:20:41 +00:00
|
|
|
s_channel_free (channel_t *chan)
|
2021-06-23 15:04:02 +00:00
|
|
|
{
|
|
|
|
if (!sound_started)
|
|
|
|
return;
|
|
|
|
SND_ChannelStop (&snd, chan);
|
|
|
|
}
|
|
|
|
|
2022-06-03 06:20:41 +00:00
|
|
|
static int
|
|
|
|
s_channel_set_sfx (channel_t *chan, sfx_t *sfx)
|
|
|
|
{
|
2022-06-05 07:57:13 +00:00
|
|
|
sfxbuffer_t *buffer = sfx->open (sfx);
|
|
|
|
if (!buffer) {
|
2022-06-03 06:20:41 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2022-06-05 07:57:13 +00:00
|
|
|
chan->buffer = buffer;
|
2022-06-03 06:20:41 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
s_channel_set_paused (channel_t *chan, int paused)
|
|
|
|
{
|
|
|
|
chan->pause = paused != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
s_channel_set_looping (channel_t *chan, int looping)
|
|
|
|
{
|
|
|
|
// FIXME implement
|
|
|
|
}
|
|
|
|
|
|
|
|
static chan_state
|
|
|
|
s_channel_get_state (channel_t *chan)
|
|
|
|
{
|
|
|
|
// stop means the channel has been "freed" and is waiting for the mixer
|
|
|
|
// thread to be done with it, thus putting the channel in an invalid state
|
|
|
|
// from the user's point of view. ie, don't touch (user should set channel
|
|
|
|
// pointer to null).
|
|
|
|
if (!chan->stop) {
|
|
|
|
if (chan->done) {
|
|
|
|
// The mixer has finished mixing the channel (come to the end).
|
|
|
|
return chan_done;
|
|
|
|
}
|
2022-06-05 07:57:13 +00:00
|
|
|
if (!chan->buffer) {
|
|
|
|
// channel has not been started yet
|
2022-06-03 06:20:41 +00:00
|
|
|
return chan_pending;
|
|
|
|
}
|
|
|
|
if (chan->pause) {
|
|
|
|
return chan_paused;
|
|
|
|
}
|
|
|
|
return chan_playing;
|
|
|
|
}
|
|
|
|
return chan_invalid;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
s_channel_set_volume (channel_t *chan, float volume)
|
|
|
|
{
|
2022-06-04 07:17:43 +00:00
|
|
|
SND_ChannelSetVolume (chan, volume);
|
2022-06-03 06:20:41 +00:00
|
|
|
}
|
|
|
|
|
2021-06-23 15:04:02 +00:00
|
|
|
static void
|
|
|
|
s_local_sound (const char *sound)
|
|
|
|
{
|
|
|
|
if (!sound_started)
|
|
|
|
return;
|
|
|
|
if (!snd_shutdown)
|
|
|
|
SND_LocalSound (&snd, sound);
|
|
|
|
}
|
|
|
|
|
|
|
|
static channel_t *
|
|
|
|
s_alloc_channel (void)
|
|
|
|
{
|
|
|
|
if (!sound_started)
|
|
|
|
return 0;
|
|
|
|
if (!snd_shutdown)
|
|
|
|
return SND_AllocChannel (&snd);
|
|
|
|
return 0;
|
2002-11-05 19:12:51 +00:00
|
|
|
}
|
|
|
|
|
2003-04-20 02:05:28 +00:00
|
|
|
static general_funcs_t plugin_info_general_funcs = {
|
2021-06-26 06:43:17 +00:00
|
|
|
.init = s_init_cvars,
|
|
|
|
.shutdown = s_shutdown,
|
2003-04-20 02:05:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static snd_render_funcs_t plugin_info_render_funcs = {
|
2021-06-26 06:43:17 +00:00
|
|
|
.init = s_init,
|
2021-06-23 15:04:02 +00:00
|
|
|
.ambient_off = s_ambient_off,
|
|
|
|
.ambient_on = s_ambient_on,
|
|
|
|
.static_sound = s_static_sound,
|
|
|
|
.start_sound = s_start_sound,
|
|
|
|
.local_sound = s_local_sound,
|
|
|
|
.stop_sound = s_stop_sound,
|
|
|
|
|
|
|
|
.alloc_channel = s_alloc_channel,
|
2022-06-03 06:20:41 +00:00
|
|
|
.channel_free = s_channel_free,
|
|
|
|
.channel_set_sfx = s_channel_set_sfx,
|
|
|
|
.channel_set_paused = s_channel_set_paused,
|
|
|
|
.channel_set_looping = s_channel_set_looping,
|
|
|
|
.channel_get_state = s_channel_get_state,
|
|
|
|
.channel_set_volume = s_channel_set_volume,
|
2021-06-23 15:04:02 +00:00
|
|
|
|
|
|
|
.precache_sound = s_precache_sound,
|
|
|
|
.load_sound = s_load_sound,
|
|
|
|
|
|
|
|
.update = s_update,
|
2021-06-22 06:35:37 +00:00
|
|
|
.stop_all_sounds = s_stop_all_sounds,
|
2021-06-25 02:41:42 +00:00
|
|
|
.extra_update = s_extra_update,
|
2021-06-23 15:04:02 +00:00
|
|
|
.block_sound = s_block_sound,
|
|
|
|
.unblock_sound = s_unblock_sound,
|
2003-04-20 02:05:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static plugin_funcs_t plugin_info_funcs = {
|
2021-06-25 07:52:09 +00:00
|
|
|
.general = &plugin_info_general_funcs,
|
|
|
|
.snd_render = &plugin_info_render_funcs,
|
2003-04-20 02:05:28 +00:00
|
|
|
};
|
|
|
|
|
2021-06-26 07:18:18 +00:00
|
|
|
snd_render_data_t snd_render_data = {
|
|
|
|
.paintedtime = &snd.paintedtime,
|
|
|
|
};
|
|
|
|
|
2003-04-20 02:05:28 +00:00
|
|
|
static plugin_data_t plugin_info_data = {
|
2021-06-25 07:52:09 +00:00
|
|
|
.general = &plugin_info_general_data,
|
|
|
|
.snd_render = &snd_render_data,
|
2003-04-20 02:05:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static plugin_t plugin_info = {
|
|
|
|
qfp_snd_render,
|
|
|
|
0,
|
|
|
|
QFPLUGIN_VERSION,
|
|
|
|
"0.1",
|
|
|
|
"Sound Renderer",
|
|
|
|
"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_funcs,
|
|
|
|
&plugin_info_data,
|
|
|
|
};
|
|
|
|
|
2003-08-01 19:53:46 +00:00
|
|
|
PLUGIN_INFO(snd_render, default)
|
2001-09-09 21:41:35 +00:00
|
|
|
{
|
2001-08-23 04:01:46 +00:00
|
|
|
return &plugin_info;
|
2001-07-05 20:18:23 +00:00
|
|
|
}
|