1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | /* NOTE(naman): Description of current Audio system.
--------------------------------------------------------------------
We have two positions inside the audio buffer: play_cursor and
write_cursor. The play_cursor signals what bytes are currently
being played by the audio hardware (well...) while the write_cursor
signals where in the audio buffer is it safe to write new data.
MSDN says that write_cursor is usually ahead of the play_cursor
by about 15 ms. As the audio gets played, both these positions
slowly move forward (and wrap around when they reach the edge
of the audio buffer).
-------------------------------------------------------------------
Now, there are two parameters that effect our audio:
1. (write_cursor - play_cursor): The difference between the
two positions indicates the latency of our audio system.
Since we will be writing after the write_cursor (usually,
read further for more details), any new data that we write
will get played after the time indicated by the difference
in the two positions.
2. Difference between two back-to-back positions: This indicates
responsiveness of the sound system. If we find the difference
between the write_cursor (or play_cursor) of this frame and the
one of the last, we can hit upon two cases:
i) The difference is zero: The cursors are actually not hardware
variables. This means that they might have very low resolution and
increment only once in a while. The time between two changes
in the value of cursors could even be more than a frame time.
ii) The difference is non-zero: At this point, the cursors have
finally moved. However, because the cursors might have such low
resolution, we can not be sure that play_cursor actually indicates
the position where the audio is being played. Instead, the cursors
would only give a rough approximation of what sound might actually
be playing. Also, because of the low resolution, the difference
might be very high, higher than a frame time.
-------------------------------------------------------------------
High latency would mean that the audio that we write -- that corresponds
to the video image that is going to be displayed at the upcoming
frame flip -- will actually play some time after the video image has been
displayed.
Low latency would mean that if we were to write the audio data at
the write_cursor, it might actually start playing before the
corresponding video image has appeared on the screen. In this case,
we need to figure out what position in the sound buffer would
correspond with the frame flip and write the audio data there. It also
means that if we spend too much time between querying for the
cursors and actually writing the data, the play_cursor might
catch up to or leave behind the cached write_cursor that we queried.
Low responsivness means that our write cursor might jump ahead
of the position in the sound buffer until where we have actually
written the audio data. This would mean that we have to write data
into the part of audio buffer where it is technically not safe to write.
High responsiveness doesn't seem to have any problems.
TODO(naman): Make sure that this is actually true (regarding high
responsiveness).
------------------------------------------------------------------------
So, this is what we should do:
1. Subtract the play_cursor from the write_cursor to find out the
latency of the system.
2. If it is low latency, calculate how much time ahead the next
frame boundary lies (wrt the play_cursor). Find out what that time
means in terms of samples and bytes of audio data. This is where we
would write from instead of write_cursor.
3. Else, if it is high latency, then see if this this the first frame ever.
If it is, then just write the audio data worth one frame of time
from the write_cursor.
4. If this is not the first frame, find out the position until
where we wrote the data last time. Call it target_cursor.
5. If target_cursor is ahead of the write_cursor, everything is well.
Just get a frame's worth of audio data and and write it in the buffer
starting at target_cursor. Finally, update the target_cursor.
6. If target_cursor is behind the write_cursor, find the difference
between the two. Call it unsafe_region.
7. If the duration of unsafe_region is less than the best responsiveness
ever seen, then get (unsafe_region + one frame's worth) audio data and write
it from target_region.
8. If the duration of unsafe_region is more than that, then we our audio
probably skipped. At this point, the play cursor has either already
passed or is about to reach the last sample we wrote.
9. First, try locking the buffer from target_cursor for a size of
(unsafe_region + one frame's worth) audio data. If this succeeds, get
the data, write it in and hope that we were fast enough to avoid a skip.
10. If the lock fails, there would definitely be a skip. So, just get a
frame's worth of data and write it from write_cursor. We fucked up,
shame on us!
-------------------------------------------------------------------------
TODO(naman): Steps 2, 7 and 9 cause us to write behind the write_cursor.
Is it safe?
*/
|
mmozeiko
In WASAPI it is very common to have separate thread that submits buffer to sound card.
mrmixer
And in the case of DirectSound you can overwrite, so you'll just have the usual amount of latency (write_cursor - play_cursor).