From c29639426fa67c304d616bcc441ec26903911848 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 15 Jul 2010 23:07:41 +0000 Subject: [PATCH] - 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) --- src/sound/fmodsound.cpp | 163 ++++++++++++++++++++++++++-------------- 1 file changed, 105 insertions(+), 58 deletions(-) diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index e94ec04b7d..a31665803d 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -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; }