mstop4 / FMODGMS

🎶 GML bindings to the FMOD Studio low-level API for GM:S and GMS2. Can be used in Windows, macOS, and Linux games.
https://quadolorgames.itch.io/fmodgms
BSD 2-Clause "Simplified" License
62 stars 18 forks source link

Changed vectors to maps so sound/channel/effect IDs don't change as you unload them #11

Closed rissole closed 6 years ago

rissole commented 6 years ago

Problem scenario

Consider this:

global.bgm = FMODGMS_Snd_LoadSound("bgm");
global.temporarySound= FMODGMS_Snd_LoadSound("temporarySound");
global.oneLastSound= FMODGMS_Snd_LoadSound("oneLastSound");

// LoadSound returns the sound's index in the internal soundList, so at this point
// bgm = 1, temporarySound = 2, and oneLastSound = 3
// (let's pretend counting starts at 1.)

You load these up and go about your game, and the internal soundsList vector looks like this:

  1. bgm
  2. temporarySound
  3. oneLastSound

But eventually the time comes to unload temporarySound.

FMODGMS_Snd_Unload(global.temporarySound);

Let's have a look at what this does under the hood:

// Unload a sound and removes it from soundList
GMexport double FMODGMS_Snd_Unload(double index)
{
   // ... some validation of input, etc ...
   soundList.erase(soundList.begin());
   return GMS_true;
}

Yikes, it for some reason removes the first item in the sound list. Let's take another look at our internal soundList now:

  1. temporarySound
  2. oneLastSound

yet in GameMaker, our globals remain the same!

// bgm = 1, temporarySound = 2, and oneLastSound = 3

Holy moly. Now PlaySound(global.bgm) is going to start playing temporarySound again, even though we just unloaded that sound!

The solution

We have 3 problems here:

  1. Unloading a sound only ever removes the first sound in the list, regardless of the index passed.
  2. The sound is never actually freed using FMOD::Sound::release.
  3. Sound indexes can change after removing sounds from the soundList. For example, removing temporarySound will change oneLastSound's index from 3 to 2 in the example, even if we fixed problem 1).

In this PR, we don't store sounds in a vector, instead we store them in a map. Each sound/channel/effect is allocated a unique index when it's created. When we remove something from a map, other keys are unaffected. The sound's index after being sent to Game Maker will remain valid until it's unloaded (properly, using FMOD::Sound::release).

I tested this quickly and it fixed my unloading problem, and the demo project still worked, but since there's so many changes it's still pretty risky to merge. I'd recommend some unit tests and playing around with this yourself first.

mstop4 commented 6 years ago

Hi rissole, thanks for your contribution. I'll try to check this out as soon as I can.