Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
/* 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, 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
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
So, what is to be done? Before we begin, let's compute some shit:
1. write_cursor_linear (add buffer size to write cursor if it has wrapped,
. treat it as write_cursor from now) (maybe) (use common sense)
2. latency = write_cursor_linear - play_cursor
3. frame_bytes (bytes to be written in any frame, find using frame rate)
4. target_cursor (the last byte to which we wrote last time, not valid on
. first frame)
5. bytes_until_flip (how many bytes should be written until the frame flip)
6. worst_responsiveness (biggest difference in two consecutive write_cursors
. ever seen)
7. get_ahead_coefficient = (worst_responsiveness / frame_bytes) + 1
Okay, here's what needs to be done:
1. If sound has low latency, we might be able to get frame perfect audio.
. First see if the write_cursor lies beyond the frame boundary.
. (a) If it does, then the dream of frame perfect audio is just
. a dream. See if this is the first frame.
. [I] If it is, then start writing from (play_cursor + bytes_until_flip) and
. write frame_bytes audio data.
. [II] If it is not, then see if the target_cursor is ahead of the write_cursor.
. <i> If target_cursor is ahead of write_cursor, then write frame_bytes audio
. from target_cursor.
. <ii> If it's not, then write frame_bytes audio from write_cursor. We
. will have an audio skip here.
. (b) If the write_cursor is behind the frame boundary, there's hope!
. There's light! See if this is the first frame.
. [I] If it is, then CONGRATS! Write frame_bytes audio from (play_cursor +
. bytes_until_flip) and rejoice! Make merry!
. [I] If it is not, then there's everything is out of our hands. Our audio
. experience depends on what happened in previous frames, the ghosts of
. the past haunt all of us evermore. See if the target_cursor is ahead of
. the write_cursor.
. <i> If it is, then write frame_bytes from target_cursor.
. <ii> If not, write frame_bytes from write_cursor. There will be a skip.
. (Did someone say emotional rollercoaster?)
2. If it doesn't have low latency, then curse the poverty of our audience.
. Blade Runner was right! Anyway, see if this is the first frame ever.
. (a) If it is, see if write_cursor is behind the frame flip (it _might_ happen,
. high latency doesn't say how high).
. [I] If it is, then we can still get perfectly synced audio. Xenu surely
. smiles upon us! Write frame_bytes from (play_cursor + bytes_until_flip).
. [II] If it's not, just accept our fate. Write frame_bytes from write_cursor
. and drown our sorrows in our own tears.
. (b) If it is not the first frame, our past sins have caught up with us again.
. See if target_cursor is ahead of the write_cursor.
. [I] If it is, all is well. Just write frame_bytes from target_cursor.
. [II] If it's not, we are in deep sewers. There will definitely be
. a skip now. Get frame_bytes worth of audio data (or more if
. whatever lead to this dark end is bound to happen again) and
. write it from write_cursor, whereever it may be. All our
. hardwork will be ignored, like sweat in shower. Time to throw
. the towel.
At this point, if write_cursor is behind the target cursor but is within
some threshold of distance, multiple bytes_to_write with get_ahead_coefficient
to try to prevent the write_cursor from passing the target_cursor.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment