189 lines
14 KiB
Markdown
189 lines
14 KiB
Markdown
# Project Research Summary
|
|
|
|
**Project:** YellowJacket v1.2.1 Format Parity
|
|
**Domain:** Audio metadata tag writing — OGG Vorbis and WAV format support
|
|
**Researched:** 2026-03-18
|
|
**Confidence:** HIGH
|
|
|
|
## Executive Summary
|
|
|
|
YellowJacket v1.2.1 adds OGG Vorbis and WAV tag writing to achieve full format parity across all four supported audio formats. The existing `tagwriter` pipeline was designed for format extension — a switch-case in `pipeline.go` dispatches to format-specific writer functions, and everything downstream (DB sync, events, UI) is format-agnostic. Adding OGG and WAV requires two new writer functions, zero new dependencies, and no frontend changes.
|
|
|
|
The recommended approach is **custom implementations for both formats**: a custom OGG page rewriter (~350 LOC) for OGG Vorbis, and a custom RIFF chunk parser (~200 LOC) wrapping the existing `bogem/id3v2` library for WAV. No pure-Go library exists for OGG Vorbis tag writing, and the Go WAV ecosystem recently lost its most popular library (`go-audio/wav` archived Feb 2026). The OGG container format and RIFF chunk format are both well-documented and simple enough to implement directly. Vorbis Comments (used by OGG) are the exact same metadata format already used in FLAC — field names, encoding, and comment structure are identical, enabling significant code reuse.
|
|
|
|
The primary risk is the OGG page infrastructure: CRC32 checksums use a non-standard bit ordering (MSB-first, not the Go standard library's reflected CRC32), page sequence numbers must be strictly sequential, and granule positions must be preserved exactly. These are well-understood constraints with clear specifications, but incorrect implementation produces silently corrupted files. Round-trip testing (write → read back via `dhowden/tag`) is the primary mitigation, following the pattern established by the existing MP3 and FLAC writers.
|
|
|
|
## Key Findings
|
|
|
|
### Recommended Stack
|
|
|
|
Zero new dependencies. Both formats are implemented using Go standard library primitives (`encoding/binary`, `encoding/base64`, `bytes`, `io`) plus existing dependencies for shared functionality.
|
|
|
|
**Core technologies:**
|
|
- **Custom OGG page rewriter:** Parse/write OGG pages with CRC32 and segmentation — no pure-Go OGG writing library exists; `mccoyst/ogg` (37 stars, no semver) saves ~80 LOC but adds dependency risk
|
|
- **Custom RIFF chunk parser/writer:** Read/write WAV RIFF structure — `go-audio/wav` and `go-audio/riff` were archived Feb 2026; RIFF is simple enough for custom code
|
|
- **`bogem/id3v2` (existing):** Generate ID3v2 tags for WAV `id3 ` chunks — same library already used for MP3 writing, all 8 fields + cover art work identically
|
|
- **`go-flac/flacpicture` (existing):** Build METADATA_BLOCK_PICTURE binary blocks for OGG cover art — same binary format, just base64-wrapped for OGG
|
|
- **`fileutil.AtomicWrite` (existing):** Crash-safe file writes for both formats — proven pattern from MP3/FLAC writers
|
|
|
|
### Expected Features
|
|
|
|
**Must have (table stakes):**
|
|
- Write all 8 text fields for OGG Vorbis (same Vorbis Comment field names as FLAC)
|
|
- Write all 8 text fields for WAV (via ID3v2 in RIFF chunk)
|
|
- Preserve existing non-edited metadata (ReplayGain, lyrics, etc.)
|
|
- Preserve audio data byte-for-byte (no re-encoding)
|
|
- Crash-safe writes via AtomicWrite
|
|
- Batch and single-track editing work for both formats
|
|
|
|
**Should have (differentiators):**
|
|
- OGG cover art via METADATA_BLOCK_PICTURE (base64-encoded FLAC picture block)
|
|
- WAV cover art via ID3v2 APIC frame (identical to MP3)
|
|
- Preserve existing RIFF INFO chunks when writing ID3v2 to WAV
|
|
- Round-trip test coverage (7 tests per format, following FLAC precedent)
|
|
|
|
**Defer (v2+):**
|
|
- RIFF INFO writing (lossy — can't represent album_artist, disc_number, or cover art)
|
|
- Dual-write ID3v2 + RIFF INFO in WAV
|
|
- Migrating deprecated OGG `COVERART` field to `METADATA_BLOCK_PICTURE`
|
|
- RF64 (>4GB WAV) support
|
|
- OGG Opus tag writing (different header structure from Vorbis)
|
|
|
|
### Architecture Approach
|
|
|
|
Both writers integrate into the existing pipeline with minimal modification: add two format constants, extend the `DetectFormat()` switch, and add two cases to the `WriteTrackTags()` format dispatch. No new interfaces, no refactoring. The frontend is completely format-agnostic and requires zero changes.
|
|
|
|
**Major components:**
|
|
1. **`ogg.go`** (~350 LOC) — OGG page parser/writer, Vorbis Comment serializer, CRC32, METADATA_BLOCK_PICTURE encoding, `writeOggTags()` orchestrator
|
|
2. **`wav.go`** (~200 LOC) — RIFF chunk parser/writer, ID3v2 tag in `id3 ` chunk via `bogem/id3v2`, `writeWavTags()` orchestrator
|
|
3. **`tagwriter.go` + `pipeline.go`** (~15 LOC changes) — Format constants, detection, dispatch
|
|
4. **`ogg_test.go` + `wav_test.go`** (~600 LOC) — 7 round-trip tests each, following FLAC pattern
|
|
|
|
**Key code reuse:**
|
|
- Vorbis Comment field mapping: identical to FLAC (extract shared helpers from `flac.go`)
|
|
- ID3v2 tag building for WAV: identical to MP3 (`applyTextChanges()`, `applyCoverArtChanges()`)
|
|
- AtomicWrite: used as-is by both writers
|
|
- `dhowden/tag` for read-back verification in tests
|
|
|
|
### Critical Pitfalls
|
|
|
|
1. **OGG CRC32 non-standard bit ordering (P1)** — OGG uses MSB-first CRC32 with polynomial 0x04c11db7. Go's `hash/crc32` uses reflected (LSB-first) ordering and produces wrong checksums. Must implement custom CRC or port from `jfreymuth/oggvorbis/crc.go`.
|
|
|
|
2. **OGG page sequence number continuity (P2)** — Rewriting comment header may change the number of header pages, requiring all subsequent page sequence numbers to be renumbered. Use full-stream rewrite approach (correct by construction).
|
|
|
|
3. **OGG granule position preservation (P3)** — Header pages must have granule position 0; audio pages must preserve original granule positions exactly. Corruption here breaks seeking and duration reporting.
|
|
|
|
4. **WAV RIFF chunk size updates (P6)** — Adding or resizing the `id3 ` chunk requires updating the outer RIFF header size field. Wrong size makes the file appear truncated to some players.
|
|
|
|
5. **WAV chunk word alignment (P10)** — RIFF chunks must start at even byte offsets. Odd-length chunks need a padding byte that's NOT included in the chunk's size field but IS part of the physical file.
|
|
|
|
### WAV Metadata Approach Decision
|
|
|
|
Research revealed a tension between two approaches:
|
|
- **RIFF INFO:** Native WAV format, simple, but can't represent album_artist, disc_number, or cover art
|
|
- **ID3v2-in-WAV:** Reuses existing `bogem/id3v2`, full field + cover art support, read by `dhowden/tag`
|
|
|
|
**Decision: ID3v2-in-WAV.** This gives full field parity with MP3, enables cover art, reuses existing code, and round-trips through `dhowden/tag` (our reader). RIFF INFO is preserved when present but not written to.
|
|
|
|
## Implications for Roadmap
|
|
|
|
Based on research, suggested phase structure:
|
|
|
|
### Phase 1: WAV Tag Writer
|
|
**Rationale:** Lower risk, faster to implement. Reuses existing `bogem/id3v2` library and `applyTextChanges()`/`applyCoverArtChanges()` from MP3 writer. RIFF container is simpler than OGG (no checksums, no page segmentation). Building this first proves the pipeline extension pattern works before tackling the harder OGG format.
|
|
**Delivers:** WAV text tag writing (all 8 fields) + cover art + round-trip tests
|
|
**Addresses:** WAV table stakes + WAV cover art (differentiator, but trivial since it reuses MP3 APIC code)
|
|
**Avoids:** P5 (use ID3v2, not RIFF INFO), P6 (careful RIFF size bookkeeping), P10 (chunk alignment padding)
|
|
**New code:** ~200 LOC `wav.go` + ~250 LOC `wav_test.go` + ~15 LOC pipeline changes
|
|
**Estimated effort:** Small — RIFF parsing is straightforward binary parsing
|
|
|
|
### Phase 2: OGG Vorbis Text Tag Writer
|
|
**Rationale:** OGG requires the most new infrastructure (page parser, CRC32, segmentation). Text-only tag writing exercises all the hard parts (page rewrite, CRC, sequence numbers) without the added complexity of multi-page comment packets from large cover art. This is the riskiest phase and benefits from Phase 1 having proven the pipeline extension works.
|
|
**Delivers:** OGG Vorbis text tag writing (all 8 fields) + round-trip tests
|
|
**Addresses:** OGG text field table stakes, audio data preservation, existing comment preservation
|
|
**Avoids:** P1 (CRC32), P2 (sequence numbers), P3 (granule positions), P4 (three-header structure), P7 (framing bit), P8 (packet prefix)
|
|
**New code:** ~300 LOC `ogg.go` (page infra + text writer) + ~300 LOC `ogg_test.go`
|
|
**Estimated effort:** Medium — OGG page infrastructure is the hardest new code in this milestone
|
|
|
|
### Phase 3: OGG Vorbis Cover Art
|
|
**Rationale:** Separated from Phase 2 because it adds multi-page packet complexity (large base64-encoded images can exceed the ~64KB OGG page limit). Text fields exercise the page infrastructure with small comment packets; cover art stress-tests it with large ones. Can be deferred if Phase 2 runs long without blocking the milestone.
|
|
**Delivers:** OGG Vorbis cover art embed/remove via METADATA_BLOCK_PICTURE
|
|
**Addresses:** OGG cover art differentiator
|
|
**Avoids:** P9 (METADATA_BLOCK_PICTURE format), P15 (multi-page segmentation for large payloads)
|
|
**New code:** ~50 LOC additions to `ogg.go` + ~50 LOC additions to `ogg_test.go`
|
|
**Estimated effort:** Small if Phase 2's page infrastructure is solid; medium if multi-page edge cases surface
|
|
|
|
### Phase 4: Edge Cases and Cleanup
|
|
**Rationale:** Validation and hardening after core functionality works. Adds detection/rejection of unsupported edge cases, size warnings, and documentation updates.
|
|
**Delivers:** RF64 detection, multi-stream OGG detection, large file warnings, PROJECT.md updates
|
|
**Addresses:** P13 (multi-stream OGG), P16 (RF64 WAV), P12 (disk space for large files)
|
|
**New code:** ~30 LOC validation checks + documentation updates
|
|
**Estimated effort:** Small
|
|
|
|
### Phase Ordering Rationale
|
|
|
|
- **WAV before OGG:** WAV is lower risk (reuses existing ID3v2 library, simpler container) and proves the pipeline extension pattern. OGG requires all-new page infrastructure with correctness-critical CRC and sequencing.
|
|
- **OGG text before OGG cover art:** Text fields exercise the page rewrite with small comment packets. Cover art adds multi-page complexity that should only be attempted once the core page infrastructure is validated by round-trip tests.
|
|
- **Edge cases last:** Detection/rejection of unusual files (RF64, multi-stream) is low risk and low effort — just validation guards at file-open time.
|
|
|
|
### Research Flags
|
|
|
|
Phases likely needing deeper research during planning:
|
|
- **Phase 2 (OGG text writer):** The OGG page re-segmentation and CRC implementation is the most complex new code. The spec is clear, but implementation details (lacing values, continuation flags, packet splitting across pages) benefit from studying `jfreymuth/oggvorbis` source as reference. Phase-level research recommended.
|
|
|
|
Phases with standard patterns (skip research-phase):
|
|
- **Phase 1 (WAV writer):** RIFF parsing is trivial; ID3v2 tag generation reuses existing code. Well-documented, no unknowns.
|
|
- **Phase 3 (OGG cover art):** METADATA_BLOCK_PICTURE format is well-specified; base64 encoding is trivial. Only depends on Phase 2's page infrastructure being correct.
|
|
- **Phase 4 (edge cases):** Simple validation checks with clear specifications.
|
|
|
|
## Confidence Assessment
|
|
|
|
| Area | Confidence | Notes |
|
|
|------|------------|-------|
|
|
| Stack | HIGH | Zero new dependencies; all recommendations based on official specs and existing codebase analysis |
|
|
| Features | HIGH | Feature set derived from official format specs (Xiph.org, RIFF) and existing codebase field model |
|
|
| Architecture | HIGH | Full codebase analysis confirms pipeline was designed for format extension; minimal changes needed |
|
|
| Pitfalls | HIGH | Pitfalls sourced from official OGG/RIFF specs, cross-referenced with existing library implementations |
|
|
|
|
**Overall confidence:** HIGH
|
|
|
|
### Gaps to Address
|
|
|
|
- **`bogem/id3v2` WAV compatibility:** The library's `Open()`/`Save()` API expects MP3 file structure. For WAV, we'll need to use `ParseReader()` to read existing ID3v2 tags from a byte slice, and `WriteTo()` to serialize the tag to bytes for embedding in the RIFF chunk. This needs validation during Phase 1 implementation — if `ParseReader` doesn't work for standalone tag parsing, we may need to create tags from scratch (losing existing ID3v2 data in the WAV).
|
|
- **OGG test fixture creation:** Cannot programmatically generate a valid OGG Vorbis file (requires Vorbis codebook data in setup header). Need to embed a minimal OGG fixture via `//go:embed`. Can be created once with ffmpeg during Phase 2 setup.
|
|
- **Multi-stream OGG prevalence:** Research confirms multi-stream OGG music files are extremely rare, but we should detect and reject them rather than silently corrupting. Validation during Phase 2.
|
|
|
|
## Sources
|
|
|
|
### Primary (HIGH confidence)
|
|
- OGG framing specification: https://xiph.org/ogg/doc/framing.html
|
|
- OGG RFC 3533: https://xiph.org/ogg/doc/rfc3533.txt
|
|
- Vorbis I Specification (comment field): https://xiph.org/vorbis/doc/Vorbis_I_spec.html
|
|
- Vorbis Comment specification: https://xiph.org/vorbis/doc/v-comment.html
|
|
- METADATA_BLOCK_PICTURE: https://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE
|
|
- FLAC Picture block format: https://xiph.org/flac/format.html#metadata_block_picture
|
|
- RIFF/WAV format: https://www.mmsp.ece.mcgill.ca/documents/AudioFormats/WAVE/WAVE.html
|
|
|
|
### Secondary (MEDIUM confidence)
|
|
- WAV metadata overview: https://en.wikipedia.org/wiki/WAV#Metadata
|
|
- RIFF tag reference: https://exiftool.org/TagNames/RIFF.html
|
|
|
|
### Libraries (HIGH confidence — direct code review)
|
|
- `jfreymuth/oggvorbis` v1.0.5: OGG page reader reference, CRC32 lookup table
|
|
- `dhowden/tag`: Reads OGG + WAV tags; validates round-trip correctness
|
|
- `bogem/id3v2` v2.1.4: ID3v2 tag generation for WAV `id3 ` chunks
|
|
- `go-flac/flacpicture` v2.0.2: FLAC picture block builder, reused for OGG METADATA_BLOCK_PICTURE
|
|
- `go-audio/wav` (ARCHIVED 2026-02-21): Evaluated and rejected
|
|
- `mccoyst/ogg` (37 stars, no semver): Evaluated and rejected — marginal benefit vs dependency risk
|
|
|
|
### Codebase (HIGH confidence — validated in v1.2)
|
|
- `backend/tagwriter/flac.go` — Vorbis Comment manipulation patterns to reuse
|
|
- `backend/tagwriter/mp3.go` — ID3v2 + AtomicWrite patterns to reuse for WAV
|
|
- `backend/tagwriter/pipeline.go` — Format dispatch switch to extend
|
|
- `backend/tagwriter/tagwriter.go` — TagChanges model, format detection, helpers
|
|
- `backend/fileutil/atomicwrite.go` — Crash-safe file write utility
|
|
|
|
---
|
|
*Research completed: 2026-03-18*
|
|
*Ready for roadmap: yes*
|