[sound] Clean up the public API

sfx_t is now private, and cd_file no longer accesses channel_t's
internals. This is necessary for hiding the code needed to make mixing
and channel management *properly* lock-free (I've been getting away with
murder thanks to x86's strong memory model and just plain luck with
gcc).
This commit is contained in:
Bill Currie 2022-06-03 15:20:41 +09:00
parent 0ebb0717b0
commit 1f16f875f6
6 changed files with 152 additions and 38 deletions

View file

@ -44,7 +44,12 @@ typedef struct snd_render_funcs_s {
void (*stop_sound) (int entnum, int entchannel);
struct channel_s *(*alloc_channel) (void);
void (*channel_stop) (struct channel_s *chan);
void (*channel_free) (struct channel_s *chan);
int (*channel_set_sfx) (struct channel_s *chan, struct sfx_s *sfx);
void (*channel_set_paused) (struct channel_s *chan, int paused);
void (*channel_set_looping) (struct channel_s *chan, int looping);
enum chan_state_e (*channel_get_state) (struct channel_s *chan);
void (*channel_set_volume) (struct channel_s *chan, float volume);
struct sfx_s *(*precache_sound) (const char *sample);
struct sfx_s *(*load_sound) (const char *name);

View file

@ -42,6 +42,15 @@ struct transform_s;
*/
///@{
typedef struct sfx_s sfx_t;
typedef struct channel_s channel_t;
typedef enum chan_state_e {
chan_invalid, //!< Channel is in an invalid state and must not be used
chan_pending, //!< Channel is waiting to be initialized
chan_done, //!< Channel is done and should be freed
chan_paused, //!< Channel is paused but can be resumed at any time
chan_playing, //!< Channel is currently playing
} chan_state;
///@}
/** \defgroup sound_init Initialization functions
@ -138,12 +147,24 @@ sfx_t *S_LoadSound (const char *name);
/** Allocate a sound channel that can be used for playing sounds.
*/
struct channel_s *S_AllocChannel (void);
channel_t *S_AllocChannel (void);
/** Stop and safely free a channel.
\param chan channel to stop
*/
void S_ChannelStop (struct channel_s *chan);
void S_ChannelFree (channel_t *chan);
int S_ChannelSetSfx (channel_t *chan, sfx_t *sfx);
void S_ChannelSetPaused (channel_t *chan, int paused);
void S_ChannelSetLooping (channel_t *chan, int looping);
chan_state S_ChannelGetState (channel_t *chan);
/** Set a channel's volume.
\param chan The channel for which the volume will be set.
\param volume The overall playback volume of the channel: set to 0 for
silence, 1 for full volume.
*/
void S_ChannelSetVolume (channel_t *chan, float volume);
/** Start a sound local to the client view.
\param s name of sound to play

View file

@ -48,7 +48,6 @@ struct transform_s;
typedef struct portable_samplepair_s portable_samplepair_t;
typedef struct snd_s snd_t;
typedef struct wavinfo_s wavinfo_t;
typedef struct channel_s channel_t;
typedef struct sfxbuffer_s sfxbuffer_t;
typedef struct sfxblock_s sfxblock_t;
typedef struct sfxstream_s sfxstream_t;
@ -224,7 +223,7 @@ struct sfxblock_s {
/** Representation of a sound being played.
*/
struct channel_s {
struct channel_s *next; //!< next channel in "free" list
channel_t *next; //!< next channel in "free" list
sfx_t *sfx; //!< sound played by this channel
int leftvol; //!< 0-255 volume
int rightvol; //!< 0-255 volume
@ -250,7 +249,7 @@ struct channel_s {
can be reused at any time.
*/
//@{
int stop;
int stop;
int done;
//@}
};
@ -342,7 +341,7 @@ extern int snd_total_channels; //!< number of active channels
/** Allocate a sound channel that can be used for playing sounds.
\param snd sound system state
*/
struct channel_s *SND_AllocChannel (snd_t *snd);
channel_t *SND_AllocChannel (snd_t *snd);
/** Stop a channel from playing.
\param snd sound system state

View file

@ -65,7 +65,6 @@
#include "QF/plugin/cd.h"
#include "compat.h"
#include "snd_internal.h"
/* Generic plugin structures */
static general_data_t plugin_info_general_data;
@ -107,11 +106,8 @@ static cvar_t mus_ogglist_cvar = {
static void
set_volume (void)
{
if (cd_channel && cd_channel->sfx) {
int vol = bgmvolume * 255;
cd_channel->master_vol = vol;
cd_channel->leftvol = cd_channel->rightvol = cd_channel->master_vol;
if (cd_channel) {
S_ChannelSetVolume (cd_channel, bgmvolume);
}
}
@ -136,7 +132,7 @@ I_OGGMus_Stop (void)
wasPlaying = false;
if (cd_channel) {
S_ChannelStop (cd_channel);
S_ChannelFree (cd_channel);
cd_channel = NULL;
}
}
@ -235,8 +231,7 @@ static void
I_OGGMus_PlayNext (int looping)
{
const char *track;
sfx_t *cd_sfx, *sfx;
wavinfo_t *info = 0;
sfx_t *sfx;
if (!play_list)
return;
@ -252,29 +247,21 @@ I_OGGMus_PlayNext (int looping)
}
if (cd_channel) {
S_ChannelStop (cd_channel);
S_ChannelFree (cd_channel);
cd_channel = 0;
}
if (!(cd_channel = S_AllocChannel ()))
return;
if (!(cd_sfx = S_LoadSound (track)) || !(sfx = cd_sfx->open (cd_sfx))) {
S_ChannelStop (cd_channel);
if (!(sfx = S_LoadSound (track)) || !S_ChannelSetSfx (cd_channel, sfx)) {
S_ChannelFree (cd_channel);
cd_channel = 0;
return;
}
Sys_Printf ("Playing: %s.\n", track);
if (sfx->wavinfo)
info = sfx->wavinfo (sfx);
if (info) {
if (looping == true)
info->loopstart = 0;
else
info->loopstart = -1;
}
cd_channel->sfx = sfx;
S_ChannelSetLooping (cd_channel, looping ? 1 : -1);
set_volume ();
Sys_Printf ("Playing: %s.\n", track);
playing = true;
}
@ -286,7 +273,7 @@ I_OGGMus_Pause (void)
return;
if (cd_channel)
cd_channel->pause = 1;
S_ChannelSetPaused (cd_channel, 1);
wasPlaying = playing;
playing = false;
@ -299,7 +286,7 @@ I_OGGMus_Resume (void)
return;
set_volume ();
cd_channel->pause = 0;
S_ChannelSetPaused (cd_channel, 0);
wasPlaying = false;
playing = true;
}
@ -446,7 +433,7 @@ I_OGG_f (void)
static void
I_OGGMus_Update (void)
{
if (!cd_channel || !cd_channel->done)
if (!cd_channel || S_ChannelGetState (cd_channel) > chan_done)
return;
// will get here only when multi-tracked
I_OGGMus_Stop ();

View file

@ -465,13 +465,67 @@ s_load_sound (const char *name)
}
static void
s_channel_stop (channel_t *chan)
s_channel_free (channel_t *chan)
{
if (!sound_started)
return;
SND_ChannelStop (&snd, chan);
}
static int
s_channel_set_sfx (channel_t *chan, sfx_t *sfx)
{
sfx_t *s = sfx->open (sfx);
if (!s) {
return 0;
}
chan->sfx = s;
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;
}
if (!chan->sfx) {
// channel requires initialization
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)
{
chan->master_vol = volume * 256; //FIXME
chan->leftvol = chan->rightvol = chan->master_vol;
}
static void
s_local_sound (const char *sound)
{
@ -506,7 +560,12 @@ static snd_render_funcs_t plugin_info_render_funcs = {
.stop_sound = s_stop_sound,
.alloc_channel = s_alloc_channel,
.channel_stop = s_channel_stop,
.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,
.precache_sound = s_precache_sound,
.load_sound = s_load_sound,

View file

@ -229,7 +229,7 @@ S_LoadSound (const char *name)
return 0;
}
VISIBLE struct channel_s *
VISIBLE channel_t *
S_AllocChannel (void)
{
if (snd_render_funcs)
@ -238,8 +238,51 @@ S_AllocChannel (void)
}
VISIBLE void
S_ChannelStop (struct channel_s *chan)
S_ChannelFree (channel_t *chan)
{
if (snd_render_funcs)
snd_render_funcs->channel_stop (chan);
if (snd_render_funcs) {
snd_render_funcs->channel_free (chan);
}
}
VISIBLE int
S_ChannelSetSfx (channel_t *chan, sfx_t *sfx)
{
if (snd_render_funcs) {
return snd_render_funcs->channel_set_sfx (chan, sfx);
}
return 0;
}
VISIBLE void
S_ChannelSetPaused (channel_t *chan, int paused)
{
if (snd_render_funcs) {
snd_render_funcs->channel_set_paused (chan, paused);
}
}
VISIBLE void
S_ChannelSetLooping (channel_t *chan, int looping)
{
if (snd_render_funcs) {
snd_render_funcs->channel_set_looping (chan, looping);
}
}
VISIBLE chan_state
S_ChannelGetState (channel_t *chan)
{
if (snd_render_funcs) {
return snd_render_funcs->channel_get_state (chan);
}
return 0;
}
VISIBLE void
S_ChannelSetVolume (channel_t *chan, float volume)
{
if (snd_render_funcs) {
snd_render_funcs->channel_set_volume (chan, volume);
}
}