| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Rewrites the ATRAC3 gain control pipeline to eliminate noise flashes
and substantially reduce pre-echo artifacts:
Transient detection & curve building (transient_detector.cpp/h):
- Replace legacy heuristic detector with ratio-scored DetectTransients:
score = peak/floor for rising (c/a) or falling (a/c) triplets, scored
and ranked, top-N kept, sorted by location
- Add explicit point0 derived from windowed-RMS match between bufCur and
the curve-modulated bufNext (CalcWindowedRmsAfterCurve)
- Replace RegionMax with RegionRMS for smoother region amplitude estimate
- Add per-band detection thresholds kMinScorePerBand[4] = {1.9,1.9,2.1,2.2}
- Dynamic minScore: scale threshold by min(1.5, max(1.0, overlapRatio)) to
suppress false-positive curves when previous frame dominates
- Scale constraint: curve[0].Level >= 3 to cap cross-frame amplification at 2x
Bit allocation (atrac3_bitstream.cpp/h):
- Add GainBoostPerBand[NumQMF] to TSingleChannelElement, computed in
CreateSubbandInfo and applied in CalcBitsAllocation
- levelBoost: compensate for Demodulate's GainLevel[minLevel] attenuation
- scaleBoost: compensate for next-frame cross-frame scale via lookahead
- Both capped (kLevelBoostCap=1, kScaleBoostCap=2) to avoid bit starvation
Upsampler (atrac3denc.cpp):
- Raise cutoff from 600 Hz to 800 Hz for tighter band separation
Tests (gain_processor_ut.cpp):
- Relax fixed curve shape assertions to ExpectCurveReasonable (bounds checks)
- Relax quantization error bound for dense-event spacing (<=128 samples)
Results (branch new_psy vs original baseline):
riddler: pre-echo worse 43->15/479 frames, 0 flashes
spine: pre-echo worse 192->107/1804 frames, 0 flashes
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
tracking
Encoder (atrac3denc.cpp / atrac3denc.h):
- Replace old TTransientParam / TransientParamsHistory with TSpectralUpsampler-
based CreateSubbandInfo(): analyses the upsampled QMF band, computes gain[]
and nextLevel from the contiguous look-ahead buffer, and calls CalcCurve to
build ATRAC3 gain-curve points.
- highFreqRatio guard: skip CalcCurve for sub-bass bands where the HPF signal
is too weak to produce meaningful gain control.
CalcCurve (transient_detector.cpp):
- Fix Issue 1 (FFT-window context mismatch at frame boundary):
Store ctx.LastLevel = in.back() instead of target (nextLevel).
in.back() and the next call's gain[0] are both analysis-domain estimates of
adjacent 8-sample blocks — no cross-domain FFT-window divergence that produced
false boundary transients.
- Guard against zero savedLastLevel (first frame or post-reset): return empty
curve rather than emitting scaleLevel=15 (GainLevel=1/2048) which would cause
extreme amplification in the gain modulator.
- Tighten gain-point budget to 7 (< MaxGainPointsNum=8) to match the 3-bit
count field in the ATRAC3 bitstream.
Tests (gain_processor_ut.cpp):
- Add BoundaryLevelMismatch suite: Issue1_FalseTransientOnConstantTone_AfterOnset,
Issue1_MdctRoundtrip_NoGain, Issue1_MdctRoundtrip_WithGain,
Issue1_RoundtripWithGainAndQuantization.
- Quantization test threshold set to 400× kQuantStep: correct two-point gain
curves for a 9:1 amplitude-ratio signal produce at most ~323× peak error
(scale×level=16 × ~8× IMDCT base noise); pathological false transients would
cause signal-level reconstruction errors well above this bound.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
CalcCurve (transient_detector.cpp/h):
- Recursive divide-and-conquer FindTransients scans the gain vector for
monotonic 3-subframe windows (rising or falling); kMinScore=2.0 filters
out oscillations smaller than a factor of 2 (no-op at Level 4).
- RelationToIdx maps an amplitude ratio to an ATRAC3 gain Level index.
- TCurveBuilderCtx carries LastLevel across frames; CalcCurve prepends it
as a virtual boundary element to detect Location=0 attacks.
- budget=8 matches ATRAC3 SubbandInfo::MaxGainPointsNum.
TSpectralUpsampler (transient_spectral_upsampler.cpp/h):
- Applies a Planck-taper window (ε=0.15) to a 512-sample context window,
forward-FFTs, applies a 3-bin raised-cosine HPF, zero-pads to 4096 bins,
and inverse-FFTs to give an 8× upsampled output.
- Returns highFreqRatio = Σ|X[k]·H[k]|²/Σ|X[k]|²; callers skip CalcCurve
when this is below kHighFreqThreshold=0.05, preventing false transients
from Planck noise-floor variation in sub-cutoff frames.
Tests:
- gain_processor_ut: upsampled-path blocks added to all FreqDomain tests;
CalcCurve negative tests (NegativeTests suite).
- transient_spectral_upsampler_ut: OutputSize, DCIsRemovedByLowCutFilter,
HighFreqSinePreservesRMS (parametrised), ChirpNoTransient (0→5510 Hz
sweep at 689 Hz low-cut, Len1024/16384/262144).
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Demonstrates handling a burst where the post-burst quiet level (A_after=2)
differs from the pre-burst quiet level (A_before=1).
Strategy: normalise to A_after using {{5,4},{2,12}}:
- scale = GainLevel[5] = 0.5 → amplifies quiet prefix ×2 to A_after
- Level=2 (GainLevel=4) attenuates burst ÷4 to A_after
- Remainder [104..255] already at A_after → untouched
Modulated bufNext is uniform A_after throughout → near-zero HF leakage
in both frame 1 and frame 2. No compensating gain needed. Release ramp
uses gainInc = 2^(-2/8) (GainLevel 4.0→1.0, 2-octave span to neutral).
Includes full TDAC round-trip verification.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Both tests now normalise to the dominant amplitude using the minimum
number of gain points, with no compensating gain needed in frame 2:
- AttackAndRelease (QUIET→LOUD→QUIET burst): switch from 3-point
{{7,4},{4,12},{7,31}} (normalise to A_loud, needed {{1,1}} frame 2
compensation) to 2-point {{4,4},{1,12}} (scale=1.0 leaves quiet
bufCur unchanged, loud burst attenuated ÷8 to A_quiet, quiet tail
falls in untouched remainder). Strengthen frame 2 assertion to ×10.
- ReleaseAndAttack (LOUD→QUIET→LOUD dip): new test with 2-point
{{4,4},{7,12}} (scale=1.0 leaves loud bufCur unchanged, quiet dip
amplified ×8 to A_loud, loud tail falls in untouched remainder).
Frame 2 is plain A_loud with no compensating gain.
Both include full TDAC round-trip verification.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Demonstrates that a QUIET->LOUD transient inside bufNext can be handled
in the current frame by amplifying the quiet prefix to match the loud
suffix, rather than deferring to the next frame.
Signal (3 frames):
frame 0: quiet (A=1) -- primes overlap
frame 1: quiet[0..55] + attack ramp[56..63] + loud[64..255]
frame 2: all loud (A=8) -- continuation
Gain: {{7, 7}} on frame 1
Level=7 -> GainLevel[7]=0.125 -> scale=0.125 (amplify x8)
Location=7 -> lastPos=56; constant [0..55] amplified x8 to A_loud
Transition [56..63]: level 0.125->1.0 at gainInc_atk=2^(3/8) rate;
signal pre-shaped with matching ramp so Modulate divides it out
exactly -> uniform A_loud*sin across the entire MDCT window.
Remainder [64..255]: loud signal untouched (already at A_loud). No
compensating gain needed on frame 2 -- loud bufCur and loud bufNext
are already matched.
Assertions:
EXPECT_LT(hf1_mod * 10, hf1_nomod) -- frame 1: HF leakage reduced >10x
EXPECT_LE(hf2_mod * 10, hf2_nomod) -- frame 2: no regression
Round-trip: Mdct(Modulate) -> Midct(Demodulate) recovers signal.
frame 1 Midct: Demodulate(siCur=empty, siNext={{7,7}})
frame 2 Midct: Demodulate(siCur={{7,7}}, siNext=empty)
Verified with EXPECT_NEAR(..., 1e-5) over frames 1 and 2.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|
|
|
gain_processor_ut.cpp covers three test suites:
TGainProcessor_Modulate (5 tests):
- Empty gain returns null op
- Single-point constant/transition/remainder regions
- Two-point and three-point envelopes
TGainProcessor_Demodulate (5 tests):
- Empty gain returns null op
- Single-point constant/transition/remainder regions
- Two-point envelope
TGainProcessor_Mirror (6 tests):
- Round-trip Modulate->Demodulate algebraic identity
- Constant, transition, remainder, and asymmetric-scale cases
TGainProcessor_FreqDomain (6 tests):
Each test builds a 3-frame signal, runs MDCT with Modulate, checks
HF energy reduction vs unmodulated, and verifies perfect signal
reconstruction via MDCT(Modulate)->MIDCT(Demodulate) with 1e-5 tolerance.
Test signals and gain envelopes (mirroring atrac3denc_ut.cpp reference tests):
| Test name | Frame 1 gain | Frame 2 comp gain |
|-------------------------------------------------------|-----------------------|-------------------|
| GainModulation_ReducesSpectralEnergy | {{7,0}} | none |
| GainModulation_ReducesSpectralEnergy_TransientInFrame | {{4,8},{7,31}} | {{1,1}} |
| GainModulation_ReducesSpectralEnergy_AttackAndRelease | {{7,4},{4,12},{7,31}} | {{1,1}} |
| GainModulation_ReducesSpectralEnergy_DcSignal | {{7,1}} | {{1,1}} |
| GainModulation_ReducesSpectralEnergy_DcSignal2 | {{7,1}} | {{1,0}} |
| GainModulation_ReducesSpectralEnergy_2PointsWithoutScaleDc2 | {{4,0},{1,31}} | none |
Round-trip Demodulate mapping (siCur -> siNext at frame 2 MIDCT):
| Test name | siCur | siNext |
|-------------------------------------------------------|-----------------------|---------|
| GainModulation_ReducesSpectralEnergy | {{7,0}} | empty |
| GainModulation_ReducesSpectralEnergy_TransientInFrame | {{4,8},{7,31}} | {{1,1}} |
| GainModulation_ReducesSpectralEnergy_AttackAndRelease | {{7,4},{4,12},{7,31}} | {{1,1}} |
| GainModulation_ReducesSpectralEnergy_DcSignal | {{7,1}} | {{1,1}} |
| GainModulation_ReducesSpectralEnergy_DcSignal2 | {{7,1}} | {{1,0}} |
| GainModulation_ReducesSpectralEnergy_2PointsWithoutScaleDc2 | {{4,0},{1,31}} | empty |
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|