"the position seeked to". This fixes a special case: SetPosition(n)
returning 0 was EOF except when SetPosition(0). The returned value
on success was always n, anyway; we never seek to a different position
and return success.
returned sound time. This is tricky: we may be interrupted, causing timing error.
We tried working around this by trying to hint the scheduler that we're in a
period where we don't want to be interrupted, even though we're in a thread
that's not normally high priority, by boosting the priority temporarily. This
worked in Windows, but not in general; it's too far out of the expectations of
schedulers and generally just made things worse.
Let's look at this like an interruption-based lockless algorithm: try it, see
if it succeeded, and if it failed, try again.
Retrying even once should be a rare exception, but failsafe anyway, so a bug
in a sound driver won't hang.
different rate than the source it's reading from; one second
of returned data may correspond to two seconds in the source
material.
GetStreamToSourceRatio returns the ratio of returned data in
the next Read() call to source data. This is propagated
upwards in the filter tree, so rate changes by a speed changer
in the middle of the tree will be reflected in the final
GetStreamToSourceRatio().
This means that whenever the ratio changes, Read() stops
returning data; it returns whatever it has, so the caller
has an opportunity to call GetStreamToSourceRatio again
and notice the change.
These semantics can be annoying to implement in some
cases, where only the processing of Read() may notice
a ratio change. Read() may want to return 0, to say
"something changed, call GetStreamToSourceRatio again",
but 0 means EOF.
Add RageSoundReader::END_OF_FILE. 0 is now no
longer a special case; it means "there's more data, I
just didn't return any this time". This is functionally
equivalent to errno EINTR.
GetStreamToSourceRatio. This will allow RateChange() to
be implemented with a filter, instead of being built into
RageSound, allow filters that change the music rate while
preserving robust correlation to the original audio.
This tightens up the frame definitions, resulting in three
frame counters:
- source frames; frames into the original source audio
- stream frames; frame counter after all filtering
- hardware frames; the sound driver's frame counter
All frames are in the sample rate of the stream; if audio
is resampled to match the hardware, the resampled audio is
considered the source audio, and frame units are in the
resulting sample rate.
By design, source frames are never exposed to the sound
driver, which only deals with stream and hardware frames.
The principle of the ratio API is that while a filter stream
should ideally output audio at the exact rate it advertises
through GetStreamToSourceRatio, rounding error will inevitably
creep in. Rather than depending on the ratio being exact
over an arbitrary length of time, each filter's GetNextSourceFrame
calculates the actual next source frame. Typically, it
will be equal to (its value before the previous read) + (number
of frames read * previous ratio). This avoids hard-to-debug
synchronization drift.
(This also eliminates the 1k buffer which was used for crappy
rate changing, which can be readded with a buffer filter if
needed. Rate changing is disabled for a while until a bit
more work is done.)
comments fell behind the code. In this case, it's better to use a better
name than "iSize" which indicates units, eg. "iBytes" or "iFrames", than
to explain the units in a comment.