Title Different channel behavior when switching from Core API to Studio API: after sound finishes Channel::* calls fail

Windows (x64), C++
FMOD: 2.03.09

I have a working setup with the Core API:

CHECKFMODERR(FMOD::System_Create(&system));

CHECKFMODERR(system->setSoftwareChannels(MAX_SOUNDS_SLOTS));

CHECKFMODERR(system->init(MAX_SOUNDS_SLOTS, FMOD_INIT_NORMAL, nullptr));

CHECKFMODERR(system->set3DSettings(1.0f, DISTANCEFACTOR, 1.0f));

// per frame:

CHECKFMODERR(system->update());

When I switch to the Studio API (but still play sounds via low-level System::playSound):

CHECKFMODERR(FMOD::Studio::System::create(&studiosystem));

CHECKFMODERR(studiosystem->getCoreSystem(&system));

CHECKFMODERR(system->setSoftwareChannels(MAX_SOUNDS_SLOTS));

CHECKFMODERR(studiosystem->initialize(MAX_SOUNDS_SLOTS, FMOD_STUDIO_INIT_NORMAL, FMOD_INIT_NORMAL, nullptr));

CHECKFMODERR(system->set3DSettings(1.0f, DISTANCEFACTOR, 1.0f));

// per frame:

CHECKFMODERR(studiosystem->update());

Observed:

  • After a short sound naturally finishes, any calls on the channel (e.g., channel->getPaused, channel->isPlaying) sometimes do not return FMOD_OK. With pure Core init I get FMOD_OK and isPlaying=false in the same situation.
  • Virtualization behaves differently: channels become virtual more often / seem to “disappear” earlier, even though I did not change virtualization settings.

Questions:

  1. Is it expected that the lifetime/validity of low-level channels (FMOD::Channel) differs when initialized via Studio API vs pure Core init? In my case, this is critical (see the code below).
  2. Can Studio API be configured to behave as close as possible to Core (same/near virtualization behavior and the same return codes after a sound ends)?

Additional notes:

  • Channels are created with system->playSound (not Studio events).
  • With Core API the current logic is stable; issues appear immediately after switching Init/Update to Studio.

Small code excerpt where the issue manifests (cleanup after completion):

bool paused = true;
FMOD_RESULT status = PlayingSounds[i].channel->getPaused(&paused);
if (status != FMOD_OK) {
    core.Trace("PlayingSounds[%d].channel 0x%08X %s paused %d status %d",
               i, PlayingSounds[i].channel, PlayingSounds[i].Name.c_str(), paused, status);
    i = FreeSound(i);
    continue;
}

if (paused) continue;

bool is_playing;
status = CHECKFMODERR(PlayingSounds[i].channel->isPlaying(&is_playing));

if (!is_playing || status != FMOD_OK) {
    if (PlayingSounds[i].aliasLoopIdx != -1)
        AliasRandomLoopSound(i);
    else {
        PlayingSounds[i].channel = nullptr;
        if (i <= 1 && OGG_sound[i] != nullptr) {
            CHECKFMODERR(OGG_sound[i]->release());
            OGG_sound[i] = nullptr;
        }
        i = FreeSound(i);
        int32_t soundId = (i + 1) | (PlayingSounds[i].stamp << 16);
        core.Event("SoundEnded", "l", soundId);
    }
}

In Core, checking the paused state worked fine, but in Studio, sounds that have finished playing immediately return FMOD_ERR_INVALID_HANDLE. Even if I rewrite this logic, the channel becomes completely inaccessible, including losing info like its last 3D coordinates, which is important for restarting a random sound from a specific list at the same location.