|
|
|
@ -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 |