2

I'm attempting to port an old open-source FMOD 3 game (Candy Crisis) to the latest version of FMOD Ex 4 on OS X. Its sound needs are very simple—it plays WAVs, sometimes changing their frequency or speaker mix, and also plays MOD tracker music, sometimes changing the speed. I'm finding that the game works fine at first, but over the course of a few minutes, it starts truncating sounds early, then the music loses channels and eventually stops, then over time all sound ceases. I can cause the problem to reproduce more quickly if I lower the number of channels available to FMOD.

I can get the truncated/missing sounds issue to occur even if I never play a music file, but music definitely seems to make things worse. I have also tried commenting out the code which adjusts the sound frequency and speaker mix, and that was not the issue.

I am calling update() every frame.

Here's the entirety of my interactions with FMOD to play WAVs:

void InitSound( void )
{
    FMOD_RESULT   result = FMOD::System_Create(&g_fmod);
    FMOD_ERRCHECK(result);

    unsigned int  version;
    result = g_fmod->getVersion(&version);
    FMOD_ERRCHECK(result);

    if (version < FMOD_VERSION)
    {
        printf("Error!  You are using an old version of FMOD %08x.  This program requires %08x\n", version, FMOD_VERSION);
        abort();
    }

    result = g_fmod->init(8 /* was originally 64, but 8 repros the issue faster */, FMOD_INIT_NORMAL, 0);
    FMOD_ERRCHECK(result);

    for (int index=0; index<kNumSounds; index++)
    {
        result = g_fmod->createSound(QuickResourceName("snd", index+128, ".wav"), FMOD_DEFAULT, 0, &s_sound[index]);
        FMOD_ERRCHECK(result);
    }
}


void PlayMono( short which )
{
    if (soundOn)
    {
        FMOD_RESULT       result = g_fmod->playSound(FMOD_CHANNEL_FREE, s_sound[which], false, NULL);
        FMOD_ERRCHECK(result);
    }
}

void PlayStereoFrequency( short player, short which, short freq )
{
    if (soundOn)
    {
        FMOD::Channel*    channel = NULL;
        FMOD_RESULT       result = g_fmod->playSound(FMOD_CHANNEL_FREE, s_sound[which], true, &channel);
        FMOD_ERRCHECK(result);

        result = channel->setSpeakerMix(player, 1.0f - player, 0, 0, 0, 0, 0, 0);
        FMOD_ERRCHECK(result);

        float channelFrequency;
        result = s_sound[which]->getDefaults(&channelFrequency, NULL, NULL, NULL);
        FMOD_ERRCHECK(result);

        result = channel->setFrequency((channelFrequency * (16 + freq)) / 16);
        FMOD_ERRCHECK(result);

        result = channel->setPaused(false);
        FMOD_ERRCHECK(result);
    }
}

void UpdateSound()
{
    g_fmod->update();
}

And here's how I play MODs.

void ChooseMusic( short which )
{
    if( musicSelection >= 0 && musicSelection <= k_songs )  
    {
        s_musicChannel->stop();
        s_musicChannel = NULL;

        s_musicModule->release();
        s_musicModule = NULL;

        musicSelection = -1;
    }

    if (which >= 0 && which <= k_songs)
    {
        FMOD_RESULT result = g_fmod->createSound(QuickResourceName("mod", which+128, ""), FMOD_DEFAULT, 0, &s_musicModule);
        FMOD_ERRCHECK(result);

        result = g_fmod->playSound(FMOD_CHANNEL_FREE, s_musicModule, true, &s_musicChannel);
        FMOD_ERRCHECK(result);

        EnableMusic(musicOn);
        s_musicModule->setLoopCount(-1);
        s_musicChannel->setPaused(false);

        musicSelection = which;
        s_musicPaused  = 0;
    }
}

If someone wants to experiment with this, let me know and I'll upload the project somewhere. My gut feeling is that FMOD is busted but I'd love to be proven wrong.

StilesCrisis
  • 15,972
  • 4
  • 39
  • 62

3 Answers3

2

Sounds like your music needs to be set as higher priority than your other sounds. Remember, lower numbers are more important. I think you can just set the priority on the channel.

  • This worked like a charm. I didn't realize the WAVs defaulted to equal priority with the MODs, meaning they could steal the channel. Thank you! – StilesCrisis Feb 21 '15 at 19:35
  • Well, this mostly worked. The music never cuts out. But if I run the game for long enough, eventually all the WAVs stop playing back; the music never stops, though. FMOD isn't returning any errors. – StilesCrisis Feb 21 '15 at 20:11
2

Every time I play the following WAV, FMOD loses one channel permanently. I am able to reproduce this channel-losing behavior in the "playsound" example if I replace the existing jaguar.wav with my file.

https://drive.google.com/file/d/0B1eDRY8sV_a9SXMyNktXbWZOYWs/view?usp=sharing

I contacted Firelight and got this response. Apparently WAVs can include a looping command! I had no idea.

Hello John,

I've taken a look at the two files you have provided. Both files end with a 2 sample infinite loop region.

FMOD 4 (and FMOD 5 for that matter) will see the loop region in the file and automatically enable FMOD_LOOP_NORMAL if you haven't specified any loop mode. Assuming you want one-shot behavior just pass in FMOD_LOOP_OFF when you create the sound.

Kind regards, Mathew Block | Senior Platform Engineer

Technically this behavior contradicts the documented behavior of FMOD_DEFAULT (which is specified to imply FMOD_LOOP_OFF) so they are planning to improve the documentation here.

StilesCrisis
  • 15,972
  • 4
  • 39
  • 62
  • Thanks for the info. I had a hard time finding credible contenders for MOD playback in 2015 :) FMOD's glitchiness appears to be due to unusual sample rates in the WAV—the linked one was 22254Hz (classic Mac OS sounds used this frequency) and I have another sound at 11127Hz which also exhibits the glitch. I can work around the issue just by resampling the WAV. Annoying, but not worth a rewrite. – StilesCrisis Feb 25 '15 at 07:34
  • I've never used FMOD, but I'm surprised it doesn't have a re-sampling function. The bass and bassmod libs have been around for ages, and can do just about anything you might imagine. I've tried a few others over the years, but have yet to find anything that sounds as good — especially for playing mod files. :) – l'L'l Feb 25 '15 at 07:46
  • Who said FMOD doesn't have a resampler? Of course they do :) It just has a channel leak bug here, inexplicably. – StilesCrisis Feb 25 '15 at 15:33
  • I see — from your original comment it sounded as if it did not have the capability, which in turn was causing the issues. As I understand now, the issue is the resampling not working properly. – l'L'l Feb 25 '15 at 16:11
  • Well, I was still working on understanding the issue at the time. And I don't know why FMOD is leaking the channel when playing my WAV, to be clear. The only interesting thing about this WAV that I can see is that it has an atypical frequency, and replacing it with a WAV that has a normal frequency seems to resolve the issue, but without peering into the FMOD source I can't say with certainty that the bug is in the resampler. – StilesCrisis Feb 25 '15 at 19:37
2

Based on the wave sample you supplied, FMOD is behaving correctly as it appears you've figured out. The sample has a loop that is honored by FMOD and the last samples are simply repeated forever. While useless, this is correct and the variance in the samples is so slight as to not be audible. While not part of the original spec for wave format, extended information was added later to support meta data such as author, title, comments and multiple loop points.

Your best bet is to examine all your source assets for those that contain loop information. Simply playing all sounds without loop information is probably not the best workaround. Some loops may be intentional. Those that are will have code that stops them. Typically, in a game, the entire waveform is looped when looping is desired. You can then write or use a tool that will strip the loop information. If you do write your own tool, I'd recommend resampling the audio to the native output sampling rate of the hardware. You'd need to insure your resampler was sample accurate (no time shift) and did not introduce noise.

Historically, some game systems had a section at the end of the sound with silence and a loop point set on this region. The short reason for this was to reduce popping that might occur at the end of a sound in a hardware audio channel.

Curiosly, the last 16 samples of your .wav look like garbage and I'm wondering if the .wav assets you're using were converted from a source meant for a game console and that's where the bogus loop information came from as well.

This would have been a comment but my lowly rep does not allow it.

ChocoBilly
  • 409
  • 3
  • 10
  • I am the author of the WAVs so I don't need to worry about the looping data being intentional. It's not :) The data was converted from Mac 'snd' resources back in 2001; unfortunately I don't remember which tool I used. The original game did not rely on any special 'snd' tricks. They're just supposed to be plain sampled sounds. – StilesCrisis Mar 03 '15 at 15:45
  • @StilesCrisis Lucky you know the source. Is that WAV unique in having a loop point? Also, do the last 16 samples look like noise to you? One-shot game sounds typically have a fade to 0 so they don't pop. Many (including older) systems and sound card drivers automatically apply an ADSR (attack-decay-sustain-release) type effect to a WAV playback which mainly eliminates pops from a waveform not starting or ending on 0. You might want to do a quick spot check of some of your other source waves. – ChocoBilly Mar 04 '15 at 04:53