- Sounds that define a loop no longer play looped by default. They must be started with

CHAN_LOOP so that the higher level sound code knows they loop and can handle them accordingly.
- Added support for a LOOP_BIDI tag. Set it to "1", "On", "True", or "Yes" to use a
  bidirectional loop. This only works with sounds and not music, because music is streamed
  so does not support them.
- Extended custom loop support to work with samples as well as music.

SVN r2434 (trunk)
This commit is contained in:
Randy Heit 2010-07-15 23:07:41 +00:00
parent 99a2014ead
commit c29639426f

View file

@ -1456,6 +1456,88 @@ const char *GetTagData(FMOD::Sound *sound, const char *tag_name)
return NULL;
}
//==========================================================================
//
// SetCustomLoopPts
//
// Sets up custom sound loops by checking for these tags:
// LOOP_START
// LOOP_END
// LOOP_BIDI
//
//==========================================================================
static void SetCustomLoopPts(FMOD::Sound *sound)
{
#if 0
FMOD_TAG tag;
int numtags;
if (FMOD_OK == stream->getNumTags(&numtags, NULL))
{
for (int i = 0; i < numtags; ++i)
{
if (FMOD_OK == sound->getTag(NULL, i, &tag))
{
Printf("Tag %2d. %d %s = %s\n", i, tag.datatype, tag.name, tag.data);
}
}
}
#endif
const char *tag_data;
unsigned int looppt[2];
bool looppt_as_samples[2], have_looppt[2] = { false };
static const char *const loop_tags[2] = { "LOOP_START", "LOOP_END" };
for (int i = 0; i < 2; ++i)
{
if (NULL != (tag_data = GetTagData(sound, loop_tags[i])))
{
if (S_ParseTimeTag(tag_data, &looppt_as_samples[i], &looppt[i]))
{
have_looppt[i] = true;
}
else
{
Printf("Invalid %s tag: '%s'\n", loop_tags[i], tag_data);
}
}
}
if (have_looppt[0] && !have_looppt[1])
{ // Have a start tag, but not an end tag: End at the end of the song.
have_looppt[1] = (FMOD_OK == sound->getLength(&looppt[1], FMOD_TIMEUNIT_PCM));
looppt_as_samples[1] = true;
}
else if (!have_looppt[0] && have_looppt[1])
{ // Have an end tag, but no start tag: Start at beginning of the song.
looppt[0] = 0;
looppt_as_samples[0] = true;
have_looppt[0] = true;
}
if (have_looppt[0] && have_looppt[1])
{ // Have both loop points: Try to set the loop.
FMOD_RESULT res = sound->setLoopPoints(
looppt[0], looppt_as_samples[0] ? FMOD_TIMEUNIT_PCM : FMOD_TIMEUNIT_MS,
looppt[1] - 1, looppt_as_samples[1] ? FMOD_TIMEUNIT_PCM : FMOD_TIMEUNIT_MS);
if (res != FMOD_OK)
{
Printf("Setting custom loop points failed. Error %d\n", res);
}
}
// Check for a bi-directional loop.
if (NULL != (tag_data = GetTagData(sound, "LOOP_BIDI")) &&
(stricmp(tag_data, "on") == 0 ||
stricmp(tag_data, "true") == 0 ||
stricmp(tag_data, "yes") == 0 ||
stricmp(tag_data, "1") == 0))
{
FMOD_MODE mode;
if (FMOD_OK == (sound->getMode(&mode)))
{
sound->setMode(mode & ~(FMOD_LOOP_OFF | FMOD_LOOP_NORMAL) | FMOD_LOOP_BIDI);
}
}
}
//==========================================================================
//
// FMODSoundRenderer :: OpenStream
@ -1517,61 +1599,7 @@ SoundStream *FMODSoundRenderer::OpenStream(const char *filename_or_data, int fla
}
if (result == FMOD_OK)
{
// Handle custom loop starts by checking for a "loop_start" tag.
#if 0
FMOD_TAG tag;
int numtags;
if (FMOD_OK == stream->getNumTags(&numtags, NULL))
{
for (int i = 0; i < numtags; ++i)
{
if (FMOD_OK == stream->getTag(NULL, i, &tag))
{
Printf("Tag %2d. %d %s = %s\n", i, tag.datatype, tag.name, tag.data);
}
}
}
#endif
const char *tag_data;
unsigned int looppt[2];
bool looppt_as_samples[2], have_looppt[2] = { false };
static const char *const loop_tags[2] = { "LOOP_START", "LOOP_END" };
for (int i = 0; i < 2; ++i)
{
if (NULL != (tag_data = GetTagData(stream, loop_tags[i])))
{
if (S_ParseTimeTag(tag_data, &looppt_as_samples[i], &looppt[i]))
{
have_looppt[i] = true;
}
else
{
Printf("Invalid %s tag: '%s'\n", loop_tags[i], tag_data);
}
}
}
if (have_looppt[0] && !have_looppt[1])
{ // Have a start tag, but not an end tag: End at the end of the song.
have_looppt[1] = (FMOD_OK == stream->getLength(&looppt[1], FMOD_TIMEUNIT_PCM));
looppt_as_samples[1] = true;
}
else if (!have_looppt[0] && have_looppt[1])
{ // Have an end tag, but no start tag: Start at beginning of the song.
looppt[0] = 0;
looppt_as_samples[0] = true;
have_looppt[0] = true;
}
if (have_looppt[0] && have_looppt[1])
{ // Have both loop points: Try to set the loop.
FMOD_RESULT res = stream->setLoopPoints(
looppt[0], looppt_as_samples[0] ? FMOD_TIMEUNIT_PCM : FMOD_TIMEUNIT_MS,
looppt[1] - 1, looppt_as_samples[1] ? FMOD_TIMEUNIT_PCM : FMOD_TIMEUNIT_MS);
if (res != FMOD_OK)
{
Printf("Setting custom song loop points for song failed. Error %d\n", res);
}
}
SetCustomLoopPts(stream);
return new FMODStreamCapsule(stream, this, url ? filename_or_data : NULL);
}
return NULL;
@ -1613,7 +1641,15 @@ FISoundChannel *FMODSoundRenderer::StartSound(SoundHandle sfx, float vol, int pi
mode = (mode & ~FMOD_3D) | FMOD_2D;
if (flags & SNDF_LOOP)
{
mode = (mode & ~FMOD_LOOP_OFF) | FMOD_LOOP_NORMAL;
mode &= ~FMOD_LOOP_OFF;
if (!(mode & (FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI)))
{
mode |= FMOD_LOOP_NORMAL;
}
}
else
{
mode |= FMOD_LOOP_OFF;
}
chan->setMode(mode);
chan->setChannelGroup((flags & SNDF_NOPAUSE) ? SfxGroup : PausableSfx);
@ -1713,7 +1749,16 @@ FISoundChannel *FMODSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener *
}
if (flags & SNDF_LOOP)
{
mode = (mode & ~FMOD_LOOP_OFF) | FMOD_LOOP_NORMAL;
mode &= ~FMOD_LOOP_OFF;
if (!(mode & (FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI)))
{
mode |= FMOD_LOOP_NORMAL;
}
}
else
{
// FMOD_LOOP_OFF overrides FMOD_LOOP_NORMAL and FMOD_LOOP_BIDI
mode |= FMOD_LOOP_OFF;
}
mode = SetChanHeadSettings(listener, chan, pos, !!(flags & SNDF_AREA), mode);
chan->setMode(mode);
@ -1810,13 +1855,14 @@ bool FMODSoundRenderer::HandleChannelDelay(FMOD::Channel *chan, FISoundChannel *
// Clamp the position of looping sounds to be within the sound.
// If we try to start it several minutes past its normal end,
// FMOD doesn't like that.
// FIXME: Clamp this right for loops that don't cover the whole sound.
if (flags & SNDF_LOOP)
{
FMOD::Sound *sound;
if (FMOD_OK == chan->getCurrentSound(&sound))
{
unsigned int len;
if (FMOD_OK == sound->getLength(&len, FMOD_TIMEUNIT_MS) && len)
if (FMOD_OK == sound->getLength(&len, FMOD_TIMEUNIT_MS) && len != 0)
{
difftime %= len;
}
@ -2358,6 +2404,7 @@ SoundHandle FMODSoundRenderer::LoadSound(BYTE *sfxdata, int length)
DPrintf("Failed to allocate sample: Error %d\n", result);
return retval;
}
SetCustomLoopPts(sample);
retval.data = sample;
return retval;
}