fix(draw_kit): reclaim 4x waveform oversample; relocate waveformColumnCount to component_geometry
kWaveformOversample set to 1 (was 4) — overbinning produced byte-identical pixels because columnMinMax exact partition already makes the draw gap-free. Comments credit columnMinMax, not oversampling.
This commit is contained in:
+6
-6
@@ -13,8 +13,8 @@
|
||||
// LICE-drawn named-banks tab-page region below (one tab per named bank, an
|
||||
// overflow/scroll strip), and two full-height toggles that collapse the split.
|
||||
// Each region reuses the M5 grid render loop (waveform thumbnails / empty state).
|
||||
// * per-sample PCM read via PCM_source fed to peaks::computeEnvelope, oversampled
|
||||
// (kWaveformOversample bins per drawn pixel column) for the FA3 gap-free draw.
|
||||
// * per-sample PCM read via PCM_source fed to peaks::computeEnvelope, one bin per
|
||||
// drawn pixel column; drawWaveform's gap-free render comes from peaks::columnMinMax.
|
||||
// * an in-memory thumbnail cache keyed by (sample id, draw width, bank generation).
|
||||
// * id-keyed bank management (create / rename / delete / evacuate / activate) and
|
||||
// sample move/copy — driven from a tab context menu and a drag — against the B1
|
||||
@@ -1350,10 +1350,10 @@ void drawRegionGrid(LICE_IBitmap* bmp, const RECT& region, bool isBanks,
|
||||
// BankIndex insertion order. Selection/focus are keyed by the occupied-ordinal (selection
|
||||
// space); a slot maps back to its ordinal via selectionForSlot.
|
||||
const RegionDisplay disp = regionDisplay(region, isBanks, reg);
|
||||
// FA3 anti-alias: thumbnails are computed OVERSAMPLED — kWaveformOversample bins per
|
||||
// drawn pixel column — and drawWaveform collapses them per column (peaks::columnMinMax)
|
||||
// so steep transients render as true full-height spans. computeThumbnail clamps the
|
||||
// request to the frame count.
|
||||
// FA3 gap-free: request one bin per drawn pixel column; drawWaveform's
|
||||
// peaks::columnMinMax exact partition makes every column gap-free — overbinning
|
||||
// produces byte-identical pixels at higher memory/CPU cost. computeThumbnail clamps
|
||||
// the request to the frame count.
|
||||
const int binWidth = kWaveformOversample *
|
||||
waveformColumnCount(KitBox{0, 0, kGrid.cellWidth, kGrid.cellHeight});
|
||||
for (const SlotCellRect& r : disp.slotRects) {
|
||||
|
||||
@@ -100,4 +100,9 @@ int hitTestListRow(int px, int py, const KitBox& list, int rowHeight, int rowCou
|
||||
return row;
|
||||
}
|
||||
|
||||
int waveformColumnCount(const KitBox& box) {
|
||||
const int w = box.width - 4; // fixed 2px inset each side (matches drawWaveform)
|
||||
return w > 0 ? w : 0;
|
||||
}
|
||||
|
||||
} // namespace reasampler
|
||||
|
||||
@@ -126,4 +126,14 @@ ListRowBox computeListRow(const KitBox& list, int index, int rowHeight);
|
||||
// a phantom row. Half-open bounds match computeListRow so the hit maps to the drawn row.
|
||||
int hitTestListRow(int px, int py, const KitBox& list, int rowHeight, int rowCount);
|
||||
|
||||
// --- Waveform column count ---------------------------------------------------
|
||||
//
|
||||
// The number of pixel columns drawWaveform renders inside `box` (its fixed 2px side
|
||||
// insets), never negative. Callers pass this count directly as the `binCount` argument to
|
||||
// peaks::computeEnvelope — one bin per column is the correct resolution, and
|
||||
// peaks::columnMinMax's exact partition makes the render gap-free at any bins-to-pixels
|
||||
// ratio. Overbinning does NOT improve render quality (columnMinMax's frame union is
|
||||
// identical whether bins == columns or bins == k*columns) and wastes memory and CPU.
|
||||
int waveformColumnCount(const KitBox& box);
|
||||
|
||||
} // namespace reasampler
|
||||
|
||||
+6
-10
@@ -277,11 +277,6 @@ void drawListRow(LICE_IBitmap* bmp, const ListRowBox& row, const char* label,
|
||||
}
|
||||
}
|
||||
|
||||
int waveformColumnCount(const KitBox& box) {
|
||||
const int w = box.width - 4; // fixed 2px inset each side (matches drawWaveform below)
|
||||
return w > 0 ? w : 0;
|
||||
}
|
||||
|
||||
void drawWaveform(LICE_IBitmap* bmp, const KitBox& box, const Envelope& env) {
|
||||
if (!bmp || box.empty()) return;
|
||||
|
||||
@@ -311,11 +306,12 @@ void drawWaveform(LICE_IBitmap* bmp, const KitBox& box, const Envelope& env) {
|
||||
if (bins.empty() || innerW <= 0) continue;
|
||||
|
||||
// Render one filled vertical span per pixel column. peaks::columnMinMax merges
|
||||
// all bins that project to column `col` under the same partition as
|
||||
// computeEnvelope, so every pixel column is covered with no gaps regardless of
|
||||
// the bins-to-pixels ratio — callers oversample (kWaveformOversample bins per
|
||||
// column) so each span shows true extremes. Same dB display compression
|
||||
// everywhere (bank_grid, pure).
|
||||
// all bins that project to column `col` under the exact same partition as
|
||||
// computeEnvelope used to build the envelope, so every pixel column is covered
|
||||
// with no gaps regardless of the bins-to-pixels ratio. With one bin per column
|
||||
// (kWaveformOversample == 1) each span covers the true min/max of exactly the
|
||||
// frames that fall in that column. Same dB display compression everywhere
|
||||
// (bank_grid, pure).
|
||||
for (int col = 0; col < innerW; ++col) {
|
||||
const MinMax mm = columnMinMax(bins, innerW, col);
|
||||
const int x = box.x + 2 + col;
|
||||
|
||||
+17
-18
@@ -115,29 +115,28 @@ void drawSlider(LICE_IBitmap* bmp, const SliderGeometry& geom, InteractionState
|
||||
void drawListRow(LICE_IBitmap* bmp, const ListRowBox& row, const char* label,
|
||||
int thumbWidth, InteractionState state);
|
||||
|
||||
// The number of pixel columns drawWaveform renders inside `box` (its fixed 2px side
|
||||
// insets), never negative. Callers size the envelope they compute from this: request
|
||||
// `kWaveformOversample * waveformColumnCount(box)` bins (clamped to the frame count) and
|
||||
// drawWaveform collapses them per column — the anti-aliasing lever. Requesting fewer bins
|
||||
// than columns still renders gap-free (the enclosing bin fills each column) but at the
|
||||
// envelope's coarser resolution.
|
||||
int waveformColumnCount(const KitBox& box);
|
||||
// waveformColumnCount — declared in component_geometry.h (already included above). Returns
|
||||
// the drawable column count inside `box` (box.width minus the fixed 2px insets each side).
|
||||
// Callers pass this value directly as the `binCount` argument to peaks::computeEnvelope;
|
||||
// overbinning (more bins than columns) costs memory and CPU without changing a rendered
|
||||
// pixel — peaks::columnMinMax's exact partition already makes the draw gap-free.
|
||||
|
||||
// The house oversampling factor for waveform envelopes: bins requested per drawn pixel
|
||||
// column. Each display column then shows the true min/max of ~4 bins (via
|
||||
// peaks::columnMinMax), so steep transients render as accurate full-height spans instead
|
||||
// of aliased single-bin dots.
|
||||
inline constexpr int kWaveformOversample = 4;
|
||||
// Multiplier kept at 1 (no oversampling). kWaveformOversample is present only so existing
|
||||
// call sites `kWaveformOversample * waveformColumnCount(box)` compile unchanged; a value of
|
||||
// 1 means they request exactly one bin per column, which is correct. The gap-free render
|
||||
// comes from peaks::columnMinMax's exact partition, NOT from extra bins.
|
||||
inline constexpr int kWaveformOversample = 1;
|
||||
|
||||
// A waveform envelope drawn as a min/max plot over the bg/panel surface: a midline per
|
||||
// channel and one accent vertical span PER PIXEL COLUMN, each column covering the true
|
||||
// extremes of every bin that projects to it (peaks::columnMinMax — gap-free at any
|
||||
// bins-to-pixels ratio). The ONE waveform shape in the system: the dock-panel thumbnail,
|
||||
// the browser cards, and the editor hero all render through this. `box` is the draw
|
||||
// region; `env` is the per-channel min/max envelope from peaks::computeEnvelope, ideally
|
||||
// oversampled (see waveformColumnCount / kWaveformOversample above). An empty env draws
|
||||
// just the midline. The caller fills the surface first (or passes a box already filled);
|
||||
// this draws only the wave + midline.
|
||||
// bins-to-pixels ratio because columnMinMax partitions bins exactly as computeEnvelope
|
||||
// does, so every pixel column is always covered). The ONE waveform shape in the system:
|
||||
// the dock-panel thumbnail, the browser cards, and the editor hero all render through
|
||||
// this. `box` is the draw region; `env` is the per-channel min/max envelope from
|
||||
// peaks::computeEnvelope, sized to waveformColumnCount(box) bins (clamped to frame count).
|
||||
// An empty env draws just the midline. The caller fills the surface first (or passes a
|
||||
// box already filled); this draws only the wave + midline.
|
||||
void drawWaveform(LICE_IBitmap* bmp, const KitBox& box, const Envelope& env);
|
||||
|
||||
} // namespace reasampler
|
||||
|
||||
@@ -121,9 +121,9 @@ std::string sampleLabel(const std::vector<SampleChoice>& samples, const std::str
|
||||
return "?";
|
||||
}
|
||||
|
||||
// The bin count a card's thumbnail is computed at: kWaveformOversample bins per drawn
|
||||
// thumbnail pixel column (FA3 anti-alias) — drawWaveform collapses them per column via
|
||||
// peaks::columnMinMax. thumbnailFor clamps the request to the decoded frame count.
|
||||
// The bin count a card's thumbnail is computed at: one bin per drawn pixel column — the
|
||||
// gap-free render comes from peaks::columnMinMax's exact partition, not from extra bins.
|
||||
// thumbnailFor clamps the request to the decoded frame count.
|
||||
int thumbBins(const BrowserLayout& layout) {
|
||||
return (std::max)(1, kWaveformOversample *
|
||||
waveformColumnCount(toKitBox(cardThumbnailRect(layout, 0))));
|
||||
@@ -1011,9 +1011,9 @@ void ReaSamplerEditor::paintSample(LICE_IBitmap* bmp, int w, int h) {
|
||||
const Rect waveArea = bands.hero;
|
||||
fillSurface(bmp, toKitBox(waveArea), Role::BgBase, InteractionState::Rest);
|
||||
if (frames > 0 && waveArea.width() > 0) {
|
||||
// FA3 anti-alias: request kWaveformOversample bins per drawn pixel column (clamped
|
||||
// to the frame count) — drawWaveform collapses them per column via
|
||||
// peaks::columnMinMax into a gap-free true min/max envelope.
|
||||
// FA3 gap-free: one bin per drawn pixel column (kWaveformOversample == 1, so this
|
||||
// multiplies by 1). The gap-free draw comes from peaks::columnMinMax's exact
|
||||
// partition — extra bins produce no visible change. Clamped to frame count below.
|
||||
const std::int64_t wantBins =
|
||||
static_cast<std::int64_t>((std::max)(1, waveformColumnCount(toKitBox(waveArea)))) *
|
||||
kWaveformOversample;
|
||||
|
||||
Reference in New Issue
Block a user