// 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 #include #include #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& pcm = monoPcmFor(selectedId_); const std::int64_t frames = static_cast(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(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(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(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(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& pcm = monoPcmFor(selectedId_); if (!pcm.empty()) { newFrame = nearestZeroCrossing(pcm.data(), static_cast(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