Best way to determine buffer size
I am working on an application that takes realtime acquired data (from a DAQ board) and outputs it to the speakers via DirectSound. The software project does a lot of toher things too, so to some extent the overall structure is fixed.
Data makes it's way to the class responsible for spitting it out via DirectSound on a SW buffer. As it arrives I push it onto a queue. I have a timer that goes off (every 1/8 sec) and determines how much data DriectSound's buffer needs. It pops it out of the buffer, does a little processing (dynamic volume), resamples it to 44.1kHz, and copys it over into DirectSound's buffer.
Now the question is how do I figure out the optimal size of that queue. I'd like it to be as small as possible. But at the same time I have to have enough of a buffer such that if some data arrives a little late, there is some left in the queue to give to DirectSound. Now that will obviosly vary based on many end-user parameters. Also, if I am short by a single sample, dropping just that sample results in an annoying "click", so I'd rather "clump" buffer underflows, by predicintg them.
Also, I know I need to correct for the long term skew of the two clocks. It would be nice if I could do this a bunch of samples at a time as well.
Right now I have a 1.25 sec delay between the time data arrives at the DAQ board and the time it is spat out (timed on a dual trace scope). I timed some portions of the code which I have little control over and there is about 0.125 sec delay there. So between the DirectSound buffer and my queue, there is probably at least 1 sec of data. How do I det that down to about 0.25 sec?
Just make the queue large enough. Because once you find just the right size for the queue you'll know windows will find some other process to give more time, just to kill your carefully calculated queue.
Don't bother with a queue. Use a ring buffer of sample frames. Make the frames as big as dwBufferBytes when you create a DS buffer (just grab the primary since you don't need to mix anything).
Set the size of the ring buffer dynamically based on the buffer protection time (make that a user-configurable number of milliseconds) divided by the time-length of each frame (taking into account channels, sample rate, and frame size).
Then what you do is allow the DAQ process to place the raw data at the head of this circular buffer and let it wrap around. Force the DAQ to wait until it has a full frame before adding to the ring buffer and advancing the head. This means the DAQ thread will never wait/block for any reason and you just focus on getting your other threads to keep up.
Meanwhile have your audio munging thread pull from a tail pointer in the ring buffer. Make a copy of the frame. Do your audio processing on the copy. Blit the copy into the DS primary circular buffer where it wants you to.
If the audio munging thread can't keep up with the sound card, you'll hear a echo-y sound where the DMA of the sound card "ate the tail" of the DS primary buffer and wrapped around before you could write new data over old data. This is preferable to clicks, drop-outs, or pops, which may cause sudden changes in volume.
Meanwhile if your audio munging thread can't keep up with the DAQ, it will run out of "backlog" and start reading from recently written entries in the circular buffer as the head overtakes the tail. Thus a timing skew with cause you to lag further and further until you suddenly jump back in sync. This will sound like a section was skipped over -- again, better than a drop-out or pop.
Keeping the two processes from getting out of sync and avoiding echos or dropouts entirely may require a more sophisticated munging thread that has its' own pair of ring buffers with samples at different rates (copied from one to the other using a windowed resample operation driven by measuring the ratio of the tail-advance and head-adavance rates of the sound card and DAQ thread respectively).
If the daq board is filling in samples, I'm sure there is a way to determine the sample count. I would make a buffer that the daq board writes to more than big enough, but when you create buffers to send to DirectSound, pull out the number of samples that were written the last time you read them. If you want to just leave your queue from the daq board alone, than you should be able to determine the size of the queue when and push that many samples to DirectSound. If you do everything right, you shouldn't have to worry about the size unless you want to make a cieling on the max queue size.