cmuratori
No - we do not know that the audio clock is at all synchronized with the other clocks, so it may drift. You cannot assume that, say, one second's worth of time passing on QueryPerformanceCounter equals one second's worth of samples elapsing on the PlayCursor.
- Casey
That's right. But I can "assume" that one second's worth of playbuffer really is one second, right? So that means 33.33333m/s worth of DSPlaybuffer really amounts to this amount of time?
I have now checked it, for several days. And it seem now 99.99% clear to me, unless I have made mistakes that the only latency to care about, is not comming from Directsound. But from the code we write, in the gameloop.
My timingcode is simply wrong (off). And it needs to be corrected, on the fly. This is the "only" thing the playcursor is useful for. To tell us which bytes in the buffer, which is to be avoided. In other words, to tell us to correct our timing, which is that which is off. We can use the playcursor for timing! It is that accurate.
I used your tricks with drawing a visual graph to confirm this, of course also the sound works now. (very useful debugging strategy, thanks for teaching me that).
Directsound is not off. If it would be off, then it would be impossible to play music, on the thing, right? And even If it would be off, which it's not, then we still would like to synchronize with it.
As long as we stay 1 frame worth of time ahead of the playcursor, in the buffer we write to, we can write exactly one frame worth of data. Samples for 33.33333m/s without ever asking. And without ever having to correct how many bytes we are writing. (which also saves us from writing too much). This is because we know from our last write, that the playcursor is still in the last (previous) frame worth of sample-data.
______playcursor______________________byteToLock/mywritecursor
[fraction|....unplayed sound..........................]| < Byte to lock at the end of the 6400 bytes we filled last time.
< CURRENT 1 frameworth of sample -> | Next frame
So what needs to be done, to update the buffer on a frame boundary, is to make sure that our own timing code, always stays 1 frame ahead of the playcursor. That frame "ahead" is the frame that will play when next screenframe is shown. And this is pretty much guarantied, if our soundcard can actually playback 48000 samples in one second.
In addition we must offset the waveperiod each second. If we play the same wavesound, by part of the wave that will not fit perfectly into the first 1 second buffer. And if we CHANGE the wavelengths we insert, we must reset new samples to start of a new period.
I found it better to do this by generating samples in my private buffer. I then always generate exactly 1 frame worth of samples. For 30hz this is 6400 bytes, for 60hz it is 3200 bytes, and so on. I am not sure, but I pretty much guess that I can use this buffer as a mixer, so I called it "mixer".
If I now fill a frame worth into the directsoundbuffer, and it starts to play, then if I can time my own code correctly, so that it, on each frameupdate can write exactly a frameworth of data, at exactly the right boundary, 1 frame ahead of the playcursor(roughly), or 1 frame ahead of the previous frame written, then directsound will never be a problem. DS is so fast, that it will allow you to update almost just a couple of bytes, if you just stay away from the playcursor.
The only problem is that we need to correct our own timing. Because what typically happens is that we fall behind. It is we who has latencies... Therefore, we must check the playcursor, and offset our update of the next frameworth of data. If we fall behind, the playcursor will catch up and cause noise. But if we make sure that it does not, and also that we do not move too far ahead, sound will play without any problems whatsoever.
I am only 99.99% sure of this. So it would be nice to get some comments. But everything seems fine, except for my timing, which is a little off, a little behind each frame. I fixed this by comparing my projection of when to update the buffer, with the playcursor, if the playcursor is close to MyWriteCursor, then I update imidiatly, and not wait for the next frame, but if the playcursor is staying safely behind (a frame or so), I wait until the frametime to do the update. As long as I can manage to control my timing, this works well for all framerates, so thats why I say DirectSound is not lagging :)
From what I understood, you will be revisiting this later. And maybe the way I am doing it now will not work for a game once it becomes real? IDK. I just wanted to say that as far as I know, DS is not laggy. And in fact it would be better to use that for a clock, since it's almost bound to be a better clock, due to it must be able to play those 48000 samles a second.
Right. Sorry for the long post. And sorry if I have jumped to conclusions. I just thought it could be an important topic, since if I don't have any feedback to the contrary of what I am saying, I am going to go with this and use it for my own projects.