|
|
|
@ -15,8 +15,11 @@ static float old_volume = -1.0f; |
|
|
|
|
|
|
|
static FMOD_CHANNELGROUP *sfx_channelGroup = NULL; |
|
|
|
|
|
|
|
vec3_t listener_origin; |
|
|
|
|
|
|
|
static const char *FMOD_SpeakerModeString(FMOD_SPEAKERMODE speakermode); |
|
|
|
static float SND_FMOD_Attenuation(FMOD_CHANNELCONTROL *channelControl, float distance); |
|
|
|
static void SND_StartAmbientSounds(); |
|
|
|
|
|
|
|
// Copy and convert coordinate system |
|
|
|
#define FMOD_VectorCopy(a, b) {(b).x=(a)[0];(b).y=(a)[2];(b).z=(a)[1];} |
|
|
|
@ -35,6 +38,7 @@ typedef struct entsounds_s |
|
|
|
} entsounds_t; |
|
|
|
|
|
|
|
static entsounds_t entsounds[MAX_CHANNELS]; |
|
|
|
static FMOD_CHANNEL *ambients[NUM_AMBIENTS]; |
|
|
|
|
|
|
|
// Keep track of all the sounds started each frame |
|
|
|
static sfx_t *sfxThisFrame[16]; |
|
|
|
@ -130,7 +134,7 @@ void S_Shutdown(void) |
|
|
|
|
|
|
|
Con_DPrintf("[FMOD] Shutdown\n"); |
|
|
|
|
|
|
|
S_StopAllSounds(true); |
|
|
|
S_StopAllSounds(false); |
|
|
|
|
|
|
|
// Release all sounds that were loaded and attached to sfx_t's |
|
|
|
for (i = 0; i < num_sfx; i++) |
|
|
|
@ -228,6 +232,26 @@ static FMOD_RESULT SND_FMOD_Callback(FMOD_CHANNELCONTROL *channelcontrol, FMOD_C |
|
|
|
return FMOD_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static void SND_FMOD_SetChannelAttributes(FMOD_CHANNEL *channel, sfx_t *sfx, vec3_t origin, float vol) |
|
|
|
{ |
|
|
|
FMOD_VECTOR position; |
|
|
|
|
|
|
|
FMOD_VectorCopy(origin, position); |
|
|
|
FMOD_Channel_Set3DAttributes(channel, &position, NULL); |
|
|
|
FMOD_Channel_SetVolume(channel, vol); |
|
|
|
FMOD_Channel_SetVolumeRamp(channel, 1); // This *might* help somewhat with slight audio pops (esp. noticable on the Nailgun) |
|
|
|
|
|
|
|
if (sfx->loopstart >= 0) |
|
|
|
{ |
|
|
|
FMOD_Channel_SetMode(channel, FMOD_LOOP_NORMAL); |
|
|
|
FMOD_Channel_SetLoopPoints(channel, sfx->loopstart, FMOD_TIMEUNIT_MS, sfx->loopend, FMOD_TIMEUNIT_MS); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
FMOD_Channel_SetMode(channel, FMOD_LOOP_OFF); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static soundslot_t *SND_PickSoundSlot(int entnum, int entchannel) |
|
|
|
{ |
|
|
|
soundslot_t *slot; |
|
|
|
@ -255,11 +279,107 @@ static soundslot_t *SND_PickSoundSlot(int entnum, int entchannel) |
|
|
|
return slot; |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
============== |
|
|
|
Ambient sounds |
|
|
|
|
|
|
|
These are sounds that are always present and always playing. They are unaffected by distance or orientation. |
|
|
|
Instead they are modulated in volume based on whether the player is inside a world volume that has ambient sound levels set. |
|
|
|
============== |
|
|
|
*/ |
|
|
|
static FMOD_CHANNEL *SND_StartAmbientSound(const char *samplename) |
|
|
|
{ |
|
|
|
sfx_t *sfx; |
|
|
|
FMOD_CHANNEL *channel; |
|
|
|
FMOD_RESULT result; |
|
|
|
|
|
|
|
sfx = S_PrecacheSound(samplename); |
|
|
|
if (!sfx) |
|
|
|
return NULL; |
|
|
|
|
|
|
|
S_LoadSound(sfx); |
|
|
|
if (!sfx->sound) |
|
|
|
return NULL; |
|
|
|
|
|
|
|
result = FMOD_System_PlaySound(fmod_system, sfx->sound, sfx_channelGroup, 1, &channel); |
|
|
|
if (result != FMOD_OK) |
|
|
|
{ |
|
|
|
Con_Printf("Failed to play ambient FMOD sound: %s\n", FMOD_ErrorString(result)); |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
SND_FMOD_SetChannelAttributes(channel, sfx, vec3_origin, 0.0f); |
|
|
|
FMOD_Channel_Set3DLevel(channel, 0.0f); |
|
|
|
FMOD_Channel_SetPaused(channel, 0); |
|
|
|
|
|
|
|
return channel; |
|
|
|
} |
|
|
|
|
|
|
|
static void SND_StartAmbientSounds() |
|
|
|
{ |
|
|
|
memset(ambients, 0, sizeof(ambients)); |
|
|
|
ambients[AMBIENT_WATER] = SND_StartAmbientSound("ambience/water1.wav"); |
|
|
|
ambients[AMBIENT_SKY] = SND_StartAmbientSound("ambience/wind2.wav"); |
|
|
|
} |
|
|
|
|
|
|
|
static void S_UpdateAmbientSounds() |
|
|
|
{ |
|
|
|
FMOD_CHANNEL *channel; |
|
|
|
mleaf_t *leaf; |
|
|
|
float vol, channel_vol; |
|
|
|
|
|
|
|
if (cl.worldmodel && cl.worldmodel->nodes) |
|
|
|
leaf = Mod_PointInLeaf(listener_origin, cl.worldmodel); |
|
|
|
else |
|
|
|
leaf = NULL; |
|
|
|
|
|
|
|
for (int i = 0; i < NUM_AMBIENTS; i++) |
|
|
|
{ |
|
|
|
channel = ambients[i]; |
|
|
|
if (!channel) |
|
|
|
continue; |
|
|
|
|
|
|
|
if (!leaf || !ambient_level.value) |
|
|
|
{ |
|
|
|
FMOD_Channel_SetVolume(channel, 0.0f); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
vol = (ambient_level.value * (float)leaf->ambient_sound_level[i]) / 255; |
|
|
|
if (vol < 0.03f) |
|
|
|
vol = 0.0f; |
|
|
|
|
|
|
|
// don't adjust volume too fast |
|
|
|
FMOD_Channel_GetVolume(channel, &channel_vol); |
|
|
|
if (channel_vol < vol) |
|
|
|
{ |
|
|
|
channel_vol += host_frametime * (ambient_fade.value / 255); |
|
|
|
if (channel_vol > vol) |
|
|
|
channel_vol = vol; |
|
|
|
} |
|
|
|
else if (channel_vol > vol) |
|
|
|
{ |
|
|
|
channel_vol -= host_frametime * (ambient_fade.value / 255); |
|
|
|
if (channel_vol < vol) |
|
|
|
channel_vol = vol; |
|
|
|
} |
|
|
|
|
|
|
|
FMOD_Channel_SetVolume(channel, channel_vol); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
============= |
|
|
|
Entity sounds |
|
|
|
|
|
|
|
These are your typical 3D sounds generated by entities in the world, including the player. |
|
|
|
Each entity has a number of preset voice channels, each of which can only play one sound at a time. |
|
|
|
============= |
|
|
|
*/ |
|
|
|
void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation) // Note: volume and attenuation are properly normalized here |
|
|
|
{ |
|
|
|
int i; |
|
|
|
FMOD_CHANNEL *channel; |
|
|
|
FMOD_VECTOR position; |
|
|
|
FMOD_RESULT result; |
|
|
|
soundslot_t *userdata; |
|
|
|
unsigned long long dspclock; |
|
|
|
@ -285,26 +405,14 @@ void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
SND_FMOD_SetChannelAttributes(channel, sfx, origin, fvol); |
|
|
|
|
|
|
|
// Set up callback data for rolloff and cleanup |
|
|
|
userdata->channel = channel; |
|
|
|
userdata->dist_mult = attenuation / sound_nominal_clip_dist; |
|
|
|
FMOD_Channel_SetUserData(channel, userdata); |
|
|
|
FMOD_Channel_SetCallback(channel, &SND_FMOD_Callback); |
|
|
|
|
|
|
|
FMOD_VectorCopy(origin, position); |
|
|
|
FMOD_Channel_Set3DAttributes(channel, &position, NULL); |
|
|
|
FMOD_Channel_SetVolume(channel, fvol); |
|
|
|
|
|
|
|
if (sfx->loopstart >= 0) |
|
|
|
{ |
|
|
|
FMOD_Channel_SetMode(channel, FMOD_LOOP_NORMAL); |
|
|
|
FMOD_Channel_SetLoopPoints(channel, sfx->loopstart, FMOD_TIMEUNIT_MS, sfx->loopend, FMOD_TIMEUNIT_MS); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
FMOD_Channel_SetMode(channel, FMOD_LOOP_OFF); |
|
|
|
} |
|
|
|
|
|
|
|
// Anything coming from the view entity will always be full volume, and entchannel -1 is used for local sounds (e.g. menu sounds) |
|
|
|
if (entchannel < 0 || entnum == cl.viewentity) |
|
|
|
{ |
|
|
|
@ -330,12 +438,17 @@ void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f |
|
|
|
FMOD_Channel_SetPaused(channel, 0); |
|
|
|
} |
|
|
|
|
|
|
|
// TODO: we're still missing Automatic Ambient sounds for water, lava, slime etc (see S_UpdateAmbientSounds) |
|
|
|
/* |
|
|
|
============= |
|
|
|
Static sounds |
|
|
|
|
|
|
|
These are sounds that have a fixed position in the world and loop continuously. |
|
|
|
They typically start playing immediately on level load. |
|
|
|
============= |
|
|
|
*/ |
|
|
|
void S_StaticSound(sfx_t *sfx, vec3_t origin, float vol, float attenuation) // Note: volume and attenuation are in 0-255 range here |
|
|
|
{ |
|
|
|
FMOD_CHANNEL *channel; |
|
|
|
FMOD_VECTOR position; |
|
|
|
FMOD_RESULT result; |
|
|
|
soundslot_t *userdata; |
|
|
|
unsigned long long dspclock; |
|
|
|
@ -354,11 +467,8 @@ void S_StaticSound(sfx_t *sfx, vec3_t origin, float vol, float attenuation) // N |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
FMOD_VectorCopy(origin, position); |
|
|
|
FMOD_Channel_Set3DAttributes(channel, &position, NULL); |
|
|
|
FMOD_Channel_SetMode(channel, FMOD_LOOP_NORMAL); |
|
|
|
FMOD_Channel_SetVolume(channel, vol / 255); |
|
|
|
|
|
|
|
SND_FMOD_SetChannelAttributes(channel, sfx, origin, vol / 255); |
|
|
|
|
|
|
|
// Set up attenuation info for use by the rolloff callback |
|
|
|
userdata = SND_PickSoundSlot(-1, -1); |
|
|
|
userdata->channel = channel; |
|
|
|
@ -402,7 +512,12 @@ void S_StopAllSounds(qboolean clear) |
|
|
|
FMOD_ChannelGroup_Stop(sfx_channelGroup); |
|
|
|
|
|
|
|
if (clear) |
|
|
|
{ |
|
|
|
S_ClearBuffer(); |
|
|
|
|
|
|
|
// Ambient sounds need to be restarted, as they are always "playing" |
|
|
|
SND_StartAmbientSounds(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void S_ClearBuffer(void) |
|
|
|
@ -426,6 +541,8 @@ void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) |
|
|
|
old_volume = sfxvolume.value; |
|
|
|
} |
|
|
|
|
|
|
|
VectorCopy(origin, listener_origin); |
|
|
|
|
|
|
|
FMOD_VECTOR fmod_pos, fmod_forward, fmod_up; |
|
|
|
FMOD_VectorCopy(origin, fmod_pos); |
|
|
|
FMOD_VectorCopy(forward, fmod_forward); |
|
|
|
@ -436,6 +553,8 @@ void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) |
|
|
|
|
|
|
|
FMOD_ChannelGroup_SetVolume(sfx_channelGroup, sfxvolume.value); |
|
|
|
|
|
|
|
S_UpdateAmbientSounds(); |
|
|
|
|
|
|
|
FMOD_System_Update(fmod_system); |
|
|
|
|
|
|
|
// Reset sounds played for the next frame |
|
|
|
|