SYNTHINGS is a digital synthesizer development platform that I've been building.
jason | 15 January, 2011 23:21
Today, I went in a bit of a circle with the memory allocation for the tune.
I started off in two minds as to the best approach for this. I need to have it quick to update in the screen, and easy to insert events in the middle of the data. I've got 32K of SRAM assigned to the tune arrangement, and only 6K of RAM in the PIC to hold it in (this memory is given to the pattern editor when you're editing the pattern).
I could split the memory into 16 equal chunks (one for each voice), but then I'd limit myself to 512 pattern events per voice (each pattern placement takes 4 bytes - two for the start location, and another two for the pattern number) - and if you were doing an 8 voice tune, that's half the memory wasted.
I initially decided to have all the tune pattern arrangement to be linear, and sorted by the play time, so drawing the pattern would be easy. In order to make it easy to insert items in memory, I would split the 32K into 256 128-byte chunks, and use a double-linked list to make it easier to insert data in the middle of other data. Memory would get fragmented, so a defragmentation scheme was done.
While writing the insertion code, I decided that this was way to complicated. I had a 128-byte memory allocation routine in the pattern editor, and another one in the tune - both doing similar, yet different things.
I then decided to merge the memory chunks together (they were already immediately after each other), and have the pattern and tune use a common memory allocation system.
The memory code was removed from the pattern editor, and placed in its own memory handler, and I changed the memory code to have a concept of objects. An object would be a pattern, or a voice line in the tune editor. An object would need a number of blocks depending on how much memory the object needs. Each block allows 124 bytes of data (the other 4 are used to link to the next block, the parent object, and how many bytes are used in the block.
The memory transfer (load and save) functions were a bit more lower level than the pattern ones (which needed to insert the pattern name, length and colour in the first block). This meant that the pattern save and load now needed to compress and decompress while knowing that the pattern information needed to be at the start of the memory block.
On startup, the tune editor would create 16 objects - none of which would use any memory. When you create a new pattern, then a new object is created. As an extension, if a new pattern is created, and you abort without saving, then it'll free the object back.
With the new memory arrangement, there's 3360 available blocks and objects (which is up from the 3072 patterns). I can increase this slightly (there's an area of memory used to store the object start addresses, and I've got more memory allocated there than I need).
The displaying of the tune will be a little more complex than before, but hopefully it shouldn't be too bad. The memory footprint hasn't changed that much (an extra 136 bytes of ROM, and 32 bytes of RAM):
Program Memory [Origin = 0x200, Length = 0x2aa00] section address length (PC units) length (bytes) (dec) ------- ------- ----------------- -------------------- .text 0x200 0x136e 0x1d25 (7461) .const 0x156e 0x26d6 0x3a41 (14913) .text 0x3c44 0xc4be 0x1271d (75549) .dinit 0x10102 0x24c 0x372 (882) .text 0x1034e 0xde0 0x14d0 (5328) .isr 0x1112e 0x2 0x3 (3) Total program memory used (bytes): 0x196c8 (104136) 39% Data Memory [Origin = 0x800, Length = 0x7800] section address alignment gaps total length (dec) ------- ------- -------------- ------------------- .nbss 0x800 0 0x9ea (2538) .ndata 0x11ea 0 0x2 (2) _03774ee04d2cd01c 0x11ec 0 0x200 (512) _037770704d2cd01c 0x13ec 0 0x200 (512) .nbss 0x15ec 0 0x314 (788) .ndata 0x1900 0 0x86 (134) .nbss 0x1986 0 0xa0 (160) .ndata 0x1a26 0 0x20 (32) .nbss 0x1a46 0 0x38 (56) .ndata 0x1a7e 0 0x22 (34) .nbss 0x1aa0 0 0x1c (28) .ndata 0x1abc 0 0x8 (8) .nbss 0x1ac4 0 0x1a (26) .ndata 0x1ade 0 0x2 (2) .nbss 0x1ae0 0 0x4 (4) .ndata 0x1ae4 0 0x4 (4) .nbss 0x1ae8 0 0x2 (2) .ndata 0x1aea 0 0x2 (2) .data 0x1aec 0 0x106 (262) .dconst 0x1bf2 0 0x8 (8) .data 0x1bfa 0 0xa (10) .bss 0x1c04 0 0x4 (4) .data 0x1c08 0 0x2 (2) .heap 0x1c0a 0 0x2000 (8192) Total data memory used (bytes): 0x340a (13322) 43% Dynamic Memory Usage region address maximum length (dec) ------ ------- --------------------- heap 0x1c0a 0x2000 (8192) stack 0x3c0a 0x43f6 (17398) Maximum dynamic memory (bytes): 0x63f6 (25590)
Tomorrow, I'll need to start arranging the pattern - and maybe have a go at loading and saving whole tunes. There's nothing worse than creating a wonderful test tune and not being able to save it...