1 /* -----------------------------------------------------------------------------
2 *
3 * Giada - Your Hardcore Loopmachine
4 *
5 * -----------------------------------------------------------------------------
6 *
7 * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
8 *
9 * This file is part of Giada - Your Hardcore Loopmachine.
10 *
11 * Giada - Your Hardcore Loopmachine is free software: you can
12 * redistribute it and/or modify it under the terms of the GNU General
13 * Public License as published by the Free Software Foundation, either
14 * version 3 of the License, or (at your option) any later version.
15 *
16 * Giada - Your Hardcore Loopmachine is distributed in the hope that it
17 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
18 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 * See the GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with Giada - Your Hardcore Loopmachine. If not, see
23 * <http://www.gnu.org/licenses/>.
24 *
25 * -------------------------------------------------------------------------- */
26
27
28 #include <cassert>
29 #include <cmath>
30 #include <FL/fl_draw.H>
31 #include <FL/Fl_Menu_Button.H>
32 #include "core/model/model.h"
33 #include "core/wave.h"
34 #include "core/conf.h"
35 #include "core/const.h"
36 #include "core/mixer.h"
37 #include "core/waveFx.h"
38 #include "glue/channel.h"
39 #include "glue/sampleEditor.h"
40 #include "utils/log.h"
41 #include "gui/dialogs/sampleEditor.h"
42 #include "gui/elems/basics/boxtypes.h"
43 #include "waveTools.h"
44 #include "waveform.h"
45
46
47 namespace giada {
48 namespace v
49 {
geWaveform(int x,int y,int w,int h)50 geWaveform::geWaveform(int x, int y, int w, int h)
51 : Fl_Widget (x, y, w, h, nullptr)
52 , m_selection {}
53 , m_data (nullptr)
54 , m_chanStart (0)
55 , m_chanStartLit(false)
56 , m_chanEnd (0)
57 , m_chanEndLit (false)
58 , m_pushed (false)
59 , m_dragged (false)
60 , m_resizedA (false)
61 , m_resizedB (false)
62 , m_ratio (0.0f)
63 {
64 m_waveform.size = w;
65
66 m_grid.snap = m::conf::conf.sampleEditorGridOn;
67 m_grid.level = m::conf::conf.sampleEditorGridVal;
68 }
69
70
71 /* -------------------------------------------------------------------------- */
72
73
clearData()74 void geWaveform::clearData()
75 {
76 m_waveform.sup.clear();
77 m_waveform.inf.clear();
78 m_waveform.size = 0;
79 m_grid.points.clear();
80 }
81
82
83 /* -------------------------------------------------------------------------- */
84
85
alloc(int datasize,bool force)86 int geWaveform::alloc(int datasize, bool force)
87 {
88 /* TODO - geWaveform needs better isolation from m::. Refactoring needed. */
89
90 m::model::WavesLock l(m::model::waves);
91 const m::Wave& wave = m::model::get(m::model::waves, m_data->waveId);
92
93 m_ratio = wave.getSize() / (float) datasize;
94
95 /* Limit 1:1 drawing (to avoid sub-frame drawing) by keeping m_ratio >= 1. */
96
97 if (m_ratio < 1) {
98 datasize = wave.getSize();
99 m_ratio = 1;
100 }
101
102 if (datasize == m_waveform.size && !force)
103 return 0;
104
105 clearData();
106
107 m_waveform.size = datasize;
108 m_waveform.sup.resize(m_waveform.size);
109 m_waveform.inf.resize(m_waveform.size);
110
111 u::log::print("[geWaveform::alloc] %d pixels, %f m_ratio\n", m_waveform.size, m_ratio);
112
113 int offset = h() / 2;
114 int zero = y() + offset; // center, zero amplitude (-inf dB)
115
116 /* Frid frequency: store a grid point every 'gridFreq' frame (if grid is
117 enabled). TODO - this will cause round off errors, since gridFreq is integer. */
118
119 int gridFreq = m_grid.level != 0 ? wave.getSize() / m_grid.level : 0;
120
121 /* Resampling the waveform, hardcore way. Many thanks to
122 http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html */
123
124 for (int i = 0; i < m_waveform.size; i++) {
125
126 /* Scan the original waveform in chunks [pc, pn]. */
127
128 int pc = i * m_ratio; // current point TODO - int until we switch to uint32_t for Wave size...
129 int pn = (i+1) * m_ratio; // next point TODO - int until we switch to uint32_t for Wave size...
130
131 float peaksup = 0.0f;
132 float peakinf = 0.0f;
133
134 for (int k = pc; k < pn; k++) { // TODO - int until we switch to uint32_t for Wave size...
135
136 if (k >= wave.getSize())
137 continue;
138
139 /* Compute average of stereo signal. */
140
141 float avg = 0.0f;
142 float* frame = wave.getFrame(k);
143 for (int j = 0; j < wave.getChannels(); j++)
144 avg += frame[j];
145 avg /= wave.getChannels();
146
147 /* Find peaks (greater and lower). */
148
149 if (avg > peaksup) peaksup = avg;
150 else if (avg <= peakinf) peakinf = avg;
151
152 /* Fill up grid vector. */
153
154 if (gridFreq != 0 && (int) k % gridFreq == 0 && k != 0)
155 m_grid.points.push_back(k);
156 }
157
158 m_waveform.sup[i] = zero - (peaksup * offset);
159 m_waveform.inf[i] = zero - (peakinf * offset);
160
161 // avoid window overflow
162
163 if (m_waveform.sup[i] < y()) m_waveform.sup[i] = y();
164 if (m_waveform.inf[i] > y()+h()-1) m_waveform.inf[i] = y()+h()-1;
165 }
166
167 recalcPoints();
168 return 1;
169 }
170
171
172 /* -------------------------------------------------------------------------- */
173
174
recalcPoints()175 void geWaveform::recalcPoints()
176 {
177 m_chanStart = m_data->begin;
178 m_chanEnd = m_data->end;
179 }
180
181
182 /* -------------------------------------------------------------------------- */
183
184
drawSelection()185 void geWaveform::drawSelection()
186 {
187 if (!isSelected())
188 return;
189
190 int a = frameToPixel(m_selection.a) + x();
191 int b = frameToPixel(m_selection.b) + x();
192
193 if (a < 0)
194 a = 0;
195 if (b >= w() + BORDER)
196 b = w() + BORDER;
197
198 if (a < b)
199 fl_rectf(a, y(), b-a, h(), G_COLOR_GREY_4);
200 else
201 fl_rectf(b, y(), a-b, h(), G_COLOR_GREY_4);
202 }
203
204
205 /* -------------------------------------------------------------------------- */
206
207
drawWaveform(int from,int to)208 void geWaveform::drawWaveform(int from, int to)
209 {
210 int zero = y() + (h() / 2); // zero amplitude (-inf dB)
211
212 fl_color(G_COLOR_BLACK);
213 for (int i=from; i<to; i++) {
214 if (i >= m_waveform.size)
215 break;
216 fl_line(i+x(), zero, i+x(), m_waveform.sup[i]);
217 fl_line(i+x(), zero, i+x(), m_waveform.inf[i]);
218 }
219 }
220
221
222 /* -------------------------------------------------------------------------- */
223
224
drawGrid(int from,int to)225 void geWaveform::drawGrid(int from, int to)
226 {
227 fl_color(G_COLOR_GREY_3);
228 fl_line_style(FL_DASH, 1, nullptr);
229
230 for (int pf : m_grid.points) {
231 int pp = frameToPixel(pf);
232 if (pp > from && pp < to)
233 fl_line(pp+x(), y(), pp+x(), y()+h());
234 }
235
236 fl_line_style(FL_SOLID, 0, nullptr);
237 }
238
239
240 /* -------------------------------------------------------------------------- */
241
242
drawStartEndPoints()243 void geWaveform::drawStartEndPoints()
244 {
245 /* print m_chanStart */
246
247 int lineX = frameToPixel(m_chanStart) + x();
248
249 if (m_chanStartLit) fl_color(G_COLOR_LIGHT_2);
250 else fl_color(G_COLOR_LIGHT_1);
251
252 /* vertical line */
253
254 fl_line(lineX, y()+1, lineX, y()+h()-2);
255
256 /* print flag and avoid overflow */
257
258 if (lineX+FLAG_WIDTH > w()+x()-2)
259 fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, w()-lineX+x()-1, FLAG_HEIGHT);
260 else
261 fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, FLAG_WIDTH, FLAG_HEIGHT);
262
263 /* print m_chanEnd */
264
265 lineX = frameToPixel(m_chanEnd) + x() - 1;
266 if (m_chanEndLit) fl_color(G_COLOR_LIGHT_2);
267 else fl_color(G_COLOR_LIGHT_1);
268
269 /* vertical line */
270
271 fl_line(lineX, y()+1, lineX, y()+h()-2);
272
273 if (lineX-FLAG_WIDTH < x())
274 fl_rectf(x()+1, y()+1, lineX-x(), FLAG_HEIGHT);
275 else
276 fl_rectf(lineX-FLAG_WIDTH, y()+1, FLAG_WIDTH, FLAG_HEIGHT);
277 }
278
279
280 /* -------------------------------------------------------------------------- */
281
282
drawPlayHead()283 void geWaveform::drawPlayHead()
284 {
285 int p = frameToPixel(m_data->a_getPreviewTracker()) + x();
286 fl_color(G_COLOR_LIGHT_2);
287 fl_line(p, y() + 1, p, y() + h() - 2);
288 }
289
290
291 /* -------------------------------------------------------------------------- */
292
293
draw()294 void geWaveform::draw()
295 {
296 assert(m_waveform.sup.size() > 0);
297 assert(m_waveform.inf.size() > 0);
298
299 fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_2); // blank canvas
300
301 /* Draw things from 'from' (offset driven by the scrollbar) to 'to' (width of
302 parent window). We don't draw the entire waveform, only the visibile part. */
303
304 int from = abs(x() - parent()->x());
305 int to = from + parent()->w();
306 if (x() + w() < parent()->w())
307 to = x() + w() - BORDER;
308
309 drawSelection();
310 drawWaveform(from, to);
311 drawGrid(from, to);
312 drawPlayHead();
313
314 fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4); // border box
315
316 drawStartEndPoints();
317 }
318
319
320 /* -------------------------------------------------------------------------- */
321
322
handle(int e)323 int geWaveform::handle(int e)
324 {
325 /* TODO - geWaveform needs better isolation from m::. Refactoring needed. */
326
327 m::model::WavesLock l(m::model::waves);
328 const m::Wave& wave = m::model::get(m::model::waves, m_data->waveId);
329
330 m_mouseX = pixelToFrame(Fl::event_x() - x());
331 m_mouseY = pixelToFrame(Fl::event_y() - y());
332
333 switch (e) {
334
335 case FL_KEYDOWN: {
336 if (Fl::event_key() == ' ')
337 static_cast<v::gdSampleEditor*>(window())->cb_togglePreview();
338 else
339 if (Fl::event_key() == FL_BackSpace)
340 c::sampleEditor::setPreviewTracker(m_data->begin);
341 return 1;
342 }
343
344 case FL_PUSH: {
345
346 if (Fl::event_clicks() > 0) {
347 selectAll();
348 return 1;
349 }
350
351 m_pushed = true;
352
353 if (!mouseOnEnd() && !mouseOnStart()) {
354 if (Fl::event_button3()) // let the parent (waveTools) handle this
355 return 0;
356 if (mouseOnSelectionA())
357 m_resizedA = true;
358 else
359 if(mouseOnSelectionB())
360 m_resizedB = true;
361 else {
362 m_dragged = true;
363 m_selection.a = m_mouseX;
364 m_selection.b = m_mouseX;
365 }
366 }
367 return 1;
368 }
369
370 case FL_RELEASE: {
371
372 c::sampleEditor::setPreviewTracker(m_mouseX);
373
374 /* If selection has been done (m_dragged or resized), make sure that point A
375 is always lower than B. */
376
377 if (m_dragged || m_resizedA || m_resizedB)
378 fixSelection();
379
380 /* Handle begin/end markers interaction. */
381
382 if (m_chanStartLit || m_chanEndLit)
383 c::sampleEditor::setBeginEnd(m_data->channelId, m_chanStart, m_chanEnd);
384
385 m_pushed = false;
386 m_dragged = false;
387 m_resizedA = false;
388 m_resizedB = false;
389
390 redraw();
391 return 1;
392 }
393
394 case FL_ENTER: { // enables FL_DRAG
395 return 1;
396 }
397
398 case FL_LEAVE: {
399 if (m_chanStartLit || m_chanEndLit) {
400 m_chanStartLit = false;
401 m_chanEndLit = false;
402 redraw();
403 }
404 return 1;
405 }
406
407 case FL_MOVE: {
408
409 if (mouseOnStart()) {
410 m_chanStartLit = true;
411 redraw();
412 }
413 else
414 if (m_chanStartLit) {
415 m_chanStartLit = false;
416 redraw();
417 }
418
419 if (mouseOnEnd()) {
420 m_chanEndLit = true;
421 redraw();
422 }
423 else
424 if (m_chanEndLit) {
425 m_chanEndLit = false;
426 redraw();
427 }
428
429 if (mouseOnSelectionA() && isSelected())
430 fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
431 else
432 if (mouseOnSelectionB() && isSelected())
433 fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
434 else
435 fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
436
437 return 1;
438 }
439
440 case FL_DRAG: {
441
442 /* here the mouse is on the m_chanStart tool */
443
444 if (m_chanStartLit && m_pushed) {
445 m_chanStart = snap(m_mouseX);
446
447 if (m_chanStart < 0)
448 m_chanStart = 0;
449 else
450 if (m_chanStart >= m_chanEnd)
451 m_chanStart = m_chanEnd - 2;
452
453 redraw();
454 }
455 else
456 if (m_chanEndLit && m_pushed) {
457
458 m_chanEnd = snap(m_mouseX);
459
460 if (m_chanEnd > wave.getSize())
461 m_chanEnd = wave.getSize();
462 else
463 if (m_chanEnd <= m_chanStart)
464 m_chanEnd = m_chanStart + 2;
465
466 redraw();
467 }
468
469 /* Here the mouse is on the waveform, i.e. a new selection has started. */
470
471 else
472 if (m_dragged) {
473 m_selection.b = snap(m_mouseX);
474 redraw();
475 }
476
477 /* here the mouse is on a selection boundary i.e. resize */
478
479 else
480 if (m_resizedA || m_resizedB) {
481 int pos = snap(m_mouseX);
482 m_resizedA ? m_selection.a = pos : m_selection.b = pos;
483 redraw();
484 }
485
486 return 1;
487 }
488
489 default:
490 return Fl_Widget::handle(e);
491 }
492 }
493
494
495 /* -------------------------------------------------------------------------- */
496
497
snap(int pos)498 int geWaveform::snap(int pos)
499 {
500 // TODO use math::quantize
501 if (!m_grid.snap)
502 return pos;
503 for (int pf : m_grid.points) {
504 if (pos >= pf - pixelToFrame(SNAPPING) &&
505 pos <= pf + pixelToFrame(SNAPPING))
506 {
507 return pf;
508 }
509 }
510 return pos;
511 }
512
513
514 /* -------------------------------------------------------------------------- */
515
516
mouseOnStart() const517 bool geWaveform::mouseOnStart() const
518 {
519 int mouseXp = frameToPixel(m_mouseX);
520 int mouseYp = frameToPixel(m_mouseY);
521 int chanStartP = frameToPixel(m_chanStart);
522 return mouseXp - (FLAG_WIDTH / 2) > chanStartP - BORDER &&
523 mouseXp - (FLAG_WIDTH / 2) <= chanStartP - BORDER + FLAG_WIDTH &&
524 mouseYp > h() - FLAG_HEIGHT;
525 }
526
527
528 /* -------------------------------------------------------------------------- */
529
530
mouseOnEnd() const531 bool geWaveform::mouseOnEnd() const
532 {
533 int mouseXp = frameToPixel(m_mouseX);
534 int mouseYp = frameToPixel(m_mouseY);
535 int chanEndP = frameToPixel(m_chanEnd);
536 return mouseXp - (FLAG_WIDTH / 2) >= chanEndP - BORDER - FLAG_WIDTH &&
537 mouseXp - (FLAG_WIDTH / 2) <= chanEndP - BORDER &&
538 mouseYp <= FLAG_HEIGHT + 1;
539 }
540
541
542 /* -------------------------------------------------------------------------- */
543
544
mouseOnSelectionA() const545 bool geWaveform::mouseOnSelectionA() const
546 {
547 int mouseXp = frameToPixel(m_mouseX);
548 int selAp = frameToPixel(m_selection.a);
549 return mouseXp >= selAp - (FLAG_WIDTH / 2) && mouseXp <= selAp + (FLAG_WIDTH / 2);
550 }
551
552
mouseOnSelectionB() const553 bool geWaveform::mouseOnSelectionB() const
554 {
555 int mouseXp = frameToPixel(m_mouseX);
556 int selBp = frameToPixel(m_selection.b);
557 return mouseXp >= selBp - (FLAG_WIDTH / 2) && mouseXp <= selBp + (FLAG_WIDTH / 2);
558 }
559
560
561 /* -------------------------------------------------------------------------- */
562
563
pixelToFrame(int p) const564 int geWaveform::pixelToFrame(int p) const
565 {
566 if (p <= 0)
567 return 0;
568 if (p > m_waveform.size)
569 return m_data->waveSize - 1;
570 return p * m_ratio;
571 }
572
573
574 /* -------------------------------------------------------------------------- */
575
576
frameToPixel(int p) const577 int geWaveform::frameToPixel(int p) const
578 {
579 return ceil(p / m_ratio);
580 }
581
582
583 /* -------------------------------------------------------------------------- */
584
585
fixSelection()586 void geWaveform::fixSelection()
587 {
588 if (m_selection.a > m_selection.b) // inverted m_selection
589 std::swap(m_selection.a, m_selection.b);
590
591 c::sampleEditor::setPreviewTracker(m_selection.a);
592 }
593
594
595 /* -------------------------------------------------------------------------- */
596
597
clearSelection()598 void geWaveform::clearSelection()
599 {
600 m_selection.a = 0;
601 m_selection.b = 0;
602 }
603
604
605 /* -------------------------------------------------------------------------- */
606
607
setZoom(Zoom z)608 void geWaveform::setZoom(Zoom z)
609 {
610 if (!alloc(z == Zoom::IN ? m_waveform.size * G_GUI_ZOOM_FACTOR : m_waveform.size / G_GUI_ZOOM_FACTOR))
611 return;
612
613 size(m_waveform.size, h());
614
615 /* Zoom to cursor. */
616
617 int newX = -frameToPixel(m_mouseX) + Fl::event_x();
618 if (newX > BORDER)
619 newX = BORDER;
620 position(newX, y());
621
622 /* Avoid overflow when zooming out with scrollbar like that:
623
624 |----------[scrollbar]|
625
626 Offset vs smaller:
627
628 |[wave------------| offset > 0 smaller = false
629 |[wave----] | offset < 0, smaller = true
630 |-------------] | offset < 0, smaller = false */
631
632 int parentW = parent()->w();
633 int thisW = x() + w() - BORDER; // visible width, not full width
634
635 if (thisW < parentW)
636 position(x() + parentW - thisW, y());
637 if (smaller())
638 stretchToWindow();
639
640 redraw();
641 }
642
643
644 /* -------------------------------------------------------------------------- */
645
646
stretchToWindow()647 void geWaveform::stretchToWindow()
648 {
649 int s = parent()->w();
650 alloc(s);
651 position(BORDER, y());
652 size(s, h());
653 }
654
655
656 /* -------------------------------------------------------------------------- */
657
658
rebuild(const c::sampleEditor::Data & d)659 void geWaveform::rebuild(const c::sampleEditor::Data& d)
660 {
661 m_data = &d;
662 clearSelection();
663 alloc(m_waveform.size, /*force=*/true);
664 redraw();
665 }
666
667
668 /* -------------------------------------------------------------------------- */
669
670
smaller() const671 bool geWaveform::smaller() const
672 {
673 return w() < parent()->w();
674 }
675
676
677 /* -------------------------------------------------------------------------- */
678
679
setGridLevel(int l)680 void geWaveform::setGridLevel(int l)
681 {
682 m_grid.points.clear();
683 m_grid.level = l;
684 alloc(m_waveform.size, true); // force alloc
685 redraw();
686 }
687
688
689 /* -------------------------------------------------------------------------- */
690
691
isSelected() const692 bool geWaveform::isSelected() const
693 {
694 return m_selection.a != m_selection.b;
695 }
696
697
698 /* -------------------------------------------------------------------------- */
699
700
setSnap(bool v)701 void geWaveform::setSnap(bool v) { m_grid.snap = v; }
getSnap() const702 bool geWaveform::getSnap() const { return m_grid.snap; }
getSize() const703 int geWaveform::getSize() const { return m_waveform.size; }
704
705
706 /* -------------------------------------------------------------------------- */
707
708
getSelectionA() const709 int geWaveform::getSelectionA() const { return m_selection.a; }
getSelectionB() const710 int geWaveform::getSelectionB() const { return m_selection.b; }
711
712
selectAll()713 void geWaveform::selectAll()
714 {
715 m_selection.a = 0;
716 m_selection.b = m_data->waveSize - 1;
717 redraw();
718 }
719 }} // giada::v::
720