49 if (!data || num_samples <= 0) {
53 viewport_.set_total_samples(0);
54 clamp_editor_state_to_audio_bounds();
58 audio_data_.assign(data, data + num_samples);
60 total_samples_ = num_samples;
61 viewport_.set_total_samples(num_samples);
62 viewport_.set_visible_range(0, num_samples);
63 clamp_editor_state_to_audio_bounds();
72 set_selection_impl(
start,
end,
true);
77 bool has_selection()
const {
return selection_start_ != selection_end_; }
88 viewport_.set_total_samples(total_samples_);
89 viewport_.set_visible_range(
start, length);
92 int visible_start()
const {
return static_cast<int>(viewport_.visible_start); }
93 int visible_length()
const {
return static_cast<int>(viewport_.visible_length); }
96 void zoom_in(
float factor = 2.0f) { viewport_.zoom_in(factor); }
99 void zoom_out(
float factor = 2.0f) { viewport_.zoom_out(factor); }
110 void scroll(
int delta_samples) { viewport_.scroll(delta_samples); }
114 void set_playhead(
int sample) { playhead_ = std::clamp(sample, 0, total_samples_); }
121 const std::vector<WaveformRegion>&
regions()
const {
return regions_; }
127 if (audio_data_.empty() ||
b.is_empty())
return;
133 canvas.fill_rect(
b.x,
b.y,
b.width,
b.height);
135 float center_y =
b.y +
b.height / 2;
136 float half_h =
b.height / 2 - 2;
139 for (
const auto& region : regions_) {
142 canvas.set_fill_color(region.color);
143 canvas.fill_rect(rx,
b.y, rw,
b.height);
152 canvas.fill_rect(sx,
b.y, sw,
b.height);
160 float samples_per_pixel =
static_cast<float>(viewport.samples_per_pixel());
162 int s0 =
static_cast<int>(viewport.visible_start +
static_cast<int64_t
>(
static_cast<float>(
px) * samples_per_pixel));
163 int s1 =
static_cast<int>(viewport.visible_start +
static_cast<int64_t
>(
static_cast<float>(
px + 1) * samples_per_pixel));
164 s0 = std::clamp(s0, 0, total_samples_ - 1);
165 s1 = std::clamp(s1, s0 + 1, total_samples_);
167 float min_val = 0, max_val = 0;
168 for (
int s = s0;
s < s1; ++
s) {
169 float v = audio_data_[
static_cast<size_t>(
s)];
170 min_val = std::min(min_val,
v);
171 max_val = std::max(max_val,
v);
174 float x =
b.x +
static_cast<float>(
px);
175 float y_top = center_y - max_val * half_h;
176 float y_bot = center_y - min_val * half_h;
177 canvas.stroke_line(
x, y_top,
x, y_bot);
183 canvas.stroke_line(
b.x, center_y,
b.x +
b.width, center_y);
190 canvas.set_line_width(1.5f);
203 const bool explicit_phase =
event.hasExplicitPhase();
204 const bool is_press = explicit_phase ?
event.isPress() : (!dragging_ &&
event.is_down);
205 const bool is_drag = explicit_phase ?
event.isDrag() : (dragging_ && !
event.is_down);
206 const bool is_release = explicit_phase ?
event.isRelease() : (dragging_ &&
event.is_down);
208 if (!is_press && !is_drag && !is_release)
return;
215 set_selection_impl(drag_anchor_, sample,
true);
217 }
else if (is_drag && dragging_) {
218 set_selection_impl(drag_anchor_, sample,
true);
219 }
else if (is_release && dragging_) {
220 set_selection_impl(drag_anchor_, sample,
true);
226 if (!event.
is_down)
return false;
244 std::vector<float> audio_data_;
245 float sample_rate_ = 44100.0f;
246 int total_samples_ = 0;
248 int selection_start_ = 0;
249 int selection_end_ = 0;
251 int drag_anchor_ = 0;
252 bool dragging_ =
false;
253 std::vector<WaveformRegion> regions_;
255 void set_selection_impl(
int start,
int end,
bool notify) {
257 end = std::clamp(
end, 0, total_samples_);
260 const bool changed =
start != selection_start_ ||
end != selection_end_;
261 selection_start_ =
start;
262 selection_end_ =
end;
268 void clamp_editor_state_to_audio_bounds() {
269 set_selection_impl(selection_start_, selection_end_,
false);
270 playhead_ = std::clamp(playhead_, 0, total_samples_);
271 drag_anchor_ = std::clamp(drag_anchor_, 0, total_samples_);
273 for (
auto& region : regions_) {
274 region.start_sample = std::clamp(region.start_sample, 0, total_samples_);
275 region.end_sample = std::clamp(region.end_sample, 0, total_samples_);
284 auto viewport = viewport_;
Definition canvas.hpp:240
Color resolve_color(const std::string &name, Color fallback={}) const
void set_focusable(bool f)
Definition view.hpp:669
Rect local_bounds() const
Definition view.hpp:88
Definition affine_transform.hpp:24
Hover-cursor tracking for platform window hosts.
Definition fwd.hpp:27
WaveformPlayheadOverlay build_waveform_playhead_overlay(const WaveformViewport &viewport, int64_t playhead_sample)
@ px
Absolute pixels.
Definition geometry.hpp:393
@ s
Definition input_events.hpp:41
@ a
Definition input_events.hpp:39
@ right
Definition input_events.hpp:53
@ left
Definition input_events.hpp:53
@ num0
Definition input_events.hpp:45
@ b
Definition input_events.hpp:39
@ x
Definition input_events.hpp:41
@ v
Definition input_events.hpp:41
@ end
Definition geometry.hpp:370
@ start
Definition geometry.hpp:370
@ changed
Definition gesture.hpp:17
static Color hex(uint32_t rgb)
Construct from hex 0xRRGGBB (alpha = 1.0).
Definition canvas.hpp:66
static constexpr Color rgba8(uint8_t r, uint8_t g, uint8_t b, uint8_t a=255)
Construct from 8-bit channels [0,255] — convenience for legacy/hex code.
Definition canvas.hpp:61
Rich key event with key code, modifiers, and repeat state.
Definition input_events.hpp:193
bool isMainModifier() const
Definition input_events.hpp:203
KeyCode key
Definition input_events.hpp:194
bool is_down
True for key-down, false for key-up.
Definition input_events.hpp:196
Rich mouse/pointer event with position, modifiers, button state, pointer ID, and stylus properties.
Definition input_events.hpp:104
bool is_cancelled
True for touchesCancelled (→ pointercancel).
Definition input_events.hpp:113
Point position
Position in local view coordinates.
Definition input_events.hpp:105
float x
Definition geometry.hpp:21
Axis-aligned rectangle in continuous (float) coordinates.
Definition geometry.hpp:52