You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

249 lines
6.5 KiB

#include "quakedef.h"
#ifdef USE_FMOD
#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;
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
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
sound_started = true;
}
void S_Shutdown(void)
{
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)
{
// Find channel group for entity number (or create) => note entnum can also be some random hash value
// ChannelControl::setMode => set to 3D
// Pre-define channels for each entity (could be an array of ints, probably array of entchannel_t structs)
// For entchannel 0, dynamically select a free channel (or just play without doing anything, let FMOD handle it)
// If channel at index entchannel >0 is already playing: stop
// Special case entchannel -1 => just play locally on listener, no 3D
// System::playSound with paused = true
// ChannelControl::set3DAttributes
// Use ChannelControl::setDelay and ChannelControl::getDSPClock to add a delay to move sounds out of phase if necessary
// ChannelControl::setPaused = false
// Store channel handle at position entchannel
}
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
}
void S_StopSound(int entnum, int entchannel)
{
}
void S_StopAllSounds(qboolean clear)
{
}
void S_ClearBuffer(void)
{
}
void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up)
{
// System::set3DListenerAttributes
FMOD_System_Update(fmod_system);
}
void S_ExtraUpdate(void)
{
FMOD_System_Update(fmod_system);
}
static void S_SetMasterMute(FMOD_BOOL mute)
{
FMOD_CHANNELGROUP *master;
if (!fmod_system)
return;
FMOD_System_GetMasterChannelGroup(fmod_system, &master);
FMOD_ChannelGroup_SetMute(master, mute);
}
void S_BlockSound(void)
{
S_SetMasterMute(1);
}
void S_UnblockSound(void)
{
S_SetMasterMute(0);
}
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
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)
{
}
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