add an interface for sound libraries

Groundwork for a more versatile interface for using
sound libraries. A lot of sound libraries work across
multiple platforms.

The current NetHack sound stuff is quite limited.

Binaries can have a variety of window ports linked into
them, and it makes sense to have something similar for
sound.

This tries to set things up in a more soundlib-centric way,
rather than inserting things in a platform-centric way.

It establishes a new top-level directory sound (akin to win
for the window interface routines, or "window-port") where
sound-related additions and sndprocs and support files can be
added and used across platforms.

The default interface is nosound and the 'nosound' interface
is in src/sounds.c

The interface for 'windsound', which contains the same minimal
USER_SOUNDS support using built-in routines that has been in the
windows port for a long time is added to
sound/windsound/windsound.c.

For now, the sound interface support for 'qtsound' has been added
to the existing Qt files win/Qt/qt_bind.h and win/Qt/qt_bind.cpp,
and a note has been placed in sound/qtsound/README.md to avoid
confusion.

New header file added: include/sndprocs.h.
This commit is contained in:
nhmall
2023-01-19 18:51:42 -05:00
parent 6e49372d8f
commit ea4a81901d
24 changed files with 705 additions and 85 deletions

View File

@@ -58,6 +58,8 @@ moveloop_preamble(boolean resuming)
change_luck(-1);
}
activate_chosen_soundlib();
if (!resuming) { /* new game */
gc.context.rndencode = rnd(9000);
set_wear((struct obj *) 0); /* for side-effects of starting gear */

View File

@@ -206,6 +206,9 @@ const struct instance_globals_a g_init_a = {
UNDEFINED_VALUE, /* abort_looting */
/* shk.c */
FALSE, /* auto_credit */
/* sounds.c */
soundlib_unassigned, /* enum soundlib_ids active_soundlib */
/* trap.c */
{ 0, 0, FALSE }, /* acid_ctx */
TRUE, /* havestate*/
@@ -284,6 +287,8 @@ const struct instance_globals_c g_init_c = {
FALSE, /* class_filter */
/* questpgr.c */
UNDEFINED_VALUES, /* cvt_buf */
/* sounds.c */
soundlib_nosound, /* chosen_soundlib */
UNDEFINED_PTR, /* coder */
/* uhitm.c */
NON_PM, /* corpsenm_digested */

View File

@@ -2309,6 +2309,7 @@ void
record_achievement(schar achidx)
{
int i, absidx;
int repeat_achievement = 0;
absidx = abs(achidx);
/* valid achievements range from 1 to N_ACH-1; however, ranks can be
@@ -2325,8 +2326,21 @@ record_achievement(schar achidx)
an attempt to duplicate an achievement can happen if any of Bell,
Candelabrum, Book, or Amulet is dropped then picked up again */
for (i = 0; u.uachieved[i]; ++i)
if (abs(u.uachieved[i]) == absidx)
return; /* already recorded, don't duplicate it */
if (abs(u.uachieved[i]) == absidx) {
repeat_achievement = 1;
break;
}
/*
* We do the sound for an achievement, even if it has already been
* achieved before. Some players might have set up level-based
* theme music or something. We do let the sound interface know
* that it's not the original achievement though.
*/
(*soundprocs.sound_achievement)(achidx, 0, repeat_achievement);
if (repeat_achievement)
return; /* already recorded, don't duplicate it */
u.uachieved[i] = achidx;
/* avoid livelog for achievements recorded during final disclosure:

View File

@@ -3399,6 +3399,43 @@ optfn_scroll_margin(
return optn_ok;
}
static int
optfn_soundlib(int optidx, int req, boolean negated UNUSED,
char *opts, char *op)
{
char soundlibbuf[WINTYPELEN];
if (req == do_init) {
return optn_ok;
}
if (req == do_set) {
/*
* soundlib: option to choose the interface for binaries built
* with support for more than the default interface (nosound).
*
* Option processing sets gc.chosen_soundlib. A later call
* to activate_chosen_soundlib() actually activates it, and
* sets gc.active_soundlib.
*/
if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
!= empty_optstr) {
get_soundlib_name(soundlibbuf, WINTYPELEN);
assign_soundlib(gc.chosen_soundlib);
} else
return optn_err;
return optn_ok;
}
if (req == get_val || req == get_cnf_val) {
if (!opts)
return optn_err;
get_soundlib_name(soundlibbuf, WINTYPELEN);
Sprintf(opts, "%s", soundlibbuf);
return optn_ok;
}
return optn_ok;
}
static int
optfn_sortdiscoveries(
int optidx, int req, boolean negated,

View File

@@ -1502,37 +1502,27 @@ sound_matches_message(const char* msg)
void
play_sound_for_message(const char* msg)
{
audio_mapping *snd = sound_matches_message(msg);
audio_mapping *snd;
if (snd)
play_usersound(snd->filename, snd->volume);
if (soundprocs.sound_play_usersound) {
snd = sound_matches_message(msg);
if (snd)
(*soundprocs.sound_play_usersound)(
snd->filename, snd->volume, snd->idx);
}
}
void
maybe_play_sound(const char* msg)
{
#if defined(WIN32) || defined(QT_GRAPHICS) || defined(TTY_SOUND_ESCCODES)
audio_mapping *snd = sound_matches_message(msg);
audio_mapping *snd;
if (snd
#if defined(WIN32) || defined(QT_GRAPHICS)
#ifdef TTY_SOUND_ESCCODES
&& !iflags.vt_sounddata
#endif
#if defined(QT_GRAPHICS)
&& WINDOWPORT(Qt)
#endif
#if defined(WIN32)
&& (WINDOWPORT(tty) || WINDOWPORT(mswin) || WINDOWPORT(curses))
#endif
#endif /* WIN32 || QT_GRAPHICS */
)
play_usersound(snd->filename, snd->volume);
#if defined(TTY_GRAPHICS) && defined(TTY_SOUND_ESCCODES)
else if (snd && iflags.vt_sounddata && snd->idx >= 0 && WINDOWPORT(tty))
play_usersound_via_idx(snd->idx, snd->volume);
#endif /* TTY_GRAPHICS && TTY_SOUND_ESCCODES */
#endif /* WIN32 || QT_GRAPHICS || TTY_SOUND_ESCCODES */
if (soundprocs.sound_play_usersound) {
snd = sound_matches_message(msg);
if (snd)
(*soundprocs.sound_play_usersound)(
snd->filename, snd->volume, snd->idx);
}
}
void
@@ -1551,7 +1541,236 @@ release_sound_mappings(void)
if (sounddir)
free((genericptr_t) sounddir), sounddir = 0;
}
#endif /* USER_SOUNDS */
struct sound_procs soundprocs;
#ifdef SND_LIB_QTSOUND
extern struct sound_procs qtsound_procs;
#endif
#ifdef SND_LIB_PORTAUDIO
extern struct sound_procs portaudio_procs;
#endif
#ifdef SND_LIB_OPENAL
extern struct sound_procs openal_procs;
#endif
#ifdef SND_LIB_SDL_MIXER
extern struct sound_procs sdl_mixer_procs;
#endif
#ifdef SND_LIB_MINIAUDIO
extern struct sound_procs miniaudio_procs;
#endif
#ifdef SND_LIB_FMOD
extern struct sound_procs fmod_procs;
#endif
#ifdef SND_LIB_SOUND_ESCCODES
extern struct sound_procs esccodes_procs;
#endif
#ifdef SND_LIB_VISSOUND
extern struct sound_procs vissound_procs;
#endif
#ifdef SND_LIB_WINDSOUND
extern struct sound_procs windsound_procs;
#endif
extern struct sound_procs nosound_procs;
/* The order of these array entries must match the
order of the enum soundlib_ids in sndprocs.h */
static struct sound_choices {
struct sound_procs *sndprocs;
} soundlib_choices[] = {
{ (struct sound_procs *) 0 },
#ifdef SND_LIB_QTSOUND
{ &qtsound_procs },
#endif
#ifdef SND_LIB_PORTAUDIO
{ &portaudio_procs },
#endif
#ifdef SND_LIB_OPENAL
{ &openal_procs },
#endif
#ifdef SND_LIB_SDL_MIXER
{ &sdl_mixer_procs },
#endif
#ifdef SND_LIB_MINIAUDIO
{ &miniaudio_procs },
#endif
#ifdef SND_LIB_FMOD
{ &fmod_procs },
#endif
#ifdef SND_LIB_SOUND_ESCCODES
{ &esccodes_procs },
#endif
#ifdef SND_LIB_WINDSOUND
{ &windsound_procs },
#endif
#ifdef SND_LIB_VISSOUND
{ &vissound_procs },
#endif
{ &nosound_procs }, /* default, built-in */
};
void
activate_chosen_soundlib(void)
{
enum soundlib_ids idx = gc.chosen_soundlib;
if (idx <= soundlib_unassigned || idx > soundlib_nosound)
idx = soundlib_nosound;
if (ga.active_soundlib != soundlib_unassigned
|| ga.active_soundlib != idx) {
if (soundprocs.sound_exit_nhsound)
(*soundprocs.sound_exit_nhsound)("assigning a new sound library");
ga.active_soundlib = soundlib_unassigned;
}
soundprocs = *soundlib_choices[idx].sndprocs;
if (soundprocs.sound_init_nhsound)
(*soundprocs.sound_init_nhsound)();
ga.active_soundlib = soundprocs.soundlib_id;
}
void
assign_soundlib(int idx)
{
if (idx <= soundlib_unassigned || idx > soundlib_nosound)
idx = soundlib_nosound;
if (ga.active_soundlib != soundlib_unassigned) {
if (soundprocs.sound_exit_nhsound)
(*soundprocs.sound_exit_nhsound)("assigning a new sound library");
ga.active_soundlib = soundlib_unassigned;
}
gc.chosen_soundlib = soundlib_choices[idx].sndprocs->soundlib_id;
}
#if 0
static void
choose_soundlib(const char *s)
{
int i;
char *tmps = 0;
for (i = 1; soundlib_choices[i].sndprocs; i++) {
if (!strcmpi(s, soundlib_choices[i].sndprocs->soundname)) {
assign_soundlib(i);
return;
}
}
assign_soundlib((int) soundlib_nosound);
/* The code below here mimics that in windows.c error handling
for choosing Window type */
/* 50: arbitrary, no real soundlib names are anywhere near that long;
used to prevent potential raw_printf() overflow if user supplies a
very long string (on the order of 1200 chars) on the command line
(config file options can't get that big; they're truncated at 1023) */
#define SOUNDLIB_NAME_MAXLEN 50
if (strlen(s) >= SOUNDLIB_NAME_MAXLEN) {
tmps = (char *) alloc(SOUNDLIB_NAME_MAXLEN);
(void) strncpy(tmps, s, SOUNDLIB_NAME_MAXLEN - 1);
tmps[SOUNDLIB_NAME_MAXLEN - 1] = '\0';
s = tmps;
}
#undef SOUNDLIB_NAME_MAXLEN
if (!soundlib_choices[1].sndprocs) {
config_error_add(
"Soundlib type %s not recognized. The only choice is: %s",
s, soundlib_choices[0].sndprocs->soundname);
} else {
char buf[BUFSZ];
boolean first = TRUE;
buf[0] = '\0';
for (i = 0; soundlib_choices[i].sndprocs; i++) {
Sprintf(eos(buf), "%s%s",
first ? "" : ", ", soundlib_choices[i].sndprocs->soundname);
first = FALSE;
}
config_error_add("Soundlib type %s not recognized. Choices are: %s",
s, buf);
}
if (tmps)
free((genericptr_t) tmps) /*, tmps = 0*/ ;
}
#endif
/* copy up to maxlen-1 characters; 'dest' must be able to hold maxlen;
treat comma as alternate end of 'src' */
void
get_soundlib_name(char *dest, int maxlen)
{
int count, idx;
const char *src;
idx = ga.active_soundlib;
if (idx > soundlib_unassigned && idx <= soundlib_nosound) {
src = soundlib_choices[idx].sndprocs->soundname;
for (count = 1; count < maxlen; count++) {
if (*src == ',' || *src == '\0')
break; /*exit on \0 terminator*/
*dest++ = *src++;
}
}
*dest = '\0';
}
/*
* The default sound interface
*
* 3rd party sound_procs should be placed in ../sound/x
* and build procedures should reference them there.
*/
#if 0
static void nosound_init_nhsound(void);
static void nosound_exit_nhsound(const char *);
static void nosound_suspend_nhsound(const char *);
static void nosound_resume_nhsound(void);
static void nosound_achievement(schar, schar, int32_t);
static void nosound_soundeffect(int32_t, int32_t);
static void nosound_play_usersound(char *, int32_t, int32_t);
#endif
struct sound_procs nosound_procs = {
SOUNDID(nosound),
0L,
(void (*)(void)) 0, /* init_nhsound */
(void (*)(const char *)) 0, /* exit_nhsound */
(void (*)(schar, schar, int32_t)) 0, /* achievement */
(void (*)(char *, int32_t, int32_t)) 0, /* sound effect */
(void (*)(int32_t, char *, int32_t)) 0, /* hero_playnotes */
(void (*)(char *, int32_t, int32_t)) 0, /* play_usersound */
};
#if 0
static void
nosound_init_nhsound(void)
{
}
static void
nosound_exit_nhsound(const char *reason)
{
}
static void
nosound_achievement(schar ach1, schar ach2, int32_t repeat)
{
}
static void
nosound_soundeffect(int32_t seid, int volume)
{
}
static void
nosound_play_usersound(char *filename, int volume, int idx)
{
}
#endif
/*sounds.c*/