33d6f34d8a
Background-job transcode (ffmpeg/libopus) after source store; pure C# Ogg walker builds the 0.5s-bucketed granule→byte seek index + captures the OpusHead/OpusTags setup header into a per-track sidecar in a new track-opus vault. Best-effort, additive, regenerated on replace-audio.
51 lines
2.4 KiB
C#
51 lines
2.4 KiB
C#
namespace DeepDrftContent.Processors.Opus;
|
|
|
|
/// <summary>
|
|
/// Wire-format constants for the Ogg-Opus derived artifacts. Centralised so the seek-index codec,
|
|
/// the page walker, and the tests agree on one set of magic numbers.
|
|
/// </summary>
|
|
public static class OggOpusConstants
|
|
{
|
|
/// <summary>Opus granule positions are always sample counts at 48 kHz, regardless of input rate.</summary>
|
|
public const double OpusSampleRate = 48000.0;
|
|
|
|
/// <summary>One seek-index entry per this many seconds of audio (OQ7 — 0.5 s buckets).</summary>
|
|
public const double SeekBucketSeconds = 0.5;
|
|
|
|
/// <summary>The Ogg page capture pattern "OggS" — every page starts with these four bytes.</summary>
|
|
public static ReadOnlySpan<byte> CapturePattern => "OggS"u8;
|
|
|
|
/// <summary>Magic signature opening an OpusHead identification header packet.</summary>
|
|
public static ReadOnlySpan<byte> OpusHeadSignature => "OpusHead"u8;
|
|
|
|
/// <summary>Magic signature opening an OpusTags comment header packet.</summary>
|
|
public static ReadOnlySpan<byte> OpusTagsSignature => "OpusTags"u8;
|
|
|
|
/// <summary>
|
|
/// Fixed size of an Ogg page header before the segment table: capture(4) + version(1) +
|
|
/// header-type(1) + granulepos(8) + serial(4) + sequence(4) + checksum(4) + page-segments(1).
|
|
/// </summary>
|
|
public const int OggPageHeaderSize = 27;
|
|
|
|
/// <summary>Byte offset of the 64-bit granule position within an Ogg page header.</summary>
|
|
public const int GranulePositionOffset = 6;
|
|
|
|
/// <summary>Byte offset of the page-segment count (the segment-table length) within the header.</summary>
|
|
public const int PageSegmentCountOffset = 26;
|
|
|
|
/// <summary>Sentinel granule position for a page that ends mid-packet (no usable timestamp).</summary>
|
|
public const ulong NoGranulePosition = 0xFFFFFFFFFFFFFFFFUL;
|
|
|
|
/// <summary>Header size of the serialized seek-index blob: totalBytes(8) + duration(8) + count(4).</summary>
|
|
public const int SeekIndexHeaderSize = 20;
|
|
|
|
/// <summary>Size of one serialized seek point: granulepos(8) + byteOffset(8).</summary>
|
|
public const int SeekPointSize = 16;
|
|
|
|
/// <summary>Vault-resource extension for the Opus audio bytes.</summary>
|
|
public const string OpusExtension = ".opus";
|
|
|
|
/// <summary>Vault-resource extension for the combined setup-header + seek-index sidecar.</summary>
|
|
public const string SidecarExtension = ".opusidx";
|
|
}
|