127 lines
5.2 KiB
C++
127 lines
5.2 KiB
C++
// editor_input_waveform.cpp — the WAVEFORM band's input: grabbing an envelope node or a
|
|
// start/loop marker, and resolving both drags live against the pure inverse maps
|
|
// (envelope_edit, waveform_view). Windows-only.
|
|
//
|
|
// Overlay contract: see waveform_view.h's WaveformSurface.
|
|
|
|
#include "shell/instrument/reasampler_editor.h"
|
|
|
|
#ifdef _WIN32
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
#include "core/instrument/ui/envelope_edit.h" // nodeAtPoint / resolveNodeDrag
|
|
#include "core/instrument/ui/waveform_view.h" // waveformOverlayArea / markerAtPoint / snap
|
|
#include "shell/instrument/editor_internal.h"
|
|
#include "shell/instrument/reasampler_processor.h"
|
|
|
|
namespace reasampler::vst {
|
|
|
|
using namespace reasampler::ui;
|
|
using namespace reasampler::instrument::ui;
|
|
|
|
bool ReaSamplerEditor::mouseDownWaveform(const FaceLayout& fl, int x, int y) {
|
|
const std::vector<AudioSample>& pcm = monoPcmFor(selectedId_);
|
|
const std::int64_t frames = static_cast<std::int64_t>(pcm.size());
|
|
if (frames <= 0) return false;
|
|
const OverlayArea overlay = waveformOverlayArea(fl.bands.waveform);
|
|
|
|
// Envelope nodes first (they sit on top of the markers), then the wave markers.
|
|
const double rate = liveSampleRate();
|
|
if (rate > 0.0) {
|
|
const std::int64_t startFrame = params_.startPoint.value_or(0);
|
|
const AmpEnvelope env = packEnvelope(params_.play, frames, startFrame);
|
|
const double totalSeconds = static_cast<double>(frames) / rate;
|
|
const NodeHit nh = nodeAtPoint(env, overlay, totalSeconds, x, y);
|
|
if (nh.hit) {
|
|
drag_ = DragKind::kEnvNode;
|
|
envNode_ = nh.node;
|
|
dragStartX_ = x;
|
|
dragStartY_ = y;
|
|
dragStartEnv_ = env;
|
|
dragSampleFrames_ = frames;
|
|
dragStartFrame_ = startFrame;
|
|
dragStartParams_ = params_;
|
|
return true; // node moves once the cursor drags
|
|
}
|
|
}
|
|
const SetupMarkers m = pickedMarkers(frames);
|
|
const std::int64_t markerFrames[3] = {m.start, m.loopStart, m.loopEnd};
|
|
const int hit = markerAtPoint(overlay, frames, markerFrames, 3, x, y);
|
|
if (hit >= 0) {
|
|
drag_ = DragKind::kWaveMarker;
|
|
waveMarker_ = static_cast<WaveMarker>(hit);
|
|
dragStartX_ = x;
|
|
dragStartMarkers_ = m;
|
|
dragSampleFrames_ = frames;
|
|
dragStartParams_ = params_;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ReaSamplerEditor::dragWaveform(const FaceLayout& fl, int x, int y) {
|
|
const OverlayArea overlay = waveformOverlayArea(fl.bands.waveform);
|
|
const int dx = x - dragStartX_;
|
|
|
|
if (drag_ == DragKind::kEnvNode) {
|
|
// Resolve the grabbed envelope node's new params from the pixel delta (through the
|
|
// pure envelope_edit inverse map, clamped + monotonic), then unpack them back onto
|
|
// the parameter set. The AmpEnvelope was snapshotted at grab (dragStartEnv_) so the
|
|
// delta is absolute.
|
|
const std::int64_t frames = dragSampleFrames_;
|
|
const double rate = liveSampleRate();
|
|
if (frames <= 0 || rate <= 0.0) return;
|
|
const double totalSeconds = static_cast<double>(frames) / rate;
|
|
const AmpEnvelope edited = resolveNodeDrag(dragStartEnv_, envNode_, overlay, totalSeconds,
|
|
envClampBounds(), dx, y - dragStartY_);
|
|
unpackEnvelope(edited, frames, dragStartFrame_, params_.play);
|
|
invalidate(); // live feedback; commit on WM_LBUTTONUP
|
|
return;
|
|
}
|
|
|
|
// kWaveMarker: resolve the grabbed marker's new frame from the pixel delta,
|
|
// zero-crossing-snap it against the decoded PCM, apply the inter-marker clamps, and write
|
|
// the override live.
|
|
const std::int64_t frames = dragSampleFrames_;
|
|
if (frames <= 0) return;
|
|
|
|
// Grabbed frame at grab time, from the snapshot (so the delta is measured from grab).
|
|
const int idx = static_cast<int>(waveMarker_);
|
|
const std::int64_t startVals[3] = {dragStartMarkers_.start, dragStartMarkers_.loopStart,
|
|
dragStartMarkers_.loopEnd};
|
|
std::int64_t newFrame = resolveDragFrame(overlay, frames, startVals[idx], dx);
|
|
|
|
// Snap to the nearest zero crossing in the decoded PCM. Pure over the cached mono
|
|
// frames — no host types, no file I/O.
|
|
const std::vector<AudioSample>& pcm = monoPcmFor(selectedId_);
|
|
if (!pcm.empty()) {
|
|
newFrame = nearestZeroCrossing(pcm.data(), static_cast<std::int64_t>(pcm.size()),
|
|
newFrame);
|
|
}
|
|
|
|
// Build the edited marker set from the snapshot, moving only the grabbed marker, then
|
|
// clamp: loopStart <= loopEnd, start in [0, frames-1]. Dragging a loop marker MAKES a loop.
|
|
SetupMarkers m = dragStartMarkers_;
|
|
if (waveMarker_ == WaveMarker::kStart) {
|
|
m.start = newFrame;
|
|
} else if (waveMarker_ == WaveMarker::kLoopStart) {
|
|
m.loopStart = (std::min)(newFrame, m.loopEnd);
|
|
m.hasLoop = true;
|
|
} else { // kLoopEnd
|
|
m.loopEnd = (std::max)(newFrame, m.loopStart);
|
|
m.hasLoop = true;
|
|
}
|
|
if (m.start < 0) m.start = 0;
|
|
if (m.start > frames - 1) m.start = frames - 1;
|
|
|
|
applyMarkers(m);
|
|
invalidate(); // live feedback; the commit lands on release
|
|
}
|
|
|
|
} // namespace reasampler::vst
|
|
|
|
#endif // _WIN32
|