SHA256
Document Audio Pipeline
This commit is contained in:
1 parent
7bb47e4974
commit
19d0e258ac
22 files changed
+634
-12
No files matched your search
@@ -18,18 +18,34 @@ internal sealed class DecodeStream : IDisposable
|
||||
/// <summary>Input frames read from the reader per resampler call.</summary>
|
||||
private const int ReadBlockFrames = 4096;
|
||||
|
||||
/// <summary>Resampler from the reader's rate to the output rate; null when the rates are equal (samples are copied).</summary>
|
||||
private readonly PCMResampler? _resampler;
|
||||
|
||||
private readonly int _channels;
|
||||
private readonly int _outputRate;
|
||||
|
||||
/// <summary>Input block of <see cref="ReadBlockFrames"/> frames, interleaved.</summary>
|
||||
private readonly float[] _readBuffer;
|
||||
|
||||
/// <summary>Scratch output for frames discarded after a restart; allocated on first use.</summary>
|
||||
private float[]? _discardBuffer;
|
||||
|
||||
/// <summary>True once the reader returned no more input and the resampler is being drained with Flush.</summary>
|
||||
private bool _flushing;
|
||||
|
||||
/// <summary>True once the input and the resampler tail are exhausted; the stream must restart before producing more.</summary>
|
||||
private bool _ended;
|
||||
|
||||
/// <summary>Input frames per alignment step: input rate / gcd (1 without resampling).</summary>
|
||||
private readonly long _inputStep;
|
||||
|
||||
/// <summary>Output frames per alignment step: output rate / gcd (1 without resampling).</summary>
|
||||
private readonly long _outputStep;
|
||||
|
||||
/// <summary>Input frames fed before the requested position on a restart, so the filter history matches a continuous run.</summary>
|
||||
private readonly long _prerollInputFrames;
|
||||
|
||||
/// <summary>Reader of this stream (owned; disposed with the stream).</summary>
|
||||
public IPCMReader Reader { get; }
|
||||
|
||||
/// <summary>Output frame the stream continues at, or -1 when the stream is not positioned.</summary>
|
||||
@@ -41,6 +57,8 @@ internal sealed class DecodeStream : IDisposable
|
||||
/// <summary>Number of times the stream started anew (seek + resampler reset).</summary>
|
||||
public int RestartCount { get; private set; }
|
||||
|
||||
/// <param name="reader">Reader of the clip; ownership passes to the stream.</param>
|
||||
/// <param name="outputRate">Output sample rate in Hz.</param>
|
||||
public DecodeStream(IPCMReader reader, int outputRate)
|
||||
{
|
||||
Reader = reader;
|
||||
@@ -58,6 +76,7 @@ internal sealed class DecodeStream : IDisposable
|
||||
// History fed before the first kept output frame when a stream starts mid-clip.
|
||||
// The filter spans about filter_size input frames per output frame at ratios up to 1:1
|
||||
// and filter_size * (input rate / output rate) input frames when downsampling.
|
||||
// The preroll is four times that span plus one alignment step (the restart position is rounded down to a step).
|
||||
var ratio = Math.Max(1L, (reader.SampleRate + outputRate - 1L) / outputRate);
|
||||
_prerollInputFrames = 4L * _resampler.FilterSize * ratio + _inputStep;
|
||||
}
|
||||
@@ -77,6 +96,7 @@ internal sealed class DecodeStream : IDisposable
|
||||
var channels = _channels;
|
||||
var filled = 0;
|
||||
|
||||
// Same rate: output frame = input frame; seek only when the reader is not already at the start.
|
||||
if (_resampler == null)
|
||||
{
|
||||
if (Reader.Position != start)
|
||||
@@ -115,6 +135,7 @@ internal sealed class DecodeStream : IDisposable
|
||||
fixed (float* pIn = _readBuffer)
|
||||
fixed (float* pOut = data)
|
||||
{
|
||||
// Produce and drop the output frames between the aligned restart position and the requested start.
|
||||
while (discard > 0 && !_ended)
|
||||
{
|
||||
_discardBuffer ??= new float[ReadBlockFrames * channels];
|
||||
@@ -167,12 +188,14 @@ internal sealed class DecodeStream : IDisposable
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Releases the resampler and the reader.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_resampler?.Dispose();
|
||||
Reader.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>Greatest common divisor of two positive integers (Euclid).</summary>
|
||||
private static int GCD(int a, int b)
|
||||
{
|
||||
while (b != 0) (a, b) = (b, a % b);
|
||||
|
||||
Reference in new issue
Block a user