From 68db1e78ae9dfbb819002acc47009c69a4b0647a Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Mon, 12 Apr 2021 16:17:16 +0200 Subject: [PATCH] Implemented more FMOD goodness: - Initialization of FMOD System (if necessary), diagnostics and setup of SFX channel group - Loading of FMOD sounds from either disk or PAK file --- engine/Quake/q_sound.h | 7 ++ engine/Quake/snd_dma.c | 9 ++- engine/Quake/snd_fmod.c | 173 +++++++++++++++++++++++++++++++++------- 3 files changed, 159 insertions(+), 30 deletions(-) diff --git a/engine/Quake/q_sound.h b/engine/Quake/q_sound.h index ded7b5d..46b04a4 100644 --- a/engine/Quake/q_sound.h +++ b/engine/Quake/q_sound.h @@ -25,6 +25,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef __QUAKE_SOUND__ #define __QUAKE_SOUND__ +#if USE_FMOD +#include "fmod.h" +#endif + /* !!! if this is changed, it must be changed in asm_i386.h too !!! */ typedef struct { @@ -36,6 +40,9 @@ typedef struct sfx_s { char name[MAX_QPATH]; cache_user_t cache; +#if USE_FMOD + FMOD_SOUND *sound; +#endif } sfx_t; /* !!! if this is changed, it must be changed in asm_i386.h too !!! */ diff --git a/engine/Quake/snd_dma.c b/engine/Quake/snd_dma.c index 6774471..cc73134 100644 --- a/engine/Quake/snd_dma.c +++ b/engine/Quake/snd_dma.c @@ -70,7 +70,7 @@ static int num_sfx; static sfx_t *ambient_sfx[NUM_AMBIENTS]; -static qboolean sound_started = false; +qboolean sound_started = false; static qboolean snd_initialized = false; cvar_t bgmvolume = {"bgmvolume", "1", CVAR_ARCHIVE}; @@ -266,6 +266,7 @@ void S_Shutdown (void) shm = NULL; } +#endif // USE_FMOD // ======================================================================= // Load a sound @@ -277,7 +278,7 @@ S_FindName ================== */ -static sfx_t *S_FindName (const char *name) +sfx_t *S_FindName (const char *name) { int i; sfx_t *sfx; @@ -308,6 +309,7 @@ static sfx_t *S_FindName (const char *name) return sfx; } +#ifndef USE_FMOD /* ================== @@ -326,6 +328,8 @@ void S_TouchSound (const char *name) Cache_Check (&sfx->cache); } +#endif // USE_FMOD + /* ================== S_PrecacheSound @@ -348,6 +352,7 @@ sfx_t *S_PrecacheSound (const char *name) return sfx; } +#ifndef USE_FMOD //============================================================================= diff --git a/engine/Quake/snd_fmod.c b/engine/Quake/snd_fmod.c index 95697bf..0073422 100644 --- a/engine/Quake/snd_fmod.c +++ b/engine/Quake/snd_fmod.c @@ -4,34 +4,86 @@ #include "fmod.h" #include "fmod_errors.h" +extern qboolean sound_started; // in snd_dma.c + FMOD_SYSTEM *fmod_system = NULL; +static qboolean fmod_ownership = false; + +static FMOD_CHANNELGROUP *sfx_channelGroup = NULL; -#if 1 -//void S_Init(void) -//{ -// // Register cvars (see snd_dma.c) and check parameters -// // Call S_Startup -// // Call S_StopAllSounds(true) -// -// // Note: could actually keep the original S_Init from snd_dma.c and have it call the above functions in here -//} +static const char *FMOD_SpeakerModeString(FMOD_SPEAKERMODE speakermode); void S_Startup(void) { + FMOD_RESULT result; + FMOD_SPEAKERMODE speakermode; + unsigned int version; + int driver, systemrate, numchannels; + char name[1024]; + // Create FMOD System if it doesn't exist already - // Create SFX channel group + if (!fmod_system) + { + result = FMOD_System_Create(&fmod_system); + if (result != FMOD_OK) + { + Con_Printf("Failed to create FMOD System: %s\n", FMOD_ErrorString(result)); + return; + } + + fmod_ownership = true; + } + + result = FMOD_System_GetVersion(fmod_system, &version); + if (result != FMOD_OK) + { + Con_Printf("Failed to retrieve FMOD version: %s\n", FMOD_ErrorString(result)); + return; + } + + result = FMOD_System_GetDriver(fmod_system, &driver); + if (result != FMOD_OK) + { + Con_Printf("Failed to retrieve selected FMOD driver: %s\n", FMOD_ErrorString(result)); + return; + } + + result = FMOD_System_GetDriverInfo(fmod_system, driver, name, sizeof(name), NULL, &systemrate, &speakermode, &numchannels); + if (result != FMOD_OK) + { + Con_Printf("Failed to retrieve FMOD driver info: %s\n", FMOD_ErrorString(result)); + return; + } + + Con_Printf("FMOD version %01x.%02x.%02x, driver '%s', %s speaker mode, %d Hz, %d channels\n", + (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff, name, FMOD_SpeakerModeString(speakermode), systemrate, numchannels); + + result = FMOD_System_CreateChannelGroup(fmod_system, "SFX", &sfx_channelGroup); + if (result != FMOD_OK) + { + Con_Printf("Failed to create FMOD SFX channel group: %s\n", FMOD_ErrorString(result)); + return; + } + // Could use System::set3DRolloffCallback to set up a (attenuation / sound_nominal_clip_dist) distance multiplier (would need to use ChannelControl::setUserData to hold ref to attn value) // Note: sound_nominal_clip_dist could be dynamic to allow a small sound 'bubble' for local multiplayer - // Set sound_started to true - Con_DPrintf("[FMOD] Startup\n"); + sound_started = true; } void S_Shutdown(void) { - // Destroy SFX channel group - // If we created FMOD System (and consequently own it), destroy it here Con_DPrintf("[FMOD] Shutdown\n"); + + // Destroy any SFX channels still in use + + // If we created the FMOD System (and consequently own it), destroy it here + if (fmod_ownership) + { + FMOD_System_Close(fmod_system); + fmod_system = NULL; + fmod_ownership = false; + } } void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation) @@ -50,10 +102,6 @@ void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f // ChannelControl::setPaused = false // Store channel handle at position entchannel - if (sfx) - Con_DPrintf("[FMOD] Start sound %s for entity %d channel %d\n", sfx->name, entnum, entchannel); - else - Con_DPrintf("[FMOD] Start sound NULL for entity %d channel %d\n", entnum, entchannel); } void S_StaticSound(sfx_t *sfx, vec3_t origin, float vol, float attenuation) @@ -61,15 +109,11 @@ void S_StaticSound(sfx_t *sfx, vec3_t origin, float vol, float attenuation) // Similar to above, but without the per-entity channel group song and dance // Check if sound is looped (should be) and FMOD_Channel_SetMode to looped // Set Channel::setLoopPoints if the sfxcache specifies something non-standard - if (sfx) - Con_DPrintf("[FMOD] Static sound: %s\n", sfx->name); - else - Con_DPrintf("[FMOD] Static sound: NULL\n"); } void S_StopSound(int entnum, int entchannel) { - Con_DPrintf("[FMOD] Stop sound for entity %d channel %d\n", entnum, entchannel); + } void S_StopAllSounds(qboolean clear) @@ -115,18 +159,91 @@ void S_UnblockSound(void) S_SetMasterMute(0); } -sfx_t *S_PrecacheSound(const char *sample) +sfxcache_t *S_LoadSound(sfx_t *s) { + char namebuffer[256]; + byte *data; + int len, h; + FMOD_CREATESOUNDEXINFO exinfo; + FMOD_RESULT result; +#if _DEBUG + FMOD_SOUND_TYPE type; + FMOD_SOUND_FORMAT format; + int channels, bits, loopcount; + unsigned int length; +#endif + + if (!fmod_system) + return NULL; + + // Check if it's already loaded + if (s->sound) + return NULL; + + q_strlcpy(namebuffer, "sound/", sizeof(namebuffer)); + q_strlcat(namebuffer, s->name, sizeof(namebuffer)); + + // We need to briefly open the file to figure out its length, which FMOD needs to know + len = COM_OpenFile(namebuffer, &h, NULL); + if (h >= 0) + COM_CloseFile(h); + + data = COM_LoadHunkFile(namebuffer, NULL); + if (!data) + { + Con_Printf("Couldn't load %s\n", namebuffer); + return NULL; + } + + memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); + exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); + exinfo.length = len; + // System::createSound with FMOD_3D (sfx_t will need a pointer to FMOD_Sound) - // Might need to set rate and width? FMOD can probably figure this out by itself based on the WAV file contents - Con_DPrintf("[FMOD] Precaching sound: %s\n", sample); - return NULL; + // Might need to set rate and width? FMOD can probably figure this out by itself based on the WAV file contents + result = FMOD_System_CreateSound(fmod_system, (const char*)data, FMOD_3D | FMOD_OPENMEMORY, &exinfo, &s->sound); + if (result != FMOD_OK) + { + Con_Printf("Failed to create FMOD sound: %s\n", FMOD_ErrorString(result)); + return NULL; + } + +#if _DEBUG + FMOD_Sound_GetFormat(s->sound, &type, &format, &channels, &bits); + FMOD_Sound_GetLength(s->sound, &length, FMOD_TIMEUNIT_MS); + FMOD_Sound_GetLoopCount(s->sound, &loopcount); + Con_DPrintf("[FMOD] Loaded sound '%s': type %d, format %d, %d channel(s), %d bits, %d ms, loopcount = %d\n", s->name, type, format, channels, bits, length, loopcount); +#endif + + return NULL; // Return value is unused; FMOD has its own internal cache, we never need to use Quake's sfxcache_t } void S_TouchSound(const char *sample) { } -#endif + +static const char *FMOD_SpeakerModeString(FMOD_SPEAKERMODE speakermode) +{ + switch (speakermode) + { + case FMOD_SPEAKERMODE_MONO: + return "Mono"; + case FMOD_SPEAKERMODE_STEREO: + return "Stereo"; + case FMOD_SPEAKERMODE_QUAD: + return "4.0 Quad"; + case FMOD_SPEAKERMODE_SURROUND: + return "5.0 Surround"; + case FMOD_SPEAKERMODE_5POINT1: + return "5.1 Surround"; + case FMOD_SPEAKERMODE_7POINT1: + return "7.1 Surround"; + case FMOD_SPEAKERMODE_7POINT1POINT4: + return "7.1.4 Surround"; + default: + return "Unknown"; + } +} #endif // USE_FMOD