Browse Source

Implemented proper looping of sounds based on cue information from the WAV file header. Also modified Delay function so that it is unaffected by differences in sample rate between the input file and FMOD's mixer.

console
Nico de Poel 5 years ago
parent
commit
e928833f5e
  1. 4
      engine/Quake/q_sound.h
  2. 52
      engine/Quake/snd_fmod.c
  3. 3
      engine/Quake/snd_mem.c

4
engine/Quake/q_sound.h

@ -42,7 +42,9 @@ typedef struct sfx_s
cache_user_t cache; cache_user_t cache;
#if USE_FMOD #if USE_FMOD
FMOD_SOUND *sound; FMOD_SOUND *sound;
unsigned int samples;
unsigned int length; // milliseconds
int loopstart;
int loopend;
#endif #endif
} sfx_t; } sfx_t;

52
engine/Quake/snd_fmod.c

@ -152,13 +152,13 @@ static unsigned int SND_GetDelay(const sfx_t *sfx, float maxseconds)
{ {
unsigned int delay; unsigned int delay;
delay = maxseconds * fmod_samplerate;
if (delay > sfx->samples)
delay = sfx->samples;
delay = maxseconds * 1000;
if (delay > sfx->length)
delay = sfx->length;
if (delay > 0) if (delay > 0)
delay = rand() % delay; delay = rand() % delay;
return delay;
return delay * fmod_samplerate / 1000;
} }
/* /*
@ -285,8 +285,17 @@ void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f
FMOD_VectorCopy(origin, position); FMOD_VectorCopy(origin, position);
FMOD_Channel_Set3DAttributes(channel, &position, NULL); FMOD_Channel_Set3DAttributes(channel, &position, NULL);
FMOD_Channel_SetMode(channel, FMOD_LOOP_OFF); // TODO: some entity sounds do need to loop, e.g. moving elevators. How is this done?
FMOD_Channel_SetVolume(channel, fvol); 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) // 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) if (entchannel < 0 || entnum == cl.viewentity)
@ -442,13 +451,13 @@ sfxcache_t *S_LoadSound(sfx_t *s)
{ {
char namebuffer[256]; char namebuffer[256];
byte *data; byte *data;
int len, h;
wavinfo_t info;
FMOD_CREATESOUNDEXINFO exinfo; FMOD_CREATESOUNDEXINFO exinfo;
FMOD_RESULT result; FMOD_RESULT result;
#if _DEBUG #if _DEBUG
FMOD_SOUND_TYPE type; FMOD_SOUND_TYPE type;
FMOD_SOUND_FORMAT format; FMOD_SOUND_FORMAT format;
int channels, bits, loopcount;
int channels, bits;
#endif #endif
if (!fmod_system) if (!fmod_system)
@ -461,11 +470,6 @@ sfxcache_t *S_LoadSound(sfx_t *s)
q_strlcpy(namebuffer, "sound/", sizeof(namebuffer)); q_strlcpy(namebuffer, "sound/", sizeof(namebuffer));
q_strlcat(namebuffer, s->name, 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); data = COM_LoadHunkFile(namebuffer, NULL);
if (!data) if (!data)
{ {
@ -473,9 +477,16 @@ sfxcache_t *S_LoadSound(sfx_t *s)
return NULL; return NULL;
} }
info = GetWavinfo(s->name, data, com_filesize);
if (!info.channels)
{
Con_Printf("Invalid WAV file: %s\n", namebuffer);
return NULL;
}
memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
exinfo.length = len;
exinfo.length = com_filesize;
result = FMOD_System_CreateSound(fmod_system, (const char*)data, FMOD_3D | FMOD_OPENMEMORY, &exinfo, &s->sound); result = FMOD_System_CreateSound(fmod_system, (const char*)data, FMOD_3D | FMOD_OPENMEMORY, &exinfo, &s->sound);
if (result != FMOD_OK) if (result != FMOD_OK)
@ -484,12 +495,21 @@ sfxcache_t *S_LoadSound(sfx_t *s)
return NULL; return NULL;
} }
FMOD_Sound_GetLength(s->sound, &s->samples, FMOD_TIMEUNIT_PCM);
// Collect data required for looping and delay
if (info.loopstart >= 0)
{
s->loopstart = info.loopstart * 1000 / info.rate;
s->loopend = info.samples * 1000 / info.rate;
}
else
{
s->loopstart = s->loopend = -1;
}
FMOD_Sound_GetLength(s->sound, &s->length, FMOD_TIMEUNIT_MS);
#if _DEBUG #if _DEBUG
FMOD_Sound_GetFormat(s->sound, &type, &format, &channels, &bits); FMOD_Sound_GetFormat(s->sound, &type, &format, &channels, &bits);
FMOD_Sound_GetLoopCount(s->sound, &loopcount);
Con_DPrintf("[FMOD] Loaded sound '%s': type %d, format %d, %d channel(s), %d bits, %d samples, loopcount = %d\n", s->name, type, format, channels, bits, s->samples, loopcount);
Con_DPrintf("[FMOD] Loaded sound '%s': type %d, format %d, %d channel(s), %d bits, %d ms, %d samples, loopstart = %d\n", s->name, type, format, channels, bits, s->length, info.samples, s->loopstart);
#endif #endif
return NULL; // Return value is unused; FMOD has its own internal cache, we never need to use Quake's sfxcache_t return NULL; // Return value is unused; FMOD has its own internal cache, we never need to use Quake's sfxcache_t

3
engine/Quake/snd_mem.c

@ -162,7 +162,7 @@ sfxcache_t *S_LoadSound (sfx_t *s)
return sc; return sc;
} }
#endif // USE_FMOD
/* /*
=============================================================================== ===============================================================================
@ -352,4 +352,3 @@ wavinfo_t GetWavinfo (const char *name, byte *wav, int wavlength)
return info; return info;
} }
#endif // USE_FMOD
Loading…
Cancel
Save