Teensy-Based Sampler: "dynamic" Memory Allocation?

blakeAlbion

Well-known member
I have a Teensy with some extra RAM soldered on. I can play a modest amount of uncompressed WAV samples I load from SD.

This is a multitimbral sampler and I want to be able to switch samples quickly: I want to have Voice One load a sample, play it, and then if I switch to another "kit" I want it load another sample at runtime.

I can define blocks of memory to load my samples from SD.

EXTMEM char MEM_BLOCK_1[MAX_EXTMEM];
EXTMEM char MEM_BLOCK_2[MAX_EXTMEM];
EXTMEM char MEM_BLOCK_3[MAX_EXTMEM]; etc

But, this is not efficient because some samples are short, and some are long. I tend to waste memory by giving each "sample voice" in my multitimbral sample and equal amount of fixed memory.

I think I'm missing something. It looks like some folks use Flash memory to hold samples, and thereby eliminate the need for a "Teensy Memory Manager" that can malloc and free EXTMEM without fragmentation. But I'm not quite seeing the connection.

So I have two questions.
1) Can I quickly load, and then swap a sample from flash memory? Can I do it a thousand times without a hitch?
2) Just curious, are there "memory management libraries" you can use in a Teensy without embracing a complete RTOS paradigm?
( I would expect I could pass a big block of EXTMEM into a library and it would handle allocation/deallocation. )

I know these are multiple divergent questions, but I would appreciate any ideas.

Thanks,

Ben
 
...What I'm implying is I want it to act like a sampler that can pull in samples from SD at runtime without hard-coding any samples as constants or as resources that get loaded in on setup().
 
Hi blakeAlbion!

Could you share code for your solution, I have been battling the exact same problem with sample playing from SD, but memory allocation is still a bit too much for my skills at the moment.
It would be super-helpful if I could get a working example code of how you got it working.

At the moment, I have been using WaveplayerEx library for playing straight from the SD card, but that has still some limitations regarding polyphony. What kind of polyphony are you achieving with your code?

Best,
Miro
 
Okay, I got it working but for some reason with the latest Teensyduino I have to reduce my ask for heap size.

So, sm_malloc is a tiny baby malloc() which works for your soldered-on RAM. You can by Teensy 4.1 boards from Protosupplies with the RAM already attached.

This code is bodged together from forum snippets.

#include <static_malloc.h> // Paul S included this in the core.

constexpr size_t myHeapSize = 7898116;
EXTMEM uint8_t myHeap[myHeapSize];


size_t oom(struct smalloc_pool * whichPool, size_t sizeReported) {
Serial.println("OOM Bummer");
return sizeReported;
}

void initExtMem() {
checkStaticMemory();
// https://forum.pjrc.com/threads/63695-malloc-free-for-EXTMEM-and-DTCM?highlight=sm_malloc
::sm_set_default_pool(myHeap, myHeapSize, 1, &oom);
checkStaticMemory();
}

void checkStaticMemory() {
size_t total = 1;
size_t user = 1;
size_t freed = 1;
int nBlocks = 1;
/*
get stats: total used, user used, total free, nr. of allocated blocks.
any of pointers maybe set to NULL, but at least one must be non NULL.
*/
::sm_malloc_stats_pool(&smalloc_curr_pool, &total, &user, &freed, &nBlocks);
Serial.println("&&&&&&&&& checkStaticMemory: " + String(total) + ", " + String(user) + ", " + String(freed) + ", " + String(nBlocks));
}


//////////// Usage. So now in setup() you must call initExtMem() ////////

/*
My sampler was using pre-allocated BLOCKS of RAM, meaning without some kind of new()/delete()/malloc()/free() pattern, so it had to have huge blocks just in case one sample was too big. This was wasteful of memory. If the average sample is small/medium, why make every block big?

So I replaced references to giant byte arrays with normal memory allocation patterns like C++ would use in a big-boy operating system:
*/

// use sm_zalloc to give me a pointer to nulled-out bytes
m_raw_memory_block = ::sm_zalloc(m_file_size);

// and when I am done I use sm_free

void PitchControlledSamplePlayer::purge() {
if (m_sample_data_loaded != nullptr) {
stop();
::sm_free(m_raw_memory_block);
m_file_size = 0;
m_raw_memory_block = nullptr;
m_sample_data_loaded = nullptr;
}
}
 
Last edited:
Wondering with your code here, how well what you have works with allocating all of your 8MB to your own allocator,
And then in startup.c There is code to initialize the rest of the external chip to use with: extmem_malloc() ?

As far as I can tell, using the blame view up on Github, I don't think any of the startup.c code associated with this has changed since maybe 3 years ago.

But maybe the compiler differences might work different ...

Edit can not build as don't have your header file static_malloc.h
 
A picture is worth a thousand chars.
drummydrum.jpg
 
Back
Top