The first area to try and optimize is the number of DMA buffers. These buffers are used by the audio synthesizer to store samples from the cartridge during creation of the output buffers. In the worst case scenario you will need four buffers for every voice you have allocated. However, in practice you need only a portion of that. The actual number of buffers you will need is very dependent on the sequences and sound effects played. To optimize this value, you will need to allocate sufficient buffers to keep from crashing, and then play your game for a while. At the end of each frame you should be calling a routine that frees DMA buffers that have become stale. In example programs this will be called __clearAudioDMA. In this routine, before discarding stale buffers, step through the list of used DMA buffers and count how many there are. If you keep track of the maximum value, you can report this at the end of game play, using your choice of debugging method. The following code is an example of how to perform this count.
#ifdef AUD_MEM_PROF ampDMAcount = 0; dmaPtr = dmaState.firstUsed; while(dmaPtr) { ampDMAcount++; dmaPtr = (AMDMABuffer*)dmaPtr->node.next; } if(ampDMAcount > ampMaxDMABufs) ampMaxDMABufs = ampDMAcount; #endif
Because the number of buffers used can be very few, even when playing the same BGM and sound effects, it is always a good idea to have a few more (2~3) buffers than you ever found yourself needing.
In addition to the number of DMA buffers needed, it is helpful to know what is the maximum number of DMAs performed in any frame. This number will allow you to optimize the number of DMA message buffers you will need. Because the size of a message buffer is substantially less than the size of a DMA buffer, the result of this optimization is not much. However, it is easily performed since there is a variable that reports the number of DMAs done each frame. All you need to do is record its maximum value, checking it once a frame, and then report that value at the same time you report the number of DMA buffers used.
Another place for optimization is the length of the DMA buffers. Longer buffers will require fewer buffers, and use fewer DMAs. Conversely, smaller buffers will require more buffers and more DMAs. Generally, the smaller buffers, even though more are required, will use memory more efficiently. However, the smaller buffer sizes will also generate more DMAs and for that reason are less efficient in terms of processing time. It is up to the developer to decide what trade off between memory usage and processing time to pick. Optimal buffer sizes are probably ones that will handle enough samples to process one frame of audio. Below, is a table that compares the same music played back with various buffer sizes. (All other factors were the same.)
DMABufLength | MaxDMA/Frame | MaxDMABuffers | BufLen*MaxBuf |
---|---|---|---|
0x600 | 12 | 26 | 39936 |
0x500 | 12 | 30 | 38400 |
0x400 | 14 | 34 | 34816 |
0x300 | 16 | 38 | 29184 |
0x280 | 17 | 43 | 27520 |
0x200 | 22 | 50 | 25600 |
As can easily be seen, the amount of buffer space needed increases as the size of the buffers increase, even though fewer buffers are needed. However, at the same time, the number of DMAs decreases. In this case, probably the value of 0x500 is optimal, since it causes the least number of DMAs per frame in the worse case situation, but allows the memory allocated to buffers to be smaller than it would be with buffers of 0x600 size.
Another constant that can be changed is FRAME_LAG. This value defines how long a DMA buffer will be kept after it has been used. If you continually use the same sample, that sample will be kept in memory, and will not need to be DMA-ed again. Higher FRAME_LAG values will lower the number of DMAs but will increase the number of DMA buffers needed.
Like the number of DMA buffers, the command list size is dependent on the sequences and sound effects used by the game. To optimize the command list size, simply record the maximum value used, and check that value at the end of game play. Because this can vary, even when playing the same audio, it is wise to leave a little more than you ever needed.
The output buffer size is determined by the audio playback rate and the frame rate. If you synch audio to the vertical retrace you will need to have three audio output buffers. If you synch the audio to the audio completion interrupt, you will only need to have two output buffers. Example code is included in the example applications demonstrating calculating the size of the output buffers.
The audio thread stack size can be determined using the stack tool, and optimized accordingly.
Synthesizer update buffers and sequencer event buffers are allocated from the audio heap when the synthesizer and sequencer are created. There is, at present, no way to efficiently optimize these values. However, because the size of each buffer is small, it is better to allocate buffer a bit more than necessary.
Once all calls to alHeapAlloc have been completed, you can determine the amount of the heap that has been used by subtracting the heap's current value from the heap's base value. These values are part of the heap structure.
The sequence buffer needs to be large enough to hold the largest sequence that will be used.
The bank control file buffer needs to be large enough to hold the bank control file. This is the <bank>.ctl file.