Sound memory management

Hi

I’m a fmod newbie :slight_smile:
I’m looking for replacing my sound mobile engine, so I start looking on your engine.
In my engine, I’ve implemented a caching system (kind of LRU) for keeping the most “popular” sounds in fast access.

I didn’t understand if your engine do it for me, or should I still manage it?
If I still need it, it means that as long as I didn’t call to sound.release() it will be released? if yes, what will happen if I use a fixed memory pool size?

Thanks in advance,
Rafael

You can read about sample data loading in our documentation, but basically you manage the sample loading directly via loadSampleData or indirectly via the idle pool for a size constrained LRU list.

Hi Mathew,

Thanks for your quick reply.
I’m going to use core API not studio API (I already have many sound files in ogg format, I’m not going to convert them into FSB format), there is an idle pool also in core API? (I didn’t find).
If there is no idle pool for the core API, so I need to keep my LRU implementation, can I achieve it by creating sound and not release till my pool is full and another sound is need to be played?

I have another memory question, the pool size is a different memory block in addition to the block I allocate in init phase?
What init memory block is used for(FMOD::Memory_Initialize)?

You will need your own LRU implementation when using the Core API, holding onto the FMOD::Sound object and not calling Sound::release until you need to recover memory would be the way to go.

Specifying a memory block in Memory_Initialize sets an absolute heap size for all of FMOD, instance data and sample data together. We will only allocate from that block and never call the platform allocator. If you do this, you must ensure sufficient head room so that you don’t run out of memory, as that is a fatal condition.

One more thing, which openState considered as not in use?

That would be FMOD_OPENSTATE_READY or FMOD_OPENSTATE_ERROR, depending on if there is a problem or not.

I’ve just implemented my LRU cache and I have a question.

My goal is 25MB cache, but because I load all sounds in a non blocking way, I can’t really know what is the PCMBYTES length till it’s loaded. So if I’m under 25MB I load everything.
My system build in a way that all the sound management is happening on one thread, so when “onSoundLoaded” callback called I pass it to the management thread. In the management thread I check the size and if there is not enough space I clean the cache for the new sound.
The problem is that check FMOD memory tells me that I reached 50 MB max, but now I’m 31MB (Do you really needs 6MB management overhead?)

Currently I don’t set a memory pool size during system init, but I don’t really understand what will happen in my case, let’s say I will allocate only 25 MB, what will happen with all the non-blocking sounds that actually exceeded the 25MB limit?

If you set FMOD to operate in a memory pool and that size is breached FMOD will fail with a memory error, it’s important to not exceed the available memory when using a pool.

It’s hard to make guesses about where the unaccounted for 6MB is going, if you are using the Core API only there shouldn’t be a lot of overhead on top of your sample data. Once way to get some more info is to use FMOD_Debug_Initialize and set the memory flags on, this will show you any time FMOD allocates memory which you can compare to the APIs you’re calling to make sense of the difference. Another option is to override the memory callbacks to receive info about when allocations are happening and what their size is.

I just turn on debug mode and observed tens of allocation and deallocation (2KB) per second, just after init FMOD before I did any operation. Is it suppose to be like that?

This is my init code:

void AudioController::initialize()
{
    PLATFORM_ENSURE_MAIN_THREAD;
    
    FMOD_RESULT   result;
    unsigned int  version;
    void          *extradriverdata = 0;
    
    LOG_DEBUG("START AudioController initialize");
#ifdef __APPLE__
    activateAudioSession();
#endif
    
    result = FMOD::System_Create(&m_system);
    ERRCHECK(result);

    result = m_system->getVersion(&version);
    ERRCHECK(result);

    if (version < FMOD_VERSION)
    {
        LOG_ERROR("AudioController - FMOD lib version %08x doesn't match header version %08x", version, FMOD_VERSION);
    }

    result = m_system->init(NUMBER_OF_CHANNELS, FMOD_INIT_NORMAL & FMOD_INIT_THREAD_UNSAFE, extradriverdata);
    ERRCHECK(result);
#if DEBUG
    ERRCHECK(FMOD::Debug_Initialize(FMOD_DEBUG_TYPE_MEMORY));
#endif
    
    m_system->setStreamBufferSize(STREAM_BUFFER_SIZE, FMOD_TIMEUNIT_PCMBYTES);
    
    result = m_system->createChannelGroup("Sources-Group", &m_effect_group);
    if (result != FMOD_OK)
    {
        LOG_ERROR("AudioController - faild to create channel group Sources-Group");
        return;
    }
    
    // set maximum for the buffers and priority buffers size
    m_audio_buffers_cache.init();
    
    LOG_DEBUG("START AudioController complete");
    m_is_audio_controller_initialized = true;
}

This my log:

[LOG] OutputCoreAudio::init                    : Maximum hardware read size: 4096 samples, Software buffer size: 512 samples, Number of software buffers: 4.
[WRN] OutputCoreAudio::init                    : DSP buffer size * DSP num buffers is potentially too small.
[WRN] SystemI::init                            : Output requires a sample rate of 48000Hz, resampling will occur.
[LOG] Thread::initThread                       : Init FMOD stream thread. Priority: 2, Stack Size: 98304, Semaphore: No, Sleep Time: 10, Looping: Yes.
[LOG] Thread::initThread                       : Init FMOD mixer thread. Priority: 1, Stack Size: 81920, Semaphore: Yes, Sleep Time: 0, Looping: Yes.
[LOG] MemPool::alloc                           :    600 bytes (0x10650cfd8) (alloc 151)
[LOG] MemPool::alloc                           :    704 bytes (0x10650c4c8) (alloc 152)
[LOG] MemPool::alloc                           :     22 bytes (0x280da9868) (alloc 153)
[LOG] MemPool::alloc                           :   2064 bytes (0x10fe44700) (alloc 2)
[LOG] MemPool::free                            :   2064 bytes (0x10fe44700)
[LOG] MemPool::alloc                           :   2064 bytes (0x10fe44700) (alloc 3)
[LOG] MemPool::free                            :   2064 bytes (0x10fe44700)
[LOG] MemPool::alloc                           :   2064 bytes (0x148ae8700) (alloc 14820)
[LOG] MemPool::free                            :   2064 bytes (0x148ae8700)
[LOG] MemPool::alloc                           :   2064 bytes (0x148ae8700) (alloc 14821)
[LOG] MemPool::free                            :   2064 bytes (0x148ae8700)

I can’t debug the real problem this way (or may be it’s the real problem :slight_smile: )

Looks like those are memory allocations from our internal pools, they probably shouldn’t be reported the same as normal allocations. Instead of using the logging, try Memory_Initialize and implement your own callbacks (simple wrappers of malloc and free will suffice. That way you can perform your own logging of the real memory.

Even when I use my own allocators I get those allocations, the source file of those allocations is

/Users/raymond/jenkins/workspace/Build__2.0__API_iOS/core_api/platforms/ios/src/fmod_os_misc.cpp(510)

BTW, I’m using last core engine (2.01) but the print is 2.0, maybe I missed something?

One more question (sorry for the long thread).
I want to use 128K stream buffer per stream, so I set the following:

FMOD_ADVANCEDSETTINGS advance_settings;
result = m_system->getAdvancedSettings(&advance_settings);
advance_settings.defaultDecodeBufferSize = STREAM_BUFFER_SIZE;
m_system->setAdvancedSettings(&advance_settings);
m_system->setStreamBufferSize(STREAM_BUFFER_SIZE, FMOD_TIMEUNIT_PCMBYTES);

but when I check sound pcm bytes length (also raw bytes) it’s way bigger that 128K or 256K.

I probably don’t get the idea, I thought this is the buffer that fmod allocates for the particular stream sound. Please explain what wrong.

/Users/raymond/jenkins/workspace/Build__2.0__API_iOS/core_api/platforms/ios/src/fmod_os_misc.cpp(510)

That allocation is creating a mutex, there will be many minor allocations for things like that during initialization time. That path is directly from our internal source control, the code was built from the 2.0 branch, I’d double check your iOS binaries have been updated to 2.1.

when I check sound pcm bytes length (also raw bytes) it’s way bigger that 128K or 256K.

When you query the length of a Sound you get the actual length of the data, not the stream buffer size.

So I can be sure that the size of stream buffer is not more than the size I set during initialization? (128K or twice?)

Regarding iOS binaries, I’m using latest version 2.01.04, I don’t see 2.1 version available for downloading.

When streaming FMOD will perform double buffering of the size you give, so the memory allocation will be exactly double the value you pass to setStreamBufferSize. Keep in mind for exact byte size, use FMOD_TIMEUNIT_RAWBYTES, using FMOD_TIMEUNIT_PCMBYTES, PCM or MS will require us to estimate how big to make the buffer based on the requested size in samples.

Also apologies for my naming, 2.1 is 2.01, 2.0 is 2.00, shortcut naming can confuse matters. 2.01.04 is indeed the latest release, however the logging indicates you’re actually using a version from the 2.00 branch. Perhaps try calling System::getVersion is be sure.

I don’t get it. I don’t understand what is going on with fmod memory allocations.
I set the stream size to be 128KB in a raw bytes but when I check Sound (stream) length (in rawbyte) is about 380KB.
In general, I’m trying to manage my cache buffer using your core API, and for that I need to know what is the amount of memory used for each sound object. no matter which timeunit (also tried using FMOD_ACCURATETIME ) I check it’s not correlate with the increasing when check with FMOD_Memory_GetStats.

Can you explain what is the best way I can manage my cache? I only need to know the real memory use when insert or remove sound.
BTW, can I check memory use before loading the entire file, and then keep use the same sound (fd I guess) to start buffering? (OPENONLY)

When you query the length of the streaming Sound, you are getting the length of the file. So if the file is 1MB, Sound::getLength will return you 1MB, regardless of the streaming buffer size.

If you setStreamBufferSize as 128KB (raw bytes), FMOD will allocate 256KB (double buffer) to perform that streaming. This is not the only memory FMOD will allocate for a Sound, but it is the largest contribution when loading streams. If you want to know the exact amount of memory used by the Sound you would need to call Memory_GetStats before and after createSound, calculating the difference.

Using OpenOnly will allow you to check memory usage without doing the initial buffering if you want to defer that processing for later.