@@ 1,3 1,110 @@
+
+(** Structure to manage state within a resampling stream, pulling its
+ source samples from a provided callback function. This handles the
+ fact that BqResample has no internal reservoir and so does not
+ always return precisely the expected number of samples for a given
+ input count. With a PullResampler we always get the requested
+ number of samples, with a variable number pulled from the source
+ callback instead.
+*)
+structure PullResampler : sig
+
+ type state
+ type source = int -> RealMatrix.matrix option
+
+ val new : BqResample.parameters -> real -> state
+ val reset : state -> unit
+ val pull : source -> state * int -> RealMatrix.matrix option
+
+end = struct
+
+ structure M = RealMatrix
+
+ type state = {
+ ratio : real,
+ resampler : BqResample.t,
+ spare : M.matrix ref
+ }
+
+ type source = int -> M.matrix option
+
+ fun emptySpare channels =
+ M.fromRows
+ (List.tabulate (channels, fn _ => RealVector.fromList []))
+
+ fun new (parameters : BqResample.parameters)
+ (ratio : real)
+ : state =
+ { ratio = ratio,
+ resampler = BqResample.new parameters,
+ spare = ref (emptySpare (#channels parameters))
+ }
+
+ fun reset (state as { ratio, resampler, spare })
+ : unit =
+ (BqResample.reset resampler;
+ spare := emptySpare (BqResample.channels resampler))
+
+ fun processWithoutSpare (source : source)
+ (state as { ratio, resampler, spare } : state,
+ n : int)
+ : M.matrix option =
+ let val () = if M.columns (! spare) > 0
+ then raise Fail "Internal error: processWithoutSpare called when we do have spare"
+ else ()
+ val incount = Real.ceil (Real.fromInt n / ratio)
+ val inmaybe = source incount
+ fun process' (insamples, final) =
+ let val resampled = BqResample.resample
+ (resampler, M.toRowsV insamples,
+ ratio, final)
+ val m = M.fromRowsV resampled
+ val have = M.columns m
+ in
+ if have = n
+ then SOME m
+ else if have > n
+ then (spare := M.columnSlice (m, n, SOME (have - n));
+ SOME (M.columnSlice (m, 0, SOME n)))
+ else if final
+ then SOME m
+ else case processWithoutSpare source (state, n - have) of
+ NONE => SOME m
+ | SOME m' => SOME (M.concatHorizontal [m, m'])
+ end
+ in
+ case inmaybe of
+ SOME insamples => process' (insamples, false)
+ | NONE =>
+ case process' (M.const (M.ROW_MAJOR,
+ { rows = BqResample.channels resampler,
+ columns = 0 },
+ 0.0),
+ true) of
+ SOME outsamples => if M.columns outsamples > 0
+ then SOME outsamples
+ else NONE
+ | NONE => NONE
+ end
+
+ fun pull (source : source)
+ (state as { ratio, resampler, spare } : state, n : int)
+ : M.matrix option =
+ let val spmat = !spare
+ val have = M.columns spmat
+ in
+ if have = 0
+ then processWithoutSpare source (state, n)
+ else if have >= n
+ then (spare := M.columnSlice (spmat, n, SOME (have - n));
+ SOME (M.columnSlice (spmat, 0, SOME n)))
+ else (spare := emptySpare (BqResample.channels resampler);
+ case pull source (state, n - have) of
+ NONE => SOME spmat
+ | SOME m => SOME (M.concatHorizontal [spmat, m]))
+ end
+
+end
functor ResamplingStatefulStreamFn (S: REAL_STATEFUL_SAMPLE_STREAM) :
sig
@@ 14,8 121,7 @@ functor ResamplingStatefulStreamFn (S: R
rate : SignalTypes.rate,
ratio : real,
upstream : S.stream,
- resampler : BqResample.t option,
- spare : M.matrix ref,
+ resampler : PullResampler.state option,
quality : BqResample.quality,
upstreamStartTime : RealTime.t ref,
lastSeekTime : RealTime.t ref,
@@ 60,7 166,7 @@ functor ResamplingStatefulStreamFn (S: R
NOT_SIMPLE => (SampleStreamLog.debug (fn () => ["ResamplingStatefulStreamFn: not seekable with non-simple ratio of %1", SampleStreamLog.R ratio]);
SampleStreamTypes.NON_SEEKABLE)
| _ => S.seekable upstream
-
+(*!!!
fun readWithoutUsingSpare
(s as { ratio, upstream, resampler, spare, ... } : stream, n) :
M.matrix option =
@@ 119,12 225,14 @@ functor ResamplingStatefulStreamFn (S: R
NONE => SOME spmat
| SOME m => SOME (M.concatHorizontal [spmat, m]))
end
-
+*)
fun read (s as { upstream, resampler, frameCounter, ... } : stream, n) :
M.matrix option =
let val mopt = case resampler of
NONE => S.read (upstream, n)
- | SOME r => readResampling (s, n)
+ | SOME r => PullResampler.pull
+ (fn count => S.read (upstream, count))
+ (r, n)
in
case mopt of
NONE => NONE
@@ 132,13 240,11 @@ functor ResamplingStatefulStreamFn (S: R
mopt)
end
- fun reset (s as { resampler, upstream, spare,
+ fun reset (s as { resampler, upstream,
lastSeekTime, frameCounter, ... } : stream) =
- (case resampler of NONE => ()
- | SOME r => BqResample.reset r;
- spare := M.fromRows (List.tabulate
- (S.channels upstream,
- fn _ => RealVector.fromList []));
+ (case resampler of
+ NONE => ()
+ | SOME r => PullResampler.reset r;
lastSeekTime := S.time upstream;
frameCounter := 0
)
@@ 208,12 314,13 @@ functor ResamplingStatefulStreamFn (S: R
val resampler =
if Real.== (ratio, 1.0)
then NONE
- else SOME (BqResample.new
+ else SOME (PullResampler.new
{ channels = S.channels s,
quality = quality,
dynamism = BqResample.RATIO_MOSTLY_FIXED,
ratio_change = BqResample.SUDDEN_RATIO_CHANGE
}
+ ratio
)
in
{
@@ 221,9 328,6 @@ functor ResamplingStatefulStreamFn (S: R
ratio = ratio,
upstream = s,
resampler = resampler,
- spare = ref (M.fromRows (List.tabulate
- (S.channels s,
- fn _ => RealVector.fromList []))),
quality = quality,
upstreamStartTime = ref (S.time s),
lastSeekTime = ref (S.time s),