Skip to content

v0.0.10

Latest

Choose a tag to compare

@MagicalTux MagicalTux released this 01 Sep 01:23
7fdc381

Other

  • union overrides apply to the automatic EXTENSIBLE promotion
  • Samples-union member selection — wSamplesPerBlock for block-compressed subformats
  • EXTENSIBLE black-box interop fixtures + doc surface refresh
  • automatic WAVE_FORMAT_EXTENSIBLE promotion in the muxer
  • KSDATAFORMAT subtype naming, IEC 61937 family, resolver tag routing
  • surface dwChannelMask through core ChannelLayout + mismatch flag
  • EXTENSIBLE routing by container size + Samples-union validation
  • describe Y4M little-endian + colorspace-token conventions on their own terms
  • document WAV demuxer hostile-input robustness + bench
  • add dependency-free read/write hot-path micro-benchmark
  • per-chunk-id targeted fuzz reaching every parser directly
  • cap sxml sub-chunk / alignment-point reservations to body size
  • deterministic mutation-fuzz net over the demuxer parse path
  • guard chunk-skip seeks against i64-overflow backward wrap
  • cap ds64 table reservation to available records (OOM hardening)
  • bound chunk-body allocation against size-lies (DoS hardening)
  • add CI / crates.io / docs.rs / MIT-license badges
  • smpl + inst chunks read+write symmetric — typed structs + writers
  • document DISP / id3 / PAD chunks + LIST INFO write side
  • typed WavDemuxer::disp() / info() accessors
  • LIST INFO write side — InfoChunk/InfoEntry + with_info
  • id3 chunk write side — WavMuxOptions::with_id3
  • parse PAD (alignment-padding) chunk
  • DISP (Display) chunk write side — typed DispChunk + with_disp
  • parse DISP (Display) + id3 (embedded ID3v2 tag) chunks
  • sxml (serialized-XML ADM carrier) read+write — README + CHANGELOG
  • symmetric sxml (serialized-XML) muxer write support per BS.2088 §7
  • read BW64/ADM sxml (serialized-XML) chunk per ITU-R BS.2088-2 §7
  • end-to-end demux->re-emit->demux symmetry test for cue/plst/adtl
  • cue/plst/adtl symmetric read+write + trailing-chunk scan (CHANGELOG + README)
  • symmetric cue / plst / LIST adtl muxer write support
  • typed cue / plst / LIST adtl read + post-data trailing-chunk scan
  • test RF64 Reserve overflow→promotion via streamed >4 GiB sparse sink
  • RF64/BW64 64-bit large-file write side (ds64 + JUNK on-the-fly conversion)
  • integration round-trip test for combined acid + chna metadata
  • classify chna audioID refs by ADM prefix + BS.2088 §3 definition scope
  • BWF bext chunk read+write with typed BextChunk surface
  • parse BW64/ADM bxml (compressed XML) chunk per ITU-R BS.2088-2 §6
  • BW64/ADM chna (channel-allocation) chunk read+write
  • recognise extended LIST INFO sub-IDs (ExifTool RIFF Info Tags)

Fixed

  • WAV WAVE_FORMAT_EXTENSIBLE codec routing now dispatches on the wBitsPerSample container size, not the union's wValidBitsPerSample — per docs/container/riff/waveformatextensible/ms-waveformatextensible.html §Samples.wValidBitsPerSample ("wBitsPerSample is the container size") and ms-extensible-wave-format.html ("a 20-bit sample can be stored left-justified within a three-byte container"), the data chunk carries container-sized samples and the union WORD only states how many bits carry signal. A 24-valid-bits-in-32-bit-container stream previously mis-resolved to pcm_s24le (wrong interleave stride); it now resolves to pcm_s32le with the precision surfaced separately via wav:fmt.valid_bits_per_sample / WavDemuxer::valid_bits_per_sample

Other

  • WAV mux Samples-union control decoupled from the explicit mask: WavMuxOptions::with_valid_bits_per_sample / with_subformat now apply whenever the EXTENSIBLE form is written — the automatic promotion included — instead of silently no-oping without with_extensible (a 20-in-24 stream needs no explicit mask any more). Mux/demux symmetry guard: a precision-family wValidBitsPerSample override above the container fails write_header rather than producing a file the demuxer rejects

  • black-box WAVE_FORMAT_EXTENSIBLE interop suite (tests/wav_extensible_interop.rs): tool-generated multi-channel (6ch s16), 24-bit and 8-channel float fixtures demux with correct GUID routing / typed layouts, and the muxer's automatically-promoted EXTENSIBLE output decodes byte-exactly through an external validator invoked as a black box. Skips silently when the tool is absent

  • WAV muxer now follows the staged encoder guidance automatically (docs/container/riff/waveformatextensible/README.md §"What's covered"): more than two channels or a container size above 16 bits promote the fmt chunk to WAVE_FORMAT_EXTENSIBLE (40-byte form), with dwChannelMask derived from the stream's typed CodecParameters::channel_layout when declared, else from the channel-count default (ChannelLayout::from_count; a DiscreteN layout writes mask 0, the legal "no assigned speaker positions" bitmap). Classic ≤ 2-channel / ≤ 16-bit output (incl. the 8-bit G.711 tags) stays byte-identical to the historical muxer. WavMuxOptions::with_extensible still forces the form with an explicit mask (and wins over everything); new WavMuxOptions::with_legacy_waveformatex suppresses the automatic promotion for byte-conservative re-muxing of legacy-tagged input. Round-trip symmetric: the mask the muxer derives maps back to the same typed layout on demux

  • WAV codec-tag resolution now routes unmapped wFormatTag values through the CodecResolver threaded into open() — the registry owns the RFC 2361 tag value space (docs/container/riff/rfc2361-wav.txt), so ADPCM-family / MP3 / any registry-claimed tag resolves to its codec crate's id instead of hard-erroring (legacy path) or falling to a synthesised GUID id (EXTENSIBLE template-GUID path, per the documented tag↔GUID equivalence). Probes see the CodecTag::WaveFormat tag, bits/channels/sample_rate hints and the raw fmt chunk as the header blob. Native PCM/IEEE-float/A-law/μ-law mappings never consult the resolver. New public wav::open_wav_demuxer_with(input, &dyn CodecResolver); open_wav_demuxer keeps the resolver-free behaviour

  • WAV EXTENSIBLE SubFormat GUIDs now surface their symbolic KSDATAFORMAT_SUBTYPE_* name (wav:fmt.subformat_name) for every row of the staged consolidated catalog (docs/container/riff/waveformatextensible/ksdataformat-subtype-guids.md) — both the WAVEFORMATEX-derived family and the IEC 61937 compressed-pass-through family (Data2 == 0x0cea, incl. the Dolby MAT 2.0/2.1 revisions from ms-representing-formats-iec61937.md). IEC 61937 GUIDs additionally surface wav:fmt.iec61937_stream_type (CEA-861 stream type, Data1 low byte) and are guarded from the wave-format-tag route per the catalog's dispatch guidance — their Data1 is a stream type, not a tag

  • WAV dwChannelMask now surfaces through oxideav-core's typed ChannelLayout on CodecParameters::channel_layout — position-SET matching against the named core layouts via the staged SPEAKER_* bit table (ms-waveformatextensible.html), with the two back-pair alias rows from the staged standard-layouts table (0x33Quad, 0x3F "5.1 (Microsoft)" → Surround51) and a DiscreteN fallback for assigned-but-unnamed position sets so a consumer never gets a named layout that contradicts the explicit mask. Mask 0 (no assigned positions) and a set-bit/nChannels disagreement leave the field None; the disagreement additionally surfaces as wav:fmt.channel_mask_mismatch (the staged doc's "should be the same" is a flag, not a reject). New public wav::channel_mask_for_layout derives the mask for a core layout (muxer-side building block)

  • WAV demuxer validates the EXTENSIBLE Samples union per the staged struct doc: wValidBitsPerSample > wBitsPerSample (a precision claim exceeding the container) and a container size that is zero or not a multiple of 8 are both rejected as malformed; a zero union WORD (writer left it unset) stays tolerated with the container size as the effective precision

  • WAV EXTENSIBLE Samples union member selection: the union WORD now surfaces under the member the SubFormat gives it — wValidBitsPerSample for the precision-semantics family (PCM / IEEE-float / A-law / μ-law templates + the IEC 61937 family, whose staged worked examples all set 16-in-16) or wSamplesPerBlock (new wav:fmt.samples_per_block key + WavDemuxer::samples_per_block accessor, 0 = variable) for block-compressed subformats (other template tags + vendor GUIDs). The precision-family validation no longer fires on block-compressed streams, whose 4-bit nibble containers and large per-block sample counts are legal (ms-waveformatextensible.html §Samples); WavDemuxer::valid_bits_per_sample correspondingly returns None for them — the union has exactly one active member

  • smpl (Sampler) + inst (Instrument) chunks read+write symmetric (RIFF MCI §3) — promoted from read-only metadata-key surfaces to typed wav::SmplChunk / wav::SampleLoop / wav::InstChunk public types with byte-lossless parse/to_bytes (the smpl loop array + vendor sampler_data tail round-trip verbatim; num_sample_loops re-derived from the actual loop count on write). WavMuxOptions::with_smpl / with_inst write the chunks ahead of data (RIFF §2 word-aligned), and the typed views are reachable via WavDemuxer::smpl() / inst(). The existing wav:smpl.* / wav:inst.* read keys are unchanged (the parsers now delegate to the typed structs)

  • typed WavDemuxer::disp() / info() accessors — the DISP and LIST INFO typed views are now captured during the chunk walk and reachable on the concrete demuxer (matching the typed-view pattern of every other chunk), None when absent. info() carries every sub-ID verbatim (incl. unknown/vendor) for re-emission via with_info

  • LIST INFO (text metadata) chunk write side (WavMuxOptions::with_info) — gives read/write symmetry for WAV text tags (RIFF MCI §3 "INFO List Chunk"). New public InfoChunk / InfoEntry types carry an ordered list of (sub-ID FOURCC, text) entries with parse/to_bytes (NUL-terminated, RIFF §2 word-aligned sub-chunks); the writer wraps them in a LIST(INFO) chunk ahead of data, and the demuxer reads recognised sub-IDs back through their snake_case metadata keys. Unknown / vendor sub-IDs round-trip through InfoChunk. The existing read path now delegates to InfoChunk::parse

  • DISP (Display) chunk read+write symmetric — clipboard-format type DWORD + payload; the WAV "SoundSchemeTitle" convention (ExifTool RIFF tag table). Typed DispChunk public type with byte-lossless parse/to_bytes plus DispChunk::text/title for the common CF_TEXT (type==1) display-title form. The demuxer surfaces wav:disp.{body_len,type} always plus wav:disp.title for the CF_TEXT form (text trimmed at the first NUL); non-text clipboard formats surface header-only, truncated bodies are skipped-as-opaque. WavMuxOptions::with_disp writes the chunk ahead of data (RIFF §2 word-aligned)

  • parse PAD (Pad / alignment-padding) chunk — the alignment sibling of JUNK in the RIFF MCI §2 "skip/ignore" dispatch group. Semantic-free body, so only accounting surfaces under the parallel wav:pad.* key namespace (count / total_bytes / per-chunk <n>.body_len), mirroring the JUNK contract; JUNK and PAD stay in separate namespaces. Absence is observable (no wav:pad.* keys emitted)

  • id3 / ID3 (embedded ID3v2 tag) chunk read+write symmetric — the read side surfaces the 10-byte ID3v2 header fields for observability per docs/container/id3/id3v2.3.0.html §3.1: wav:id3.{body_len,version,flags,tag_size} plus the three flag bits .unsynchronisation/.extended_header/.experimental, with the §3.1 synchsafe (28-bit) tag-size decode. The write side (WavMuxOptions::with_id3) carries a complete ID3v2 tag verbatim as an id3 chunk ahead of data (RIFF §2 word-aligned). Frame encoding/decoding is left to oxideav-id3 per the codec/container split; malformed/non-ID3-magic bodies surface only body_len

  • BW64/ADM <sxml> (serialized-XML) chunk read+write symmetric per ITU-R BS.2088-2 §7 — the third ADM XML carrier alongside <axml> (uncompressed) and <bxml> (compressed-whole). Typed SxmlChunk / SubXmlChunk / AlignmentPoint public types with byte-lossless parse/to_bytes (the on-wire subXMLCkTbSize is preserved verbatim; computed_sub_table_byte_size yields the canonical value). Segments the (optionally gzip) XML into per-sample-run SubXMLChunk records (binding a run of audio samples to an XML fragment for time-variant / Serial ADM metadata) plus an optional AlignmentPoint table for timestamp-based access. Surfaces wav:sxml.{body_len,fmt_type,compression,sub_chunk_count,alignment_point_count,total_samples} + per-record .<n>.{samples,xml_len,xml} (XML text only for the uncompressed fmtType==0x0000 form; gzip/private codes surface header fields only, leaving RFC 1952 inflation to a higher-level ADM-aware consumer). WavMuxOptions::with_sxml writes the chunk ahead of chna/data (§2.1 order) with RIFF §2 word-alignment; WavDemuxer::sxml() accessor. An sxml chunk (like chna) marks the file ADM-carrying, so a forced/promoted 64-bit file uses the BW64 top-level magic. Malformed/truncated bodies are skipped-as-opaque

  • WAV cue / playlist / associated-data symmetric read+write: typed CuePoint/CueChunk, PlaylistSegment/PlaylistChunk, and AdtlEntry/AdtlChunk (labl/note/ltxt) public types with byte-exact parse/to_bytes per Microsoft RIFF MCI §3 (Cue-Points / Playlist / Associated Data Chunk). WavMuxOptions::with_cue / with_plst / with_adtl writers emit the chunks in the trailer (after data, the conventional placement for sample-position-referencing chunks); WavDemuxer::cue() / plst() / adtl() accessors. Overclaimed counts are clamped; adtl sub-chunks get the RIFF word-alignment pad automatically

  • WAV demuxer no longer treats data as the chunk-scan terminator — cue / plst / LIST adtl chunks written after the waveform (the common convention) are now read, by seeking over the word-aligned data body and walking remaining chunks to EOF

  • WAV muxer RF64/BW64 64-bit large-file write side (read/write symmetry): WavMuxOptions::with_rf64(Rf64Mode)Force always emits a ds64 chunk + 0xFFFFFFFF sentinels + RF64/BW64 magic; Reserve writes a ds64-sized JUNK placeholder and promotes it on-the-fly to ds64 only if the finished file overflows 32 bits (ITU-R BS.2088-2 §3.6/§4.2 + EBU Tech 3306 v2 §3); Never (default) is byte-identical to the historical 32-bit muxer and errors on overflow. BW64 magic selected automatically when an ADM chna chunk is present. New public Rf64Mode enum

  • test: Rf64Mode::Reserve overflow→promotion path driven through a genuine >4 GiB streamed data payload via a sparse seekable sink (no gigabyte allocation) — verifies the in-place JUNKds64 rewrite, RF64 magic flip, and 64-bit riffSize/dataSize/sampleCount body values

  • integration test: full public mux → demux round-trip of a WAV carrying BOTH an Acidizer acid loop chunk and a BW64/ADM chna chunk — PCM byte-for-byte, typed WavDemuxer::{acid,chna}() accessors, and the ADM kind/definition-scope metadata keys (tests/wav_adm_metadata_roundtrip.rs)

  • BW64/ADM chna: classify each audioID reference (ATU_/AT_/AC_/AP_ → AdmRefKind) and resolve its definition scope per ITU-R BS.2088-2 §8.1/§3 (trailing four hex digits ≤ 0x0FFF = BS.2094 common, ≥ 0x1000 = file-local custom) — surfaced as wav:chna..{track_ref,pack_ref}_{kind,definition} keys, AdmRefKind/DefinitionScope public enums + AudioId accessors

  • BWF bext (Broadcast Audio Extension) chunk read+write with typed BextChunk surface (EBU Tech 3285 v2 §2.3) — WavMuxOptions::with_bext writer, WavDemuxer::bext() accessor, byte-exact parse/to_bytes round-trip

  • parse BW64/ADM bxml (compressed XML) chunk per ITU-R BS.2088-2 §6 — fmtType header + uncompressed-XML text surface

  • BW64/ADM chna (channel-allocation) chunk read+write with typed ChnaChunk/AudioId surface (ITU-R BS.2088-2 §8.1)

  • recognise extended LIST INFO sub-IDs (ExifTool RIFF Info Tags) — IAS1..IAS9 audio-language slots, Windows-Media "more info" set, production-credit tags