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