SHA256
Add Chunked Audio Clip Store
This commit is contained in:
1 parent
4e556a4632
commit
0a12c12ff5
7 files changed
+1412
No files matched your search
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ADOFAIRenderer.Util.Audio.Clip;
|
||||
|
||||
/// <summary>
|
||||
/// Forward-only <see cref="IPCMReader"/> over PCM held in memory as a list of blocks.
|
||||
/// Block b holds frames [b * FramesPerBlock, (b + 1) * FramesPerBlock) interleaved; the last block may be shorter.
|
||||
/// Each block is released (passed to the release callback and dropped) as soon as the read position passes its end,
|
||||
/// and all remaining blocks are released on <see cref="Dispose"/>.
|
||||
/// </summary>
|
||||
internal sealed class MemoryPCMReader : IPCMReader
|
||||
{
|
||||
private readonly float[]?[] _blocks;
|
||||
private readonly Action<float[]>? _onBlockReleased;
|
||||
private int _releasedCount; // Blocks [0, _releasedCount) are released
|
||||
|
||||
public int Channels { get; }
|
||||
public int SampleRate { get; }
|
||||
public long TotalFrames { get; }
|
||||
public bool IsForwardOnly => true;
|
||||
public long Position { get; private set; }
|
||||
|
||||
/// <summary>Frames per block (all blocks except the last).</summary>
|
||||
public int FramesPerBlock { get; }
|
||||
|
||||
public MemoryPCMReader(IReadOnlyList<float[]> blocks, int framesPerBlock, int channels, int sampleRate, long totalFrames,
|
||||
Action<float[]>? onBlockReleased)
|
||||
{
|
||||
if (channels <= 0) throw new ArgumentOutOfRangeException(nameof(channels));
|
||||
if (framesPerBlock <= 0) throw new ArgumentOutOfRangeException(nameof(framesPerBlock));
|
||||
if (totalFrames > (long)blocks.Count * framesPerBlock) throw new ArgumentException("Blocks hold fewer frames than totalFrames.", nameof(blocks));
|
||||
|
||||
_blocks = new float[blocks.Count][];
|
||||
for (var i = 0; i < blocks.Count; i++) _blocks[i] = blocks[i];
|
||||
_onBlockReleased = onBlockReleased;
|
||||
FramesPerBlock = framesPerBlock;
|
||||
Channels = channels;
|
||||
SampleRate = sampleRate;
|
||||
TotalFrames = totalFrames;
|
||||
}
|
||||
|
||||
public void Seek(long frame)
|
||||
{
|
||||
if (frame < 0 || frame > TotalFrames) throw new ArgumentOutOfRangeException(nameof(frame));
|
||||
if (frame < (long)_releasedCount * FramesPerBlock)
|
||||
throw new InvalidOperationException($"Frame {frame} was already released by the forward-only reader.");
|
||||
Position = frame;
|
||||
}
|
||||
|
||||
public int Read(float[] buffer, int frameCount)
|
||||
{
|
||||
var total = (int)Math.Min(frameCount, TotalFrames - Position);
|
||||
if (total <= 0) return 0;
|
||||
|
||||
var written = 0;
|
||||
while (written < total)
|
||||
{
|
||||
var blockIndex = (int)(Position / FramesPerBlock);
|
||||
var block = _blocks[blockIndex] ?? throw new InvalidOperationException($"Block {blockIndex} was already released.");
|
||||
var offset = (int)(Position - (long)blockIndex * FramesPerBlock);
|
||||
var count = Math.Min(total - written, FramesPerBlock - offset);
|
||||
Array.Copy(block, offset * Channels, buffer, written * Channels, count * Channels);
|
||||
written += count;
|
||||
Position += count;
|
||||
ReleaseBlocksBefore((int)(Position / FramesPerBlock));
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
private void ReleaseBlocksBefore(int blockIndex)
|
||||
{
|
||||
while (_releasedCount < blockIndex && _releasedCount < _blocks.Length)
|
||||
{
|
||||
var block = _blocks[_releasedCount];
|
||||
_blocks[_releasedCount] = null;
|
||||
_releasedCount++;
|
||||
if (block != null) _onBlockReleased?.Invoke(block);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() => ReleaseBlocksBefore(_blocks.Length);
|
||||
}
|
||||
Reference in new issue
Block a user