Commit Graph
1565 Commits
Author SHA1 Message Date
alehelandClaude Opus 5.5 99dcf517a4 craft-audio: review fixes, round 13 (#442)
FLAC in Ogg reads its layout the way a FLAC file does: from the mask
comment, or else the one decoders assume for the count. Without this, a
6-channel stream with no comment reported back surrounds in Ogg and side
surrounds in a FLAC file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 14:49:49 +01:00
alehelandClaude Opus 5.5 57f5d063fe craft-audio: review fixes, round 12 (#442)
- FLAC files read their layout from the channel mask comment, or else
  the one decoders assume for the count, which is what the writer omits
  the comment for
- an unbuilt format is Unsupported before the layout is checked
- FLAC encode no longer reserves half the PCM size up front
- flac-encode's lib tests stream a file and compare it with encode
- the seek test compares samples after the seek

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 13:51:18 +01:00
alehelandClaude Opus 5.5 1a9f579a07 craft-audio: review fixes, round 11 (#442)
No change in output: the 74 oracle test streams are byte-identical.

- The Wasted case is handled in one function, without the unreachable arm
  and the wrapper round 8 added.
- The bitstream writer uses predict::fold, the tested copy, not its own.
- STREAMINFO's MD5 may be 0 (RFC 9639 §8.2), so the comment no longer says
  the RFC requires it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 afdc045e94 craft-audio: review fixes, round 10 (#442)
Tests only; each was confirmed to fail on a mutated encoder.

- Every sample rate code in the frame header table.
- Signals that reach the Rice parameter and LPC shift limits, where a
  value past the limit gives a file decoders refuse.
- ENCODE_EXTENSIONS lists flac.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 5617261fdc craft-audio: review fixes, round 9 (#442)
Refuse a linear predictor whose prediction leaves the i32 range. Such a
stream is valid, but symphonia adds the prediction to the residual as an
i32, which panics in a debug build. The round 8 residual limit made this
reachable at 24 bits; without the override, 13 of 240 clipped 32-bit test
streams failed in a debug build and none do now, for 0.09% more bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 82f63f3a4f craft-audio: review fixes, round 8 (#442)
- Code wasted bits (RFC 9639 §9.2.2): low bits that are zero in every
  sample of a subframe were stored anyway, so 16-bit audio exported at 24
  or 32 bits grew by the padding.
- Allow residuals up to the 32-bit limit (§9.2.7.3). The limit was half
  that, so at 32 bits a transient forced its whole block to verbatim.
- README: 32-bit FLAC codes stereo channels independently.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 c008e8ee07 craft-audio: review fixes, round 7 (#442)
- Test the high bits of STREAMINFO's total sample count, and that a total
  past 36 bits is left unknown.
- README limits: the usual 5- and 6-channel layouts depend on the mask
  comment too; say what is outside the streamable subset.
- Comments: 32-bit code 0 does not decode everywhere and leaves the
  subset; the oracle test helper is in craft_media_testkit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 430ad15683 craft-audio: review fixes, round 6 (#442)
Tests only, plus docs; the encoder does not change. Each new check was
confirmed to fail on a mutated encoder.

- Tests: frame numbers count up, LPC and Rice partitions and stereo
  decorrelation save the space they should, FileEncoder records a FLAC
  layout, the smallest frame anywhere reaches STREAMINFO, Writer::write
  applies the frame limit, finish leaves the sink at the end, the exact
  655350 Hz cap, the sample size codes.
- README limits: a FLAC layout decoders would not assume depends on its
  mask comment, and ffmpeg and flac ignore a zero mask.
- The encode example's comment named no referent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 82424e59c3 craft-audio: review fixes, round 5 (#442)
- libFLAC and ffmpeg read a FLAC with 5 or 6 channels and no mask comment
  as having side surrounds, so the comment is now written for anything
  else, including the crate's usual back-surround layouts. The round 3
  check treated back surrounds as FLAC's assignment, which lost them.
- Tests: the mask comment rules, opposite-polarity stereo, STREAMINFO
  block and frame sizes.
- A stale comment said only WAV records the layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 8ba7778f8c craft-audio: review fixes, round 4 (#442)
The PICTURE metadata test inserts its block without the last-block flag:
the encoder's VORBIS_COMMENT follows it, and the flagged version was a
broken metadata chain that only decoded by resyncing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 1648f33e6a craft-audio: review fixes, round 3 (#442)
FLAC files record the speaker layout when FLAC's channel assignment
does not already say it, as a WAVEFORMATEXTENSIBLE_CHANNEL_MASK comment.
FLAC's 7-channel order is 6.1 with side surrounds, so a buffer in the
crate's usual 7-channel layout (backs and a back centre) was decoded by
ffmpeg and libFLAC with three channels on the wrong speakers; explicit
layouts such as 5.1 with side surrounds were dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 76df23e86e craft-audio: review fixes, round 2 (#442)
- check_room docs: FLAC holds at most 2^36 - 1 frames.
- The FLAC round trip covers every channel count from 1 to 8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 7c88c201a5 craft-audio: review fixes, round 1 (#442)
- FileEncoder/encode for FLAC hand the encoder one 4096-frame block at a
  time: a whole-buffer encode drained the pending samples quadratically
  (480 s of stereo took 19 s, now 5 s) and copied the whole input twice.
- Frame headers name sample rates outside the table (codes 12-14), so
  streams at rates like 11025 or 37800 Hz stay in the streamable subset.
- The decode doctest needs symphonia to read FLAC at this point.
- README: FLAC output is verified against ffmpeg, not the flac tool.
- Tests: keep the AIFF force-RF64 check; exact 2^36 frame boundary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 550732201c craft-audio: FLAC encoder
FilmCraft's FLAC encoder behind `flac-encode`, kept over SoundCraft's:
linear prediction, stereo decorrelation and the STREAMINFO MD5 give
0.1-37% smaller files on 12 test signals, and the output is bit-exact
through `flac` and ffmpeg. It is 3-5x slower than SoundCraft's, still
about 56x real time.

`lossless-encode` is now `pcm-encode` plus `flac-encode`, and
`EncodeFormat::Flac` writes it through `encode` and `FileEncoder`.
With only one of the two features, the other's formats are refused as
`Unsupported`.

The tests that use FLAC files as decode fixtures (truncation,
corruption, metadata bounds, zero-frame files, container preflight)
come with it, as do the FLAC oracle tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
Aleksander HelgakerandClaude Opus 5.5 38740b43a9 craft-audio: core types, WAV/AIFF read and write, peaks, symphonia (#441)
* craft-audio: core types, WAV/AIFF read and write, peaks, symphonia

The new crate without any codec module: what every later codec plugs
into.

- Shared types: `AudioBuffer`, `AudioInfo`, `ChannelLayout`/`Speaker`,
  `AudioError`, `EncodeOptions` (built with `new` and `with_*`),
  `EncodeFormat`, `BitDepth`, `BwfInfo`, `SampleFormat`.
- WAV/BWF/RF64 and AIFF/AIFF-C reading and writing from SoundCraft's
  `soundcraft-audio-io`, kept over EffectCraft's and FilmCraft's
  smaller writers (only it has BWF, dither, any speaker mask and
  streaming without a known length). `encode` and `FileEncoder` behind
  `pcm-encode`; `lossless-encode` and `encode` turn it on.
- `decode`, `probe`, `tracks` and the streaming, seekable `Decoder`
  (feature `decode-native`). The native path reads WAV, AIFF, and PCM in
  MP4/MOV and Matroska through craft-media's demuxers, packet by packet,
  with edit lists and `CodecDelay`/`DiscardPadding` applied.
  `TrackDecoder` decodes PCM; each codec PR adds its codec to it.
- symphonia 0.6 (MPL-2.0, used unmodified) behind the `symphonia`
  feature, part of `decode`: the fallback for every format with no
  native decoder yet. Its size fields are checked before it reads, and
  its panics are caught where they unwind.
- `peaks`: the min/max/RMS pyramid for waveform drawing.

Workspace: craft-media is a workspace dependency without default
features, so an encode-only craft-audio does not build the MP4
demuxer, and `symphonia-bundle-flac` builds without overflow checks in
dev profiles (its 32-bit LPC restore wraps on valid input).

CI: `audio.yml`, with clippy per feature, rustdoc, WASM, MSRV, and the
tests with ffmpeg oracles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-audio: review fixes (CodeRabbit on #441)

- Reading: AIFF that declares frames without an SSND chunk is Malformed; a Matroska PCM track's
  length counts from its first frame; seek targets saturate; an empty FLAC has one track; a
  failed header read is an Io error; deinterleave allocates fallibly; concealed silence is capped
  like a decoded packet, and symphonia's uses the damaged packet's own duration when known.
- Peaks sum squares in f64, so loud float samples give a finite RMS.
- Writing: .aifc maps to AIFF; BWF text is cut at a character boundary; a WAV byte rate past
  32 bits is an error; the FileEncoder docs say nothing may follow the sink's position.
- README: the Limits list says which codecs go through symphonia and which MP4 edit lists apply.
- Tests for each, alias and dither coverage, and a FLAC case in the routing oracle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-audio: review fixes, second round (CodeRabbit on #441)

- Every header read is checked: `Native::open`, `native::tracks` and the no-symphonia error read
  it with `head_checked`. craft-media demuxer errors that are I/O failures (not a file ending
  early) are returned as `AudioError::Io` instead of sending the file to the other path.
- A Matroska track's presented length runs from its first frame plus `CodecDelay` (unclamped,
  in timestamp ticks with the delay rounded to one) to its end.
- symphonia's concealment allocates fallibly, and a damaged first packet is concealed when the
  demuxer gives its duration. `deinterleave` reserves its channel list fallibly too.
- `TrackDecoder::conceal` documents its cap at the decode limit.
- Tests: an I/O failure past the first 64 KiB, codec delay with the first block at -20, 0 and
  50 ms (checked with `probe`), a seek back after an overshoot, and the loud-sample RMS value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-audio: TrackDecoder::conceal returns a Result

Concealment allocates fallibly and fails with TooLarge when the silence would pass the 2^30-sample
decode limit, instead of returning less than the packet's length. The native path, the README,
the packets example and the doc example pass the error on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-audio: review fixes, third round (CodeRabbit on #441)

- Concealed silence is held to the decode limit inside pcm::silence, so symphonia's path checks
  frames × channels too, and symphonia's decoded audio is copied into fallibly allocated planes.
- TrackDecoder::reset forgets the last packet's length.
- Without symphonia, a damaged MP4, Matroska or Ogg file (truncated, invalid, no moov) is
  Malformed instead of Unsupported; a feature the demuxer does not read still names the build.
- Tests: an MP4 with no moov in a native-only build, conceal after a reset and past the limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-audio: review fixes, fourth round (CodeRabbit on #441)

- A file the native demuxers find damaged is Malformed with symphonia too, when symphonia calls
  it unsupported; before, an ftyp-only MP4 was Unsupported in symphonia builds.
- symphonia's decoded blocks are held to the 2^30-sample decode limit before allocating.
- Symph::seek forgets the last packet's length, so a lost packet after a seek is not concealed
  with the length of one before it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-audio: review fixes, fifth round (CodeRabbit on #441)

- The damaged-file test covers Matroska as well as MP4, and fails when either branch of
  native::damage is removed.
- native::damage leaves out Ogg: ogg::open reports no damage (it skips damaged pages and calls a
  file with none it can read not Ogg), and symphonia reports damaged Ogg as malformed itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-audio: run the ffmpeg oracle tests on ubuntu-26.04

ubuntu-latest's apt ships ffmpeg 6.1.1, which writes MP2 and E-AC-3 in
Matroska with ends a few milliseconds early, so the routing tests on the
later codec PRs cannot pass on it. ubuntu-26.04 ships ffmpeg 8.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:25:57 +01:00
.CooCoo 893eafc086 craft-update: gate translated button labels with their egui callers (#542) 2026-11-04 09:29:06 +01:00
Dylan M. Taylor fe4e2ff3ef menu: never activate a row from an earlier frame (#574)
Nav kept the rows of the last frame that drew them. When a title click
closed the popup before its rows drew again, Enter or Space ran the
cached command with its old enabled state. Nav::load now clears the rows,
so the keys act only on rows drawn this frame under the current gate.
2026-11-03 23:06:04 -05:00
Dylan M. Taylor 4029f023a6 ui: check floating destinations and integrity-matched font bytes (#567)
- docking: apply_with_permissions refuses a move or open into a floating
  window for a panel that is not floatable (Move, OpenAt, MovePanel and
  group/column/window moves), and a Group placement at a floating anchor,
  which becomes a split of its frame, needs accepts_splits.
- fonts: the served-font loader keys fetched files, in-flight requests,
  failures and pending UI faces by (url, integrity), and installs bytes
  only into faces requiring the integrity they were fetched under
  (Stack::provide_fetched); the public Stack::provide is unchanged.
2026-11-03 23:05:04 -05:00
Dylan M. Taylor d8568d6cfc text: keep hyphenation and line-breaking work linear and bounded (#560)
* text: keep hyphenation and line-breaking work linear and bounded

- hyphen::dict: inflate with a 64 MiB limit (MAX_INFLATED).
- hyphenate_word: walk the sorted break offsets with one iterator.
- patgen::score: index expected breaks once per word.
- patterns: FxHasher's finish rotates the multiplied bits down so packed
  keys sharing low bits spread over buckets; at most MAX_PATTERNS (1M).
- linebreak: Sums precomputes each item's next box or forced break,
  so next_start is a lookup instead of a scan per candidate.

* address review: refuse to serialize hyphenation dictionaries past the inflate limit
2026-11-03 23:04:30 -05:00
Dylan M. Taylor cb82b90580 print: charge skipped whitespace and comments to the lexer's token budget (#547)
* print: charge skipped whitespace and comments to the lexer's token budget

Lexer::skip_ws scanned whitespace and comments without charging them,
so an annotation object behind a long comment was rescanned in full for
every reference to it, outside the per-object and job-wide budgets. The
skipped bytes now count like tokens, and the scan stops at the budget.

* address review: fail objstm load when header lexer exhausts, restore token charge on failed reference lookahead

* address review: charge objstm header lexer tokens to the job budget
2026-11-03 22:58:48 -05:00
FredApps 01315d9c92 Merge pull request #590 from benandez/fix/variant-pack-locale
proof: a variant after the region is looked up without the region first (ca-ES-valencia → ca-valencia)
2026-11-03 23:21:37 +01:00
FredApps e8629ecdea Merge pull request #589 from benandez/fix/fr-elision-suggest
proof: offer the elided form (J'adore) before the missing space; English keeps apostrophes late
2026-11-03 23:21:02 +01:00
Martin BallandClaude Opus 5.5 11ed4abc1e proof: the Italian elision test checks dell'anno ranks before dell anno (CodeRabbit)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
2026-11-03 22:07:32 +00:00
Martin BallandClaude Opus 5.5 5933160655 proof: a test pins English's elision: false; elision docs say it ties with dropping a typed accent (review)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
2026-11-03 21:46:56 +00:00
Martin BallandClaude Opus 5.5 7a334e5306 proof: a variant after the default script and a region is looked up without the region too (sr-Cyrl-RS-1994 → sr-1994)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
2026-11-03 21:46:07 +00:00
Martin BallandClaude Sonnet 5.5 72e21ec7ca craft-proof: a variant after the region is looked up without the region first (ca-ES-valencia reads the ca-valencia pack) (review: FredApps)
One region_index/without_index helper in lang.rs serves region(), pack_lang::names and packs::locales_for; tests pin the sr-RS-ijekavsk names and the length cut of sr-Cyrl-RS-ijekavsk.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
2026-11-03 21:40:25 +00:00
Martin BallandClaude Sonnet 5.5 18cc6533fa proof: elision docs match the costs; DEFAULT's elision is gated by the dictionary (review: FredApps)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
2026-11-03 21:37:45 +00:00
Martin BallandClaude Opus 5.5 3622f802e7 proof: rank only a true elision first; other apostrophe forms after one-letter fixes
Review of the elision fix: the cheap cost went to every apostrophe form the
dictionary accepts, so English possessives (churchs → church's), contractions
(neer → ne'er) and Catalan enclitics (acusit → acusi't) jumped ahead of the
one-letter fix. A missing apostrophe now ranks first only before a dictionary
word of two letters or more (j'adore, l'home, qu'il, dell'anno) and only when
the language elides (new Tuning::elision, off for English). Any other form costs
COST_MISTYPE + 1, after every one-letter fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
2026-11-03 21:37:45 +00:00
Martin BallandClaude Opus 5.5 d7bbf0a9d4 proof: offer the elided form (J'adore) before the missing space (J adore)
Suggestions tried a missing space but never a missing apostrophe, so French
"Jadore" got "Adore, J adore, Jade" and never "J'adore". Round 4 now also
offers a'b after a short first part (at most 7 letters), ranked before every
split. The dictionary's own check decides whether the elision is right, so
French, Italian and Catalan elision (l'acqua, dell'anno) work and a dictionary
without elided forms is unaffected. Suggestions keep the writer's case and the
apostrophe the dictionary's OCONV writes (J’adore). Costs at most one lookup
per split.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
2026-11-03 21:37:45 +00:00
dotCooCoo 600734ef54 font-store: ignore malformed optional receipt values 2026-11-03 12:16:28 -08:00
dotCooCoo 8e6e369793 font-store: verify removal receipts through the recovery handle 2026-11-03 12:13:04 -08:00
dotCooCoo 5c9589c4c8 font-store: keep recovery moves and cleanup on the same handles 2026-11-03 12:09:19 -08:00
dotCooCoo f9ef3d4b17 font-store: anchor immediate recovery stage cleanup 2026-11-03 12:04:43 -08:00
dotCooCoo 40cd57930a font-store: confine recovery cleanup and preserve valid receipts 2026-11-03 11:59:16 -08:00
Dylan M. Taylor 32b2fd8adf media: cap a Matroska track's content encoding chain (#541)
A track could list any number of ContentEncoding entries, and readers walk
the whole list for every frame they return (frames_readable,
stripped_header). A track with more than 16 entries, far beyond the one or
two a muxer writes, is now treated as having an unsupported compression, so
its frames are returned as stored and the list is never long.
2026-11-03 14:10:50 -05:00
dotCooCoo 37ec49baca craft-doc: validate explicitly zero ICC frame sizes 2026-11-03 11:09:25 -08:00
dotCooCoo af2df05a35 craft-doc: retain full composite provenance in renditions 2026-11-03 10:40:31 -08:00
dotCooCoo 21a7644df4 craft-doc: keep ICC helper separate from metadata additions 2026-11-03 10:17:44 -08:00
dotCooCoo ee2d912f24 craft-doc: read a bounded PhotoCraft ICC payload 2026-11-03 10:08:44 -08:00
dotCooCoo 51c11d7b75 doc: explain metadata reader regression invariants 2026-11-03 10:01:48 -08:00
dotCooCoo 6e909558c1 PDF: compile the CCITT row helper only with images 2026-11-03 09:59:55 -08:00
dotCooCoo 1b3a3ed661 font-store: clarify the lock acquisition cleanup sweep limit 2026-11-03 09:31:01 -08:00
dotCooCoo e0738f2a0f PDF: cover cancellation in xref stream fallback 2026-11-03 09:19:21 -08:00
dotCooCoo 7b8566b25c font-store: bound removal cleanup and cover portable failure retries 2026-11-03 09:11:49 -08:00
dotCooCoo c8aeab2226 hayro-interpret: refresh the local image-reader digest 2026-11-03 09:05:50 -08:00
dotCooCoo a017817164 PDF: remove an unused JPX macro import 2026-11-03 09:03:01 -08:00
dotCooCoo b04ab28441 craft-doc: read a caller-defined PhotoCraft document head 2026-11-03 09:01:43 -08:00
dotCooCoo 9c9fdda76c font-store: make rollback recovery fixture portable 2026-11-03 08:56:21 -08:00
dotCooCoo 616d5b9ae0 craft-doc: test oversized unused composite previews 2026-11-03 08:18:55 -08:00
dotCooCoo de727cb97e font-store: preserve Language packs when removal cannot commit 2026-11-03 08:12:39 -08:00