Circular Buffer using Virtual Memory
I'm experimenting with audio decoding and playback using PortAudio at the mo. Initially I found that it isn't a good idea to decode the audio in the same thread as playback (duh :o), so I have split the application into two threads: one decodes the audio and the other copies blocks to PortAudio's buffer on callback.
Each time the playback thread copies decoded audio it signals the decoding thread via an Event. The decoding thread wakes up, decodes another block of audio and goes back to sleep.
Here's where it gets tasty: I don't intend to have a 50+ MB buffer holding the decoded audio, instead my ideal size is a one second buffer. Newly-decoded blocks overwrite blocks that have been sent for playback, thus creating a circular buffer.
I have tried a traditional implementation of a circular buffer, that typically involves one to two memcpy()s, depending on how close it is to the end of the buffer. This works well, but for one reason or another I keep hearing pops and clicks as both threads wrap around. It seems as if playback catches up with decoding too quickly. This would make sense, except that I've temporarily disabled the "decoding" element and am just reading raw samples from file into the buffer.
Reading around, I found out that it is possible to use Windows' virtual memory management to your advantage, effectively mapping the same block of physical memory over and over into consecutive regions of virtual memory. I could have a virtual region the same length as the file, but still only using one second's worth of physical memory. :cool:
This is the code that I devised to do this:-
This works brilliantly in the sense that I could dismantle all the pointer arithmetic, etc. and the clicks/pops ceased, but looking at the Task Manager, the memory usage of the process keeps increasing as time goes on. Is this just a count of virtual memory, or have I not done this properly? :confused:
// nSeconds is the length in seconds of the file, rounded up
hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_RESERVE, 0, nSeconds * 327680, NULL); // create a map for the whole range
pSound->pSound = MapViewOfFileEx(hMap, FILE_MAP_WRITE, 0, 0, 0, NULL); // find a virtual address that would accommodate it
UnmapViewOfFile(pSound->pSound); // clear down
hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_RESERVE, 0, 327680, NULL); // create a map for just the second-long buffer
MapViewOfFileEx(hMap, FILE_MAP_WRITE, 0, 0, 327680, pSound->pEnd); // map the address to the second-long buffer
pSound->pEnd += 81920; // increment float pointer (327680 bytes)
VirtualAlloc(pSound->pSound, 327680, MEM_COMMIT, PAGE_READWRITE); // "bind" the second-long buffer to memory