<feed xmlns='http://www.w3.org/2005/Atom'>
<title>atracdenc/src, branch new_psy_cont</title>
<subtitle>OpenSource ATRAC1 ATRAC3 Encoder</subtitle>
<id>https://code.mastervirt.ru/atracdenc/atom?h=new_psy_cont</id>
<link rel='self' href='https://code.mastervirt.ru/atracdenc/atom?h=new_psy_cont'/>
<link rel='alternate' type='text/html' href='https://code.mastervirt.ru/atracdenc/'/>
<updated>2026-04-06T21:14:19Z</updated>
<entry>
<title>atrac3: add boundary transient thresholding to prune low-value gain transitions</title>
<updated>2026-04-06T21:14:19Z</updated>
<author>
<name>Daniil Cherednik</name>
<email>dan.cherednik@gmail.com</email>
</author>
<published>2026-04-06T21:10:51Z</published>
<link rel='alternate' type='text/html' href='https://code.mastervirt.ru/atracdenc/commit/?id=10c82ccfa7b802eba5d1366069bc796f5b48d76b'/>
<id>urn:sha1:10c82ccfa7b802eba5d1366069bc796f5b48d76b</id>
<content type='text'>
Problem
Gain curve generation emitted many +/-1 level transitions that do not correspond
to strong local transients. These points consume gain-info bits and can create
low-level modulation artifacts without improving transient handling.

Solution
Introduce explicit transient evidence gating at transition boundaries in
CalcCurve(), and wire it to the existing dynamic min-score path.

What changed
- Added BoundaryTransientScore(env, loc, win):
  - computes local ratio around each subframe boundary
  - R = max(max_right/max_left, max_left/max_right)
  - short symmetric window (win=3 subframes)
- Re-enabled minScore usage in CalcCurve() (previously ignored).
- For each level transition candidate at loc=sf+1:
  - keep unconditionally if loc==targetSf (tail neutral anchor)
  - keep unconditionally if |deltaLevel| &gt;= 2 (strong step)
  - otherwise keep only if BoundaryTransientScore(loc) &gt;= minScore
- Added YAML telemetry:
  - transient_min_score
  - transient_window
  - transition_pruned {loc, delta, score}

Why this is safe
- Strong transitions are preserved.
- Rightmost transition is preserved to keep proper return-to-neutral anchoring.
- Only low-confidence small toggles are removed.

Measured impact (current branch comparison)
Baseline: ea4d33b38 (before this change)
Tracks: show_me_your_spine.wav, 13.wav

Gain-info bits / points:
- spine: 191,697 -&gt; 150,297 bits  (delta -41,400; -21.6%)
         15,593 -&gt; 10,993 points (delta -4,600)
- 13.wav: 1,299,035 -&gt; 979,931 bits  (delta -319,104; -24.6%)
          97,035 -&gt; 61,579 points   (delta -35,456)

Subjective note
User listening reports improved sound and fixes for some low-level artifacts.
</content>
</entry>
<entry>
<title>atrac3: remove band3 transient boost redirection to band0</title>
<updated>2026-04-06T19:38:13Z</updated>
<author>
<name>Daniil Cherednik</name>
<email>dan.cherednik@gmail.com</email>
</author>
<published>2026-04-06T19:37:44Z</published>
<link rel='alternate' type='text/html' href='https://code.mastervirt.ru/atracdenc/commit/?id=ea4d33b38ed0c8fc9ebf10011e6e8e394e5dafbf'/>
<id>urn:sha1:ea4d33b38ed0c8fc9ebf10011e6e8e394e5dafbf</id>
<content type='text'>
</content>
</entry>
<entry>
<title>atrac3: make sticky gain quantization conditional and tune thresholds</title>
<updated>2026-04-06T18:57:40Z</updated>
<author>
<name>Daniil Cherednik</name>
<email>dan.cherednik@gmail.com</email>
</author>
<published>2026-04-06T18:55:17Z</published>
<link rel='alternate' type='text/html' href='https://code.mastervirt.ru/atracdenc/commit/?id=fedaf17422dad9c5a076a6a6c609dfd342f0541e'/>
<id>urn:sha1:fedaf17422dad9c5a076a6a6c609dfd342f0541e</id>
<content type='text'>
Problem
The distribution-aware sticky quantizer reduced gain-curve bitrate, but in some
release/transient frames it over-merged nearby transitions. On spine around
17.657s (ch1/band2), this collapsed the curve shape and could produce an
audible spike.

What changed
- Added frame-level sticky eligibility gating in CalcCurve().
- Sticky is now enabled only when both conditions hold:
  - intra-frame ratio is limited: max_gain / target &lt;= kStickyMaxIntraFrameRatio
  - inter-frame target jump is limited: prev_target / target (symmetric) &lt;= kStickyMaxInterFrameRatio
- Added local uncertainty guard for sticky hold:
  - require idx span from [subframeLow, subframeHigh] quantization to be narrow
    (idxSpan &lt;= 1) before allowing prev-level hold.
- Added YAML diagnostics per band/frame to make gating decisions auditable:
  - sticky_frame_eligible
  - sticky_intra_ratio
  - sticky_inter_ratio

Threshold tuning
Swept candidate pairs on both tracks:
- show_me_your_spine.wav
- 13.wav
Pairs tested:
(5,6), (5,8), (6,8), (6,10), (7,8), (7,10), (8,12)

Selected:
- kStickyMaxIntraFrameRatio = 7.0
- kStickyMaxInterFrameRatio = 10.0

Reason for selection
- Keeps safety behavior on known failure site:
  frame 760, ch1, band2 remains sticky_frame_eligible=false
  and retains non-collapsed curve shape (loc 1,2,5,7).
- Improves gain-modulation bitrate vs previous 6/8 tuning while avoiding fully
  open behavior.

Measured gain-modulation bits (spine + 13.wav)
- 6/8:  1,493,639 bits
- 7/10: 1,490,732 bits  (selected, -2,907 bits vs 6/8)
- 8/12: 1,488,824 bits  (lowest in sweep; not selected to keep extra margin)
</content>
</entry>
<entry>
<title>atrac3: add distribution-aware sticky gain quantization</title>
<updated>2026-04-06T17:16:00Z</updated>
<author>
<name>Daniil Cherednik</name>
<email>dan.cherednik@gmail.com</email>
</author>
<published>2026-04-06T17:14:32Z</published>
<link rel='alternate' type='text/html' href='https://code.mastervirt.ru/atracdenc/commit/?id=af40e881277e429fcabaa694524772480187d555'/>
<id>urn:sha1:af40e881277e429fcabaa694524772480187d555</id>
<content type='text'>
Problem
Gain curve construction still produced many +/-1 level toggles across long runs
(e.g. 7&lt;-&gt;8 chatter). These transitions are usually quantization noise from
subframe-level RMS rounding, not real envelope changes, and they consume gain
bit budget without improving transient protection.

Feature
Introduce distribution-aware sticky quantization for subframe gain levels.
Instead of quantizing only the subframe centre estimate, we also track a robust
within-subframe range and suppress one-step toggles when the previous level is
still consistent with that range.

Implementation
1) AnalyzeGain now optionally returns per-subframe low/high energy estimates
   (robust inter-quantile bounds from micro-chunk analysis inside each subframe).
2) CalcCurve now accepts optional subframe low/high vectors.
3) During sfLevel quantization:
   - compute centre level via RelationToIdx(filtered/target)
   - if new level differs from previous by exactly 1, and previous level is still
     inside [idx(low), idx(high)], keep previous level (sticky hold)
4) CreateSubbandInfo wires the new AnalyzeGain outputs into CalcCurve.
5) Existing point0 guard/boundary logic remains intact; this feature operates
   earlier at sfLevel formation.

Why this is safe
- Only suppresses +/-1 oscillation when previous level is still supported by
  observed subframe distribution.
- Does not clamp large transitions or remove structurally important points.
- Keeps curve scan/priority flow unchanged after sfLevel is formed.

Measured impact on current HEAD (gain-info bits)
Bit accounting uses ATRAC3 gain syntax: per channel header + per band point-count
fields + 9 bits per gain point.

show_me_your_spine.wav:
- base: 219,552 bits (18,688 points)
- with sticky: 172,158 bits (13,422 points)
- saved: 47,394 bits, 5,266 points (-21.59% gain-info bits)

13.wav:
- base: 1,537,724 bits (123,556 points)
- with sticky: 1,146,746 bits (80,114 points)
- saved: 390,978 bits, 43,442 points (-25.43% gain-info bits)
</content>
</entry>
<entry>
<title>atrac3: make point0 guard boundary-aware to avoid overlap artifacts</title>
<updated>2026-04-05T21:59:39Z</updated>
<author>
<name>Daniil Cherednik</name>
<email>dan.cherednik@gmail.com</email>
</author>
<published>2026-04-05T21:59:39Z</published>
<link rel='alternate' type='text/html' href='https://code.mastervirt.ru/atracdenc/commit/?id=0c051bc59e21b2abc39c1c8b9f192a6dc46f71d4'/>
<id>urn:sha1:0c051bc59e21b2abc39c1c8b9f192a6dc46f71d4</id>
<content type='text'>
Problem
The current point0 guard decides keep/revert only from an early-frame mismatch score.
That can revert a newly inserted point0 even when it is needed for frame-boundary
continuity. In ATRAC3 demodulation, the next frame's first gain level is reused as a
scale term for the overlap region, so removing point0 can change boundary scale by
multiple quantization steps and create audible artifacts.

Root cause
For frames like 13.wav around 45.1s, point0_guard reverted point0 in key bands,
which changed first-point scale and increased boundary mismatch despite a locally
better early-fit score.

Change
- Keep existing early mismatch metric (fit + leakage proxy).
- Add boundary-aware keep criterion inside point0_guard:
  * compute desired boundary scale in the same HPF domain:
      desiredScale = LimitRel(prevTarget / hpfRmsNextMod)
  * compare log2 distance of first-level scale before/after point0 insertion
  * if point0 reduces boundary error by a material margin (0.2 bits), force keep
    even when early-fit score slightly worsens
- Apply guard only when point0 actually changes the curve.
- Add YAML telemetry for boundary error before/after to support analysis.

Implementation details
- Added helper utilities to reconstruct subframe-average divisors from curve points
  and score early mismatch consistently.
- Updated point0 insertion/update flow to track whether point0 changed.
- Extended guard decision to combine:
  * early mismatch tolerance (existing behavior), and
  * boundary continuity improvement (new behavior).

Observed effect (focused check)
- On 13 clip (~45.1s), exact bad subframe (t=45.103129s, frame256=7769, sf32=23):
  ratio vs no-gain reduced from 9.30x to 1.51x after this change.
- Frame 1942 YAML now shows point0 kept in bands where boundary error drops
  substantially.

Notes
- No full regression run in this commit (intentional for fast iteration).
</content>
</entry>
<entry>
<title>atrac3: allow to configure median filter during gain curve calculation</title>
<updated>2026-04-05T18:01:46Z</updated>
<author>
<name>Daniil Cherednik</name>
<email>dan.cherednik@gmail.com</email>
</author>
<published>2026-04-05T18:01:46Z</published>
<link rel='alternate' type='text/html' href='https://code.mastervirt.ru/atracdenc/commit/?id=7bb5b1b5462b5004e89237922843585537bc5aff'/>
<id>urn:sha1:7bb5b1b5462b5004e89237922843585537bc5aff</id>
<content type='text'>
</content>
</entry>
<entry>
<title>atrac3: trim redundant point0 and skip point0 on band 3</title>
<updated>2026-04-05T10:24:59Z</updated>
<author>
<name>Daniil Cherednik</name>
<email>dan.cherednik@gmail.com</email>
</author>
<published>2026-04-05T10:24:59Z</published>
<link rel='alternate' type='text/html' href='https://code.mastervirt.ru/atracdenc/commit/?id=ae8ec39c4e88085bb1285089034ceed81ff7fde5'/>
<id>urn:sha1:ae8ec39c4e88085bb1285089034ceed81ff7fde5</id>
<content type='text'>
</content>
</entry>
<entry>
<title>atrac3: Prefer largest locations of gain curve points.</title>
<updated>2026-04-05T08:23:54Z</updated>
<author>
<name>Daniil Cherednik</name>
<email>dan.cherednik@gmail.com</email>
</author>
<published>2026-04-05T07:08:53Z</published>
<link rel='alternate' type='text/html' href='https://code.mastervirt.ru/atracdenc/commit/?id=07c2f068c1e468b5beccf45a5e4066cd55b1cbc3'/>
<id>urn:sha1:07c2f068c1e468b5beccf45a5e4066cd55b1cbc3</id>
<content type='text'>
</content>
</entry>
<entry>
<title>atrac3: use in.back() as staircase target instead of nextLevel</title>
<updated>2026-04-05T07:19:40Z</updated>
<author>
<name>Daniil Cherednik</name>
<email>dan.cherednik@gmail.com</email>
</author>
<published>2026-04-02T22:10:08Z</published>
<link rel='alternate' type='text/html' href='https://code.mastervirt.ru/atracdenc/commit/?id=c80ee6a667beb1e7d7aa27f25b96045333090262'/>
<id>urn:sha1:c80ee6a667beb1e7d7aa27f25b96045333090262</id>
<content type='text'>
For non-plateau frames, nextLevel (first lookahead subframe of the next
frame) can be 6× higher than in.back() on release frames.  Using it as
the staircase target caused tail subframes to appear below target →
spurious amplifying points (e.g. {level:7, loc:31}) on release tails,
and underestimated ATT on the peak (33× ratio reduced to 5× because the
wrong target inflated the denominator).

Fix: always use in.back() (actual last subframe of the analysis window)
as the staircase target.  That is where the signal truly returns to
within this frame.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>atrac3: replace CalcCurve with staircase level-scan algorithm</title>
<updated>2026-04-05T07:19:26Z</updated>
<author>
<name>Daniil Cherednik</name>
<email>dan.cherednik@gmail.com</email>
</author>
<published>2026-04-02T21:10:11Z</published>
<link rel='alternate' type='text/html' href='https://code.mastervirt.ru/atracdenc/commit/?id=f0876e929c002fe1adb4ae9a64cb603c8e782439'/>
<id>urn:sha1:f0876e929c002fe1adb4ae9a64cb603c8e782439</id>
<content type='text'>
Replaces the monotone-triplet transient detector with a level-based
staircase scan that builds the gain curve from the target subframe
leftward.  The new algorithm correctly handles rising transients by
attenuating the loud peak region rather than the quiet onset.

Key changes:
- 3-point median filter on gain[] suppresses isolated spikes
- Per-sf level = RelationToIdx(filtered[sf] / target)
- Scan leftward from first-neutral-sf, emit one point per level change
- Priority trim: keep up to 6 points with largest |ΔLevel| first

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
</feed>
