#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