Stack L/R waveform lanes in stereo mode, with overlays drawn once at full band height
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include "core/instrument/ui/waveform_view.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdlib> // std::abs (int overload)
|
||||
|
||||
namespace reasampler::instrument::ui {
|
||||
@@ -17,6 +18,25 @@ std::int64_t clampFrame(std::int64_t f, std::int64_t frameCount) {
|
||||
|
||||
} // namespace
|
||||
|
||||
Rect waveformOverlayArea(const Rect& band) { return band.empty() ? Rect{} : band; }
|
||||
|
||||
WaveformSurface waveformSurface(const Rect& band, bool stereoMode, int sourceChannels) {
|
||||
WaveformSurface s;
|
||||
if (band.empty()) return s;
|
||||
s.overlay = waveformOverlayArea(band);
|
||||
const bool twoLanes = stereoMode && sourceChannels >= 2;
|
||||
const WaveformLanes lanes = waveformLanes(band, twoLanes);
|
||||
s.upper = lanes.upper;
|
||||
s.lower = lanes.lower;
|
||||
s.laneCount = twoLanes ? 2 : 1;
|
||||
return s;
|
||||
}
|
||||
|
||||
audio::Envelope laneEnvelope(const audio::Envelope& env, int lane) {
|
||||
if (lane < 0 || static_cast<std::size_t>(lane) >= env.size()) return {};
|
||||
return audio::Envelope{env[static_cast<std::size_t>(lane)]};
|
||||
}
|
||||
|
||||
int frameToX(const Rect& area, std::int64_t frameCount, std::int64_t frame) {
|
||||
const int w = std::max(0, area.width);
|
||||
if (frameCount <= 0 || w <= 0) return area.x;
|
||||
|
||||
@@ -1,22 +1,55 @@
|
||||
// waveform_view.h — waveform/marker geometry + zero-crossing snap. Mirror of keyboard_strip/
|
||||
// editor_geometry: frame<->pixel + marker hit-test + snap arithmetic lives here, unit-tested
|
||||
// outside the DAW; the shell draws and marshals mouse events into it.
|
||||
// waveform_view.h — the WAVEFORM band's interior: the drawn lane/overlay surface, plus
|
||||
// frame<->pixel mapping, marker hit-test and zero-crossing snap. Unit-tested outside the
|
||||
// DAW; the shell draws and marshals mouse events into it.
|
||||
//
|
||||
// The surface maps a sample's full frame span [0, frameCount] linearly across a horizontal
|
||||
// waveform rect. Markers are a generic N-named-marker set (not hardcoded specials), so a
|
||||
// different mode (e.g. start + %-length end + fades) can repurpose the same machinery.
|
||||
// The band maps a sample's full frame span [0, frameCount] linearly across its width.
|
||||
// Markers are a generic N-named-marker set (not hardcoded specials), so a different mode
|
||||
// (e.g. start + %-length end + fades) can repurpose the same machinery.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "core/instrument/ui/editor_geometry.h" // Rect, contains
|
||||
#include "core/audio/peaks.h" // AudioSample (float)
|
||||
#include "core/instrument/ui/sample_bands.h" // waveformLanes (the band's lane inventory)
|
||||
#include "core/audio/peaks.h" // AudioSample (float), Envelope
|
||||
|
||||
namespace reasampler::instrument::ui {
|
||||
|
||||
using audio::AudioSample;
|
||||
|
||||
// What the waveform band actually draws: the channel lane(s), and THE rect every overlay
|
||||
// riding the waveform occupies.
|
||||
//
|
||||
// OVERLAY CONTRACT — `overlay` is the whole band in BOTH modes, never a lane. The amp
|
||||
// envelope trace and its node handles, the start/loop markers, and the loop region draw
|
||||
// ONCE into `overlay`, spanning both stacked lanes in stereo. Hit-testing reads the same
|
||||
// rect, so a grab in the lower lane resolves to the same overlay item as one in the upper.
|
||||
// Anything that draws per lane is a duplicate and a defect.
|
||||
struct WaveformSurface {
|
||||
Rect upper; // lane 0 -> channel 0 (LEFT); the whole band when single-lane
|
||||
Rect lower; // lane 1 -> channel 1 (RIGHT); empty() when single-lane
|
||||
Rect overlay; // the full band, both modes
|
||||
int laneCount = 0; // 0 on a degenerate band, else 1 or 2
|
||||
};
|
||||
|
||||
// Resolves the surface for a waveform band. Two lanes need BOTH stereo mode and a source
|
||||
// that has a second channel to show: a mono source under stereo mode is dual-mono, so a
|
||||
// second lane would be the redundant duplicate single-lane mode exists to avoid.
|
||||
WaveformSurface waveformSurface(const Rect& band, bool stereoMode, int sourceChannels);
|
||||
|
||||
// THE overlay rect, standalone — same value as WaveformSurface::overlay, for the hit-test
|
||||
// paths that have no channel count to hand. An overlay's rect never depends on the lane
|
||||
// split, which is exactly the contract.
|
||||
Rect waveformOverlayArea(const Rect& band);
|
||||
|
||||
// The single-channel envelope lane `lane` draws, taken from a multi-channel envelope
|
||||
// computed in ONE computeEnvelope pass (it already envelopes channels independently, so a
|
||||
// second lane costs no second scan of the PCM). Lane 0 is the upper lane and takes channel
|
||||
// 0, lane 1 the lower and channel 1 — the L-above-R order. An out-of-range lane yields an
|
||||
// empty envelope, which draws as a bare midline.
|
||||
audio::Envelope laneEnvelope(const audio::Envelope& env, int lane);
|
||||
|
||||
// Pixel width of a marker's grab region either side of its x line. Mirrors keyboard_strip's
|
||||
// edge-grab idiom.
|
||||
inline constexpr int kMarkerGrabWidth = 5;
|
||||
|
||||
Reference in New Issue
Block a user