Here is how it works:
Startup:
1. prepare directsound
2. create a single multimedia timer that elapses every half-second.
3. create a single mutex for sychronization
Play a Sound:
1. wait for the mutex(infinitely)
2. create a directsound buffer with enough space for one second of audio
3. fill the buffer completely with audio data
4. add the buffer to a playing list
5. set the sounds's last write point to 0
6. play the buffer looping
7. release the mutex
In the timer elapse event:
1. try to take the mutex (wait 0 miliseconds)
2. for each playing buffer (could be multiple simultanious sounds)
3. get the buffer's write cursor (getcurrentposition)
4. determine how large the space between the last write point and the write cursor is (take into accout that the write cursor loops (buffer is circular, so if write cursor is ever less than last write point, then it looped(fillsize=1secofdatalength-lastwritepoint + writecursor)))
5. lock the buffer using last write point as offset and fillsize as size and retrive the two pointers (2nd potentially null)
6. fill first pointer with sound data, if not enough sound data fill remaining with silence (silence value varaies depending on PCM format(44100khz 16 bit stereo silence is 0)
7. increase last write position by amount of data written
8. if second buffer pointer valid (and you still have sound data to play), repeat 6 and 7 for the new buffer
9. unlock the buffer
10. release the mutex
whew =D
the above system does not account for 'stoping a sound that is done' or 'making a sound loop'
here is how you would do that:
Stoping a sound that is done:
if a sound is not supposed to loop (or is done looping(counted loop)) then whenever the update is unable to stream 'any' data, you should remove the buffer from the playing buffers list, and destroy it's resources. It is important that you don't do this if the update function DID write even a bit of data, since stoping then would stop data playback too soon.
Making a sound loop:
looping a sound is pretty simple. when you discover you have run out of data, instead of filling the remaining area with silence, simply rewind the stream and continue filling (this assumes of course the stream is seekable).