1 /* B.Choppr
2 * Step Sequencer Effect Plugin
3 *
4 * Copyright (C) 2018, 2019 by Sven Jähnichen
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 #include "BChoppr_GUI.hpp"
22 #include "Ports.hpp"
23 #include "screen.h"
24 #include "BUtilities/stof.hpp"
25 #include "BUtilities/vsystem.hpp"
26
27
BChoppr_GUI(const char * bundle_path,const LV2_Feature * const * features,PuglNativeView parentWindow)28 BChoppr_GUI::BChoppr_GUI (const char *bundle_path, const LV2_Feature *const *features, PuglNativeView parentWindow) :
29 Window (760, 560, "B.Choppr", parentWindow, true, PUGL_MODULE, 0),
30 controller (NULL), write_function (NULL),
31
32 mContainer (0, 0, 760, 560, "main"),
33 rContainer (260, 80, 480, 350, "rcontainer"),
34 sContainer (3, 210, 474, 137, "scontainer"),
35 monitorSwitch (600, 15, 40, 16, "switch", 0.0),
36 monitorLabel (600, 35, 40, 20, "smlabel", BCHOPPR_LABEL_MONITOR),
37 bypassButton (662, 15, 16, 16, "redbutton"),
38 bypassLabel (650, 35, 40, 20, "smlabel", BCHOPPR_LABEL_BYPASS),
39 drywetDial (703, 5, 33, 40, "dial", 1.0, 0.0, 1.0, 0.0, "%1.2f"),
40 drywetLabel (690, 35, 60, 20, "smlabel", BCHOPPR_LABEL_DRY_WET),
41 helpButton (20, 80, 24, 24, "halobutton", BCHOPPR_LABEL_HELP),
42 ytButton (50, 80, 24, 24, "halobutton", BCHOPPR_LABEL_TUTORIAL),
43 monitorDisplay (3, 3, 474, 207, "mmonitor"),
44 blendControl (0, 0, 0, 0, "widget", 1, 1, 2, 1),
45 rectButton (40, 240, 60, 40, "abutton"),
46 sinButton (140, 240, 60, 40, "nbutton"),
47 stepshapeDisplay (30, 290, 180, 140, "smonitor"),
48 attackControl (40, 445, 50, 60, "dial", 0.2, 0.01, 1.0, 0.01, "%1.2f"),
49 attackLabel (20, 500, 90, 20, "label", BCHOPPR_LABEL_ATTACK),
50 releaseControl (150, 445, 50, 60, "dial", 0.2, 0.01, 1.0, -0.01, "%1.2f"),
51 releaseLabel (130, 500, 90, 20, "label", BCHOPPR_LABEL_DECAY),
52 sequencesperbarControl (260, 442, 120, 28, "slider", 1.0, 1.0, 8.0, 1.0, "%1.0f"),
53 sequencesperbarLabel (260, 470, 120, 20, "label", BCHOPPR_LABEL_SEQUENCES_PER_BAR),
54 ampSwingControl
55 (
56 400, 442, 110, 28, "slider", 1.0, 0.001, 1000.0, 0.0, "%4.1f",
57 [] (const double val, const double min, const double max)
58 {return (val >= 1.0 ? 1.0 - 0.5 / LIMIT (sqrt(val), sqrt(min), sqrt(max)) : 0.5 * LIMIT (sqrt(val), sqrt(min), sqrt(max)));},
59 [] (const double frac, const double min, const double max)
__anonbeee13fc0202(const double frac, const double min, const double max) 60 {return (frac >= 0.5 ? pow (0.5 / (1.0 - LIMIT (frac, 0, 1)), 2) : pow (2 * LIMIT (frac, 0, 1), 2));}
61 ),
62 ampSwingLabel (400, 470, 110, 20, "label", BCHOPPR_LABEL_AMP_SWING),
63 swingControl (525, 442, 110, 28, "slider", 1.0, 1.0 / 3.0, 3.0, 0.0),
64 swingLabel (525, 470, 110, 20, "label", BCHOPPR_LABEL_STEPS_SWING),
65 markersAutoButton (655, 450, 80, 20, "button", BCHOPPR_LABEL_AUTO),
66 markersAutoLabel (655, 470, 80, 20, "label", BCHOPPR_LABEL_MARKER),
67 nrStepsControl (260, 502, 480, 28, "slider", 1.0, 1.0, MAXSTEPS, 1.0, "%2.0f"),
68 nrStepsLabel (260, 530, 480, 20, "label", BCHOPPR_LABEL_NUMBER_OF_STEPS),
69 stepshapeLabel (33, 293, 120, 20, "llabel", BCHOPPR_LABEL_STEP_SHAPE),
70 sequencemonitorLabel (263, 83, 120, 20, "llabel", BCHOPPR_LABEL_SEQUENCE_MONITOR),
71 messageLabel (420, 83, 280, 20, "hilabel", ""),
72 markerListBox (12, -68, 86, 66, "listbox", BItems::ItemList ({BCHOPPR_LABEL_MARKER, BCHOPPR_LABEL_MANUAL})),
73 sharedDataSelection (28, 528, 194, 24, "widget", 0, 0, 4, 1),
74
75 surface (NULL), cr1 (NULL), cr2 (NULL), cr3 (NULL), cr4 (NULL), pat1 (NULL), pat2 (NULL), pat3 (NULL), pat4 (NULL), pat5 (NULL),
76 pluginPath (bundle_path ? std::string (bundle_path) : std::string ("")), sz (1.0), bgImageSurface (nullptr),
77 scale (DB_CO(0.0)),
78 map (NULL)
79
80 {
81 if (!init_mainMonitor () || !init_Stepshape ())
82 {
83 std::cerr << "BChoppr.lv2#GUI: Failed to init monitor." << std::endl;
84 destroy_mainMonitor ();
85 destroy_Stepshape ();
86 throw std::bad_alloc ();
87 }
88
89 //Initialialize and configure stepControllers
90 double sw = sContainer.getEffectiveWidth();
91 double sx = sContainer.getXOffset();
92 for (int i = 0; i < MAXSTEPS; ++i)
93 {
94 stepControl[i] = BWidgets::VSlider ((i + 0.5) * sw / MAXSTEPS + sx - 7, 60, 14, 80, "slider", 1.0, 0.0, 1.0, 0.01);
95 stepControl[i].setHardChangeable (false);
96 stepControl[i].setScrollable (true);
97 stepControl[i].applyTheme (theme, "slider");
98 sContainer.add (stepControl[i]);
99
100 stepControlLabel[i] = BWidgets::Label ((i + 0.5) * sw / MAXSTEPS + sx - 14, 40, 28, 20, "mlabel", "1.00");
101 stepControlLabel[i].applyTheme (theme, "mlabel");
102 stepControlLabel[i].setState (BColors::ACTIVE);
103 stepControlLabel[i].setEditable (true);
104 stepControlLabel[i].setCallbackFunction(BEvents::EventType::MESSAGE_EVENT, stepControlLabelMessageCallback);
105 sContainer.add (stepControlLabel[i]);
106 }
107
108 //Initialialize and configure markers
109 for (int i = 0; i < MAXSTEPS - 1; ++i)
110 {
111 markerWidgets[i] = Marker ((i + 1) * sw / MAXSTEPS + sx - 5, 10, 10, 16, "marker", (double(i) + 1.0) / MAXSTEPS, 0.0, 1.0, 0.0);
112 markerWidgets[i].setHasValue (false);
113 markerWidgets[i].setDraggable (true);
114 markerWidgets[i].setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BChoppr_GUI::markerClickedCallback);
115 markerWidgets[i].setCallbackFunction (BEvents::EventType::POINTER_DRAG_EVENT, BChoppr_GUI::markerDraggedCallback);
116 markerWidgets[i].applyTheme (theme, "slider");
117 sContainer.add (markerWidgets[i]);
118 }
119
120 for (int i = 0; i < 4; ++i) sharedDataButtons[i] = HaloToggleButton
121 (50 * i, 0, 44, 24, "halobutton", BCHOPPR_LABEL_SHARED_DATA " " + std::to_string (i + 1));
122
123 // Link controllers
124 controllers[Bypass - Controllers] = &bypassButton;
125 controllers[DryWet - Controllers] = &drywetDial;
126 controllers[Blend - Controllers] = &blendControl;
127 controllers[Attack - Controllers] = &attackControl;
128 controllers[Release - Controllers] = &releaseControl;
129 controllers[SequencesPerBar - Controllers] = &sequencesperbarControl;
130 controllers[AmpSwing - Controllers] = &SwingControl;
131 controllers[Swing - Controllers] = &swingControl;
132 controllers[NrSteps - Controllers] = &nrStepsControl;
133 for (int i = 0; i < MAXSTEPS - 1; ++i) controllers[StepPositions + i - Controllers] = &markerWidgets[i];
134 for (int i = 0; i < MAXSTEPS; ++i) controllers[StepLevels + i - Controllers] = &stepControl[i];
135
136 // Set callbacks
137 for (int i = 0; i < NrControllers; ++i) controllers[i]->setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BChoppr_GUI::valueChangedCallback);
138 monitorSwitch.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BChoppr_GUI::valueChangedCallback);
139 monitorDisplay.setCallbackFunction (BEvents::EventType::WHEEL_SCROLL_EVENT, BChoppr_GUI::monitorScrolledCallback);
140 monitorDisplay.setCallbackFunction (BEvents::EventType::POINTER_DRAG_EVENT, BChoppr_GUI::monitorDraggedCallback);
141 markerListBox.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BChoppr_GUI::listBoxChangedCallback);
142 markersAutoButton.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BChoppr_GUI::markersAutoClickedCallback);
143 rectButton.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BChoppr_GUI::buttonClickedCallback);
144 sinButton.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BChoppr_GUI::buttonClickedCallback);
145 helpButton.setCallbackFunction(BEvents::BUTTON_PRESS_EVENT, helpButtonClickedCallback);
146 ytButton.setCallbackFunction(BEvents::BUTTON_PRESS_EVENT, ytButtonClickedCallback);
147 for (HaloToggleButton& s: sharedDataButtons) s.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BChoppr_GUI::sharedDataClickedCallback);
148 sharedDataSelection.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BChoppr_GUI::valueChangedCallback);
149
150 // Configure widgets
151 bgImageSurface = cairo_image_surface_create_from_png ((pluginPath + BG_FILE).c_str());
152 widgetBg.loadFillFromCairoSurface (bgImageSurface);
153 drywetDial.setScrollable (true);
154 drywetDial.setHardChangeable (false);
155 attackControl.setScrollable (true);
156 attackControl.setHardChangeable (false);
157 releaseControl.setScrollable (true);
158 releaseControl.setHardChangeable (false);
159 sequencesperbarControl.setScrollable (true);
160 ampSwingControl.setHardChangeable (false);
161 swingControl.setHardChangeable (false);
162 nrStepsControl.setScrollable (true);
163 monitorDisplay.setScrollable (true);
164 monitorDisplay.setDraggable (true);
165 markerListBox.setStacking (BWidgets::STACKING_OVERSIZE);
166 applyTheme (theme);
167
168
169 setAutoMarkers ();
170 rearrange_controllers ();
171 redrawMainMonitor ();
172 redrawSContainer ();
173 redrawButtons ();
174
175 // Pack widgets
176 mContainer.add (rContainer);
177 rContainer.add (monitorDisplay);
178 rContainer.add (sContainer);
179 mContainer.add (blendControl);
180 mContainer.add (monitorSwitch);
181 mContainer.add (monitorLabel);
182 mContainer.add (bypassButton);
183 mContainer.add (bypassLabel);
184 mContainer.add (drywetDial);
185 mContainer.add (drywetLabel);
186 mContainer.add (helpButton);
187 mContainer.add (ytButton);
188 mContainer.add (rectButton);
189 mContainer.add (sinButton);
190 mContainer.add (stepshapeDisplay);
191 mContainer.add (attackControl);
192 mContainer.add (attackLabel);
193 mContainer.add (releaseControl);
194 mContainer.add (releaseLabel);
195 mContainer.add (sequencesperbarControl);
196 mContainer.add (sequencesperbarLabel);
197 mContainer.add (ampSwingControl);
198 mContainer.add (ampSwingLabel);
199 mContainer.add (swingControl);
200 mContainer.add (swingLabel);
201 mContainer.add (markersAutoButton);
202 mContainer.add (markersAutoLabel);
203 mContainer.add (nrStepsControl);
204 mContainer.add (nrStepsLabel);
205 mContainer.add (stepshapeLabel);
206 mContainer.add (sequencemonitorLabel);
207 mContainer.add (messageLabel);
208 for (HaloToggleButton& s : sharedDataButtons) sharedDataSelection.add (s);
209 mContainer.add (sharedDataSelection);
210 add (mContainer);
211
212 //Scan host features for URID map
213 LV2_URID_Map* m = NULL;
214 for (int i = 0; features[i]; ++i)
215 {
216 if (strcmp(features[i]->URI, LV2_URID__map) == 0) m = (LV2_URID_Map*) features[i]->data;
217 }
218 if (!m) throw std::invalid_argument ("Host does not support urid:map");
219
220 //Map URIS
221 map = m;
222 getURIs (map, &uris);
223
224 // Initialize forge
225 lv2_atom_forge_init (&forge,map);
226 }
227
~BChoppr_GUI()228 BChoppr_GUI::~BChoppr_GUI()
229 {
230 send_record_off ();
231 destroy_mainMonitor ();
232 destroy_Stepshape ();
233 }
234
portEvent(uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)235 void BChoppr_GUI::portEvent(uint32_t port_index, uint32_t buffer_size, uint32_t format, const void* buffer)
236 {
237 // Notify port
238 if ((format == uris.atom_eventTransfer) && (port_index == Notify))
239 {
240 const LV2_Atom* atom = (const LV2_Atom*) buffer;
241 if ((atom->type == uris.atom_Blank) || (atom->type == uris.atom_Object))
242 {
243 const LV2_Atom_Object* obj = (const LV2_Atom_Object*) atom;
244
245 // Linked / unlinked to shared data
246 if (obj->body.otype == uris.notify_sharedDataLinkEvent)
247 {
248 LV2_Atom *oNr = NULL;
249
250 lv2_atom_object_get
251 (
252 obj,
253 uris.notify_sharedDataNr, &oNr,
254 NULL
255 );
256
257 if (oNr && (oNr->type == uris.atom_Int))
258 {
259 const int nr = ((LV2_Atom_Int*)oNr)->body;
260 if ((nr >= 0) && (nr <= 4) && (nr != sharedDataSelection.getValue()))
261 {
262 sharedDataSelection.setValueable (false);
263 sharedDataSelection.setValue (nr);
264 sharedDataSelection.setValueable (true);
265
266 for (int i = 0; i < 4; ++i)
267 {
268 sharedDataButtons[i].setValueable (false);
269 sharedDataButtons[i].setValue (i == nr - 1 ? 1 : 0);
270 sharedDataButtons[i].setValueable (true);
271 }
272
273 }
274 }
275 }
276
277 // Controller changed
278 else if (obj->body.otype == uris.notify_controllerEvent)
279 {
280 LV2_Atom *oNr = NULL, *oVal = NULL;
281
282 lv2_atom_object_get
283 (
284 obj,
285 uris.notify_controllerNr, &oNr,
286 uris.notify_controllerValue, &oVal,
287 NULL
288 );
289
290 if (oNr && (oNr->type == uris.atom_Int) && oVal && (oVal->type == uris.atom_Float))
291 {
292 const int nr = ((LV2_Atom_Int*)oNr)->body;
293 const float val = ((LV2_Atom_Float*)oVal)->body;
294
295 if ((nr >= StepPositions - Controllers) && (nr < StepPositions - Controllers + MAXSTEPS - 1))
296 {
297 setMarker (nr - (StepPositions - Controllers), val);
298 setAutoMarkers ();
299 rearrange_controllers ();
300 redrawSContainer ();
301 redrawMainMonitor ();
302 }
303
304 else setController (nr, val);
305 }
306 }
307
308 // Monitor notification
309 else if (obj->body.otype == uris.notify_event)
310 {
311 const LV2_Atom* data = NULL;
312 lv2_atom_object_get(obj, uris.notify_key, &data, 0);
313 if (data && (data->type == uris.atom_Vector))
314 {
315 const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) data;
316 if (vec->body.child_type == uris.atom_Float)
317 {
318 uint32_t notificationsCount = (uint32_t) ((data->size - sizeof(LV2_Atom_Vector_Body)) / sizeof (BChopprNotifications));
319 BChopprNotifications* notifications = (BChopprNotifications*) (&vec->body + 1);
320 if (notificationsCount > 0)
321 {
322 add_monitor_data (notifications, notificationsCount, mainMonitor.horizonPos);
323 redrawMainMonitor ();
324 }
325 }
326 }
327 else std::cerr << "BChoppr.lv2#GUI: Corrupt audio message." << std::endl;
328 }
329
330 // Message notification
331 else if (obj->body.otype == uris.notify_messageEvent)
332 {
333 const LV2_Atom* data = NULL;
334 lv2_atom_object_get(obj, uris.notify_message, &data, 0);
335 if (data && (data->type == uris.atom_Int))
336 {
337 const int messageNr = ((LV2_Atom_Int*)data)->body;
338 std::string msg = ((messageNr >= NO_MSG) && (messageNr <= MAX_MSG) ? messageStrings[messageNr] : "");
339 messageLabel.setText (msg);
340 }
341 }
342 }
343 }
344
345 // Scan remaining ports
346 else if ((format == 0) && (port_index >= Controllers) && (port_index < Controllers + NrControllers) && (sharedDataSelection.getValue() == 0))
347 {
348 int nr = port_index - Controllers;
349 float val = *(float*) buffer;
350 if ((nr >= StepPositions - Controllers) && (nr < StepPositions - Controllers + MAXSTEPS - 1))
351 {
352 setMarker (nr - (StepPositions - Controllers), val);
353 setAutoMarkers ();
354 rearrange_controllers ();
355 redrawSContainer ();
356 redrawMainMonitor();
357 }
358
359 else setController (nr, val);
360 }
361 }
362
resizeGUI()363 void BChoppr_GUI::resizeGUI()
364 {
365 hide ();
366
367 // Resize Fonts
368 defaultFont.setFontSize (12 * sz);
369 leftFont.setFontSize (12 * sz);
370 mdFont.setFontSize (10 * sz);
371 smFont.setFontSize (8 * sz);
372
373 // Resize Background
374 cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 760 * sz, 560 * sz);
375 cairo_t* cr = cairo_create (surface);
376 cairo_scale (cr, sz, sz);
377 cairo_set_source_surface(cr, bgImageSurface, 0, 0);
378 cairo_paint(cr);
379 widgetBg.loadFillFromCairoSurface(surface);
380 cairo_destroy (cr);
381 cairo_surface_destroy (surface);
382
383 // Resize widgets
384 RESIZE (mContainer, 0, 0, 760, 560, sz);
385 RESIZE (rContainer, 260, 80, 480, 350, sz);
386 RESIZE (monitorSwitch, 600, 15, 40, 16, sz);
387 RESIZE (monitorLabel, 600, 35, 40, 20, sz);
388 RESIZE (bypassButton, 662, 15, 16, 16, sz);
389 RESIZE (bypassLabel, 650, 35, 40, 20, sz);
390 RESIZE (drywetDial, 703, 5, 33, 40, sz);
391 RESIZE (drywetLabel, 690, 35, 60, 20, sz);
392 RESIZE (helpButton, 20, 80, 24, 24, sz);
393 RESIZE (ytButton, 50, 80, 24, 24, sz);
394 RESIZE (monitorDisplay, 3, 3, 474, 207, sz);
395 RESIZE (blendControl, 0, 0, 0, 0, sz);
396 RESIZE (rectButton, 40, 240, 60, 40, sz);
397 RESIZE (sinButton, 140, 240, 60, 40, sz);
398 RESIZE (stepshapeDisplay, 30, 290, 180, 140, sz);
399 RESIZE (attackControl, 40, 445, 50, 60, sz);
400 RESIZE (attackLabel, 20, 500, 90, 20, sz);
401 RESIZE (releaseControl, 150, 445, 50, 60, sz);
402 RESIZE (releaseLabel, 130, 500, 90, 20, sz);
403 RESIZE (sequencesperbarControl, 260, 442, 120, 28, sz);
404 RESIZE (sequencesperbarLabel, 260, 470, 120, 20, sz);
405 RESIZE (ampSwingControl, 400, 442, 110, 28, sz);
406 RESIZE (ampSwingLabel, 400, 470, 110, 20, sz);
407 RESIZE (swingControl, 525, 442, 110, 28, sz);
408 RESIZE (swingLabel, 525, 470, 110, 20, sz);
409 RESIZE (markersAutoButton, 655, 450, 80, 20, sz);
410 RESIZE (markersAutoLabel, 655, 470, 80, 20, sz);
411 RESIZE (nrStepsControl, 260, 502, 480, 28, sz);
412 RESIZE (nrStepsLabel, 260, 530, 480, 20, sz);
413 RESIZE (stepshapeLabel, 33, 293, 120, 20, sz);
414 RESIZE (sequencemonitorLabel, 263, 83, 120, 20, sz);
415 RESIZE (messageLabel, 420, 83, 280, 20,sz);
416 RESIZE (sContainer, 3, 210, 474, 137, sz);
417 RESIZE (markerListBox, 12, -68, 86, 66, sz);
418 markerListBox.resizeItems (BUtilities::Point (80 * sz, 20 * sz));
419 RESIZE (sharedDataSelection, 28, 528, 194, 24, sz);
420 for (int i = 0; i < 4; ++i) {RESIZE (sharedDataButtons[i], 50 * i, 0, 44, 24, sz);}
421
422 // Update monitors
423 destroy_Stepshape ();
424 init_Stepshape ();
425 redrawStepshape ();
426 destroy_mainMonitor ();
427 init_mainMonitor ();
428 redrawMainMonitor ();
429 redrawSContainer ();
430 rearrange_controllers ();
431 redrawButtons ();
432
433 // Apply changes
434 applyTheme (theme);
435 show ();
436 }
437
applyTheme(BStyles::Theme & theme)438 void BChoppr_GUI::applyTheme (BStyles::Theme& theme)
439 {
440 mContainer.applyTheme (theme);
441 rContainer.applyTheme (theme);
442 monitorSwitch.applyTheme (theme);
443 monitorLabel.applyTheme (theme);
444 bypassButton.applyTheme (theme);
445 bypassLabel.applyTheme (theme);
446 drywetDial.applyTheme (theme);
447 drywetLabel.applyTheme (theme);
448 helpButton.applyTheme (theme);
449 ytButton.applyTheme (theme);
450 monitorDisplay.applyTheme (theme);
451 blendControl.applyTheme (theme);
452 rectButton.applyTheme (theme);
453 sinButton.applyTheme (theme);
454 stepshapeDisplay.applyTheme (theme);
455 attackControl.applyTheme (theme);
456 attackLabel.applyTheme (theme);
457 releaseControl.applyTheme (theme);
458 releaseLabel.applyTheme (theme);
459 sequencesperbarControl.applyTheme (theme);
460 sequencesperbarLabel.applyTheme (theme);
461 ampSwingControl.applyTheme (theme);
462 ampSwingLabel.applyTheme (theme);
463 swingControl.applyTheme (theme);
464 swingLabel.applyTheme (theme);
465 markersAutoButton.applyTheme (theme);
466 markersAutoLabel.applyTheme (theme);
467 nrStepsControl.applyTheme (theme);
468 nrStepsLabel.applyTheme (theme);
469 stepshapeLabel.applyTheme (theme);
470 sequencemonitorLabel.applyTheme (theme);
471 messageLabel.applyTheme (theme);
472 sContainer.applyTheme (theme);
473 markerListBox.applyTheme (theme);
474 for (int i = 0; i < MAXSTEPS; ++i)
475 {
476 stepControl[i].applyTheme (theme);
477 stepControlLabel[i].applyTheme (theme);
478 }
479 sharedDataSelection.applyTheme (theme);
480 for (HaloToggleButton& s : sharedDataButtons) s.applyTheme (theme);
481 }
482
onConfigureRequest(BEvents::ExposeEvent * event)483 void BChoppr_GUI::onConfigureRequest (BEvents::ExposeEvent* event)
484 {
485 Window::onConfigureRequest (event);
486
487 sz = (getWidth() / 760 > getHeight() / 560 ? getHeight() / 560 : getWidth() / 760);
488 resizeGUI ();
489 }
490
send_record_on()491 void BChoppr_GUI::send_record_on ()
492 {
493 uint8_t obj_buf[64];
494 lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
495
496 LV2_Atom_Forge_Frame frame;
497 LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, uris.ui_on);
498 lv2_atom_forge_pop(&forge, &frame);
499 write_function(controller, Control_2, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
500 monitorSwitch.setValue (1.0);
501 }
502
send_record_off()503 void BChoppr_GUI::send_record_off ()
504 {
505 uint8_t obj_buf[64];
506 lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
507
508 LV2_Atom_Forge_Frame frame;
509 LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, uris.ui_off);
510 lv2_atom_forge_pop(&forge, &frame);
511 write_function(controller, Control_2, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
512 monitorSwitch.setValue (0.0);
513 }
514
sendSharedDataNr()515 void BChoppr_GUI::sendSharedDataNr ()
516 {
517 uint8_t obj_buf[64];
518 lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
519
520 LV2_Atom_Forge_Frame frame;
521 LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, uris.notify_sharedDataLinkEvent);
522 lv2_atom_forge_key (&forge, uris.notify_sharedDataNr);
523 lv2_atom_forge_int (&forge, sharedDataSelection.getValue());
524 lv2_atom_forge_pop(&forge, &frame);
525 write_function(controller, Control_2, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
526 }
527
sendController(const int nr,const float value)528 void BChoppr_GUI::sendController (const int nr, const float value)
529 {
530 uint8_t obj_buf[64];
531 lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
532
533 LV2_Atom_Forge_Frame frame;
534 LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, uris.notify_controllerEvent);
535 lv2_atom_forge_key (&forge, uris.notify_controllerNr);
536 lv2_atom_forge_int (&forge, nr);
537 lv2_atom_forge_key (&forge, uris.notify_controllerValue);
538 lv2_atom_forge_float (&forge, value);
539 lv2_atom_forge_pop(&forge, &frame);
540 write_function(controller, Control_2, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
541 }
542
setController(const int nr,const double value)543 float BChoppr_GUI::setController (const int nr, const double value)
544 {
545 controllers[nr]->setValueable (false);
546 controllers[nr]->setValue (value);
547 controllers[nr]->setValueable (true);
548
549 if (nr == Blend - Controllers)
550 {
551 if (value == 1) {rectButton.rename ("abutton"); sinButton.rename ("nbutton");}
552 else if (value == 2) {sinButton.rename ("abutton"); rectButton.rename ("nbutton");}
553 rectButton.applyTheme (theme);
554 sinButton.applyTheme (theme);
555 redrawButtons ();
556 redrawStepshape ();
557 }
558
559 else if ((nr == Attack - Controllers) || (nr == Release - Controllers)) redrawStepshape ();
560
561 else if (nr == AmpSwing - Controllers) rearrange_controllers();
562
563 else if (nr == Swing - Controllers)
564 {
565 setAutoMarkers();
566 rearrange_controllers();
567 redrawSContainer();
568 redrawMainMonitor();
569 }
570
571 else if (nr == NrSteps - Controllers)
572 {
573 setAutoMarkers();
574 rearrange_controllers();
575 redrawSContainer();
576 redrawMainMonitor();
577 }
578
579 else if ((nr >= StepPositions - Controllers) and (nr < StepPositions - Controllers + MAXSTEPS - 1))
580 {
581 return (((Marker*)controllers[nr])->hasValue() ? value : 0.0f);
582 }
583
584 else if ((nr >= StepLevels - Controllers) and (nr < StepLevels - Controllers + MAXSTEPS))
585 {
586 stepControlLabel[nr - (StepLevels - Controllers)].setText (BUtilities::to_string (value, "%1.2f"));
587 }
588
589 return value;
590 }
591
setMarker(const int markerNr,double value)592 void BChoppr_GUI::setMarker (const int markerNr, double value)
593 {
594 if ((markerNr < 0) || (markerNr >= MAXSTEPS - 1)) return;
595
596 // Value 0.0: Automatic
597 if (value == 0.0)
598 {
599 markerWidgets[markerNr].setHasValue (false);
600 }
601
602 else
603 {
604 // Set value and switch off automatic
605 value = LIMIT (value, MINMARKERVALUE, 1.0);
606 markerWidgets[markerNr].setHasValue (true);
607 markerWidgets[markerNr].setValue (value);
608
609 // Validate ancessors
610 for (int i = markerNr - 1; i >= 0; --i)
611 {
612 if (markerWidgets[i].hasValue())
613 {
614 if (markerWidgets[i].getValue() > value) markerWidgets[i].setValue (value);
615 else break;
616 }
617 }
618
619 // Validate successors
620 for (int i = markerNr + 1; i < MAXSTEPS - 1; ++i)
621 {
622 if (markerWidgets[i].hasValue())
623 {
624 if (markerWidgets[i].getValue() < value) markerWidgets[i].setValue (value);
625 else break;
626 }
627 }
628 }
629 }
630
setAutoMarkers()631 void BChoppr_GUI::setAutoMarkers ()
632 {
633 int nrMarkers = nrStepsControl.getValue() - 1;
634 int start = 0;
635 for (int i = 0; i < nrMarkers; ++i)
636 {
637 if (!markerWidgets[i].hasValue())
638 {
639 if ((i == nrMarkers - 1) || (markerWidgets[i + 1].hasValue()))
640 {
641 double swing = 2.0 * swingControl.getValue() / (swingControl.getValue() + 1.0);
642 double anc = (start == 0 ? 0 : markerWidgets[start - 1].getValue());
643 double suc = (i == nrMarkers - 1 ? 1 : markerWidgets[i + 1].getValue());
644 double diff = suc - anc;
645 double dist = i - start + 1.0 + (int (i - start) & 1 ? ((start & 1) ? 2.0 - swing : swing) : 1.0);
646 double step = (diff < 0 ? 0 : diff / dist);
647 for (int j = start; j <= i; ++j)
648 {
649 double f = ((j & 1) ? 2.0 - swing : swing);
650 anc += f * step;
651 markerWidgets[j].setValue (anc);
652 }
653 }
654 }
655 else start = i + 1;
656 }
657 }
658
rearrange_controllers()659 void BChoppr_GUI::rearrange_controllers ()
660 {
661 int nrStepsi = INT (nrStepsControl.getValue());
662
663 if ((nrStepsi < 1) || (nrStepsi > MAXSTEPS)) return;
664
665 double sw = sContainer.getEffectiveWidth();
666 double sx = sContainer.getXOffset();
667 const double oddf = (ampSwingControl.getValue() >= 1.0 ? 1.0 : ampSwingControl.getValue());
668 const double evenf = (ampSwingControl.getValue() >= 1.0 ? 1.0 / ampSwingControl.getValue() : 1.0);
669
670 for (int i = 0; i < MAXSTEPS; ++i)
671 {
672 if (i < nrStepsi)
673 {
674 stepControl[i].resize (14 * sz, (14 + LIMIT (66 * ((i % 2) == 0 ? oddf : evenf), 0, 66 )) * sz);
675 stepControl[i].moveTo ((i + 0.5) * sw / nrStepsi + sx - 7 * sz, 140 * sz - stepControl[i].getHeight());
676 stepControl[i].show();
677
678 if (i < nrStepsi - 1) markerWidgets[i].resize (10 * sz, 16 * sz);
679
680 stepControlLabel[i].moveTo ((i + 0.5) * sw / nrStepsi + sx - 14 * sz, 40 * sz);
681 stepControlLabel[i].resize (28 * sz, 20 * sz);
682 stepControlLabel[i].show();
683
684 }
685 else
686 {
687 stepControl[i].hide ();
688 stepControlLabel[i].hide();
689 }
690 }
691
692 for (int i = 0; i < MAXSTEPS - 1; ++i)
693 {
694 if (i < nrStepsi - 1)
695 {
696 markerWidgets[i].moveTo (markerWidgets[i].getValue() * sw + sx - 5 * sz, 10 * sz);
697 markerWidgets[i].show ();
698 }
699 else markerWidgets[i].hide ();
700 }
701 }
702
valueChangedCallback(BEvents::Event * event)703 void BChoppr_GUI::valueChangedCallback (BEvents::Event* event)
704 {
705 if ((event) && (event->getWidget ()))
706 {
707 BWidgets::ValueWidget* widget = (BWidgets::ValueWidget*) event->getWidget ();
708 const double value = widget->getValue();
709
710 if (widget->getMainWindow ())
711 {
712 BChoppr_GUI* ui = (BChoppr_GUI*) widget->getMainWindow ();
713
714 // Get controller nr
715 int controllerNr = -1;
716 for (int i = 0; i < NrControllers; ++i)
717 {
718 if (widget == ui->controllers[i])
719 {
720 controllerNr = i;
721 break;
722 }
723 }
724
725 if (controllerNr >= 0)
726 {
727 const float v = ui->setController (controllerNr, value);
728 if (ui->sharedDataSelection.getValue()) ui->sendController (controllerNr, v);
729 else ui->write_function (ui->controller, Controllers + controllerNr, sizeof (float), 0, &v);
730 }
731
732 else if (widget == &ui->sharedDataSelection)
733 {
734 const int val = ui->sharedDataSelection.getValue() - 1;
735 for (int i = 0; i < 4; ++i)
736 {
737 ui->sharedDataButtons[i].setValueable (false);
738 ui->sharedDataButtons[i].setValue (i == val ? 1 : 0);
739 ui->sharedDataButtons[i].setValueable (true);
740 }
741
742 ui->sendSharedDataNr();
743 }
744
745 // monitor on/off changed
746 else if (widget == &ui->monitorSwitch)
747 {
748 int value = INT (widget->getValue ());
749 if (value == 1)
750 {
751 ui->mainMonitor.record_on = true;
752 ui->send_record_on ();
753 }
754 else
755 {
756 ui->mainMonitor.record_on = false;
757 ui->send_record_off ();
758 }
759 return;
760 }
761 }
762 }
763 }
764
markerClickedCallback(BEvents::Event * event)765 void BChoppr_GUI::markerClickedCallback (BEvents::Event* event)
766 {
767 if (!event) return;
768 BEvents::PointerEvent* pev = (BEvents::PointerEvent*) event;
769 if (pev->getButton() != BDevices::RIGHT_BUTTON) return;
770 Marker* marker = (Marker*)event->getWidget();
771 if (!marker) return;
772 marker->raiseToTop();
773 BChoppr_GUI* ui = (BChoppr_GUI*)marker->getMainWindow();
774 if (!ui) return;
775
776 const int nrSteps = ui->nrStepsControl.getValue();
777
778 for (int i = 0; i < nrSteps - 1; ++i)
779 {
780 if (marker == &ui->markerWidgets[i])
781 {
782 Marker* oldMarker = (Marker*) ui->markerListBox.getParent();
783 ui->markerListBox.setValue (UNSELECTED);
784
785 if (oldMarker && (oldMarker == marker))
786 {
787 if (ui->markerListBox.isVisible()) ui->markerListBox.hide();
788 else ui->markerListBox.show ();
789 }
790
791 else if (oldMarker && (oldMarker != marker))
792 {
793 oldMarker->release (&ui->markerListBox);
794 marker->add (ui->markerListBox);
795 ui->markerListBox.show();
796 }
797
798 else
799 {
800 marker->add (ui->markerListBox);
801 ui->markerListBox.show();
802 }
803
804 }
805 }
806 }
807
markerDraggedCallback(BEvents::Event * event)808 void BChoppr_GUI::markerDraggedCallback (BEvents::Event* event)
809 {
810 if (!event) return;
811 BEvents::PointerEvent* pev = (BEvents::PointerEvent*) event;
812 if (pev->getButton() != BDevices::LEFT_BUTTON) return;
813 Marker* marker = (Marker*)event->getWidget();
814 if (!marker) return;
815 marker->raiseToTop();
816 BChoppr_GUI* ui = (BChoppr_GUI*)marker->getMainWindow();
817 if (!ui) return;
818
819 const int nrSteps = ui->nrStepsControl.getValue();
820
821 for (int i = 0; i < nrSteps - 1; ++i)
822 {
823 if (marker == &ui->markerWidgets[i])
824 {
825 double x0 = ui->sContainer.getXOffset();
826 double w = ui->sContainer. getEffectiveWidth();
827 double frac = (w > 0 ? (pev->getPosition().x + marker->getPosition().x - x0) / w : MINMARKERVALUE);
828 frac = LIMIT (frac, MINMARKERVALUE, 1.0);
829
830 // Limit to antecessors value
831 for (int j = i - 1; j >= 0; --j)
832 {
833 if (ui->markerWidgets[j].hasValue())
834 {
835 if (frac < ui->markerWidgets[j].getValue()) frac = ui->markerWidgets[j].getValue();
836 break;
837 }
838 }
839
840 // Limit to successors value
841 for (int j = i + 1; j < nrSteps - 1; ++j)
842 {
843 if (ui->markerWidgets[j].hasValue())
844 {
845 if (frac > ui->markerWidgets[j].getValue()) frac = ui->markerWidgets[j].getValue();
846 break;
847 }
848 }
849
850 ui->setMarker (i, frac);
851 ui->setAutoMarkers();
852 ui->rearrange_controllers();
853 ui->redrawSContainer();
854 ui->redrawMainMonitor();
855 break;
856 }
857 }
858 }
859
monitorScrolledCallback(BEvents::Event * event)860 void BChoppr_GUI::monitorScrolledCallback (BEvents::Event* event)
861 {
862 if (!event) return;
863 BEvents::WheelEvent* wev = (BEvents::WheelEvent*) event;
864 BWidgets::Widget* widget = event->getWidget();
865 if (!widget) return;
866 BChoppr_GUI* ui = (BChoppr_GUI*)widget->getMainWindow();
867 if (!ui) return;
868
869 ui->scale += 0.1 * wev->getDelta().y * ui->scale;
870 if (ui->scale < 0.0001f) ui->scale = 0.0001f;
871 ui->redrawMainMonitor ();
872 }
873
monitorDraggedCallback(BEvents::Event * event)874 void BChoppr_GUI::monitorDraggedCallback (BEvents::Event* event)
875 {
876 if (!event) return;
877 BEvents::PointerEvent* wev = (BEvents::PointerEvent*) event;
878 BWidgets::Widget* widget = event->getWidget();
879 if (!widget) return;
880 BChoppr_GUI* ui = (BChoppr_GUI*)widget->getMainWindow();
881 if (!ui) return;
882
883 ui->scale += 0.01 * wev->getDelta().y * ui->scale;
884 if (ui->scale < 0.0001f) ui->scale = 0.0001f;
885 ui->redrawMainMonitor ();
886 }
887
listBoxChangedCallback(BEvents::Event * event)888 void BChoppr_GUI::listBoxChangedCallback (BEvents::Event* event)
889 {
890 if (!event) return;
891 BEvents::ValueChangedEvent* vev = (BEvents::ValueChangedEvent*) event;
892 BWidgets::ListBox* lb = (BWidgets::ListBox*) vev->getWidget();
893 if (!lb) return;
894 Marker* m = (Marker*) lb->getParent();
895 if (!m) return;
896 BChoppr_GUI* ui = (BChoppr_GUI*)m->getMainWindow();
897 if (!ui) return;
898
899 double value = vev->getValue();
900 if (value == 1.0) m->setHasValue (false);
901 else if (value == 2.0) m->setHasValue (true);
902 else return;
903
904 lb->hide();
905 ui->setAutoMarkers();
906 ui->rearrange_controllers();
907 ui->redrawSContainer();
908 ui->redrawMainMonitor();
909 }
910
markersAutoClickedCallback(BEvents::Event * event)911 void BChoppr_GUI::markersAutoClickedCallback (BEvents::Event* event)
912 {
913 if (!event) return;
914 BEvents::ValueChangedEvent* vev = (BEvents::ValueChangedEvent*) event;
915 if (vev->getValue() == 0.0) return;
916 BWidgets::TextButton* tb = (BWidgets::TextButton*) vev->getWidget();
917 if (!tb) return;
918 BChoppr_GUI* ui = (BChoppr_GUI*)tb->getMainWindow();
919 if (!ui) return;
920
921 for (Marker& m : ui->markerWidgets) m.setHasValue (false);
922
923 ui->setAutoMarkers();
924 ui->rearrange_controllers();
925 ui->redrawSContainer();
926 ui->redrawMainMonitor();
927 }
928
buttonClickedCallback(BEvents::Event * event)929 void BChoppr_GUI::buttonClickedCallback (BEvents::Event* event)
930 {
931 if (!event) return;
932 BWidgets::DrawingSurface* w = (BWidgets::DrawingSurface*) event->getWidget();
933 if (!w) return;
934 BChoppr_GUI* ui = (BChoppr_GUI*) w->getMainWindow();
935 if (!ui) return;
936
937 if (w == &ui->rectButton) ui->blendControl.setValue (1);
938 else if (w == &ui->sinButton) ui->blendControl.setValue (2);
939 }
940
sharedDataClickedCallback(BEvents::Event * event)941 void BChoppr_GUI::sharedDataClickedCallback (BEvents::Event* event)
942 {
943 if (!event) return;
944 HaloToggleButton* widget = (HaloToggleButton*) event->getWidget ();
945 if (!widget) return;
946 double value = widget->getValue();
947 BChoppr_GUI* ui = (BChoppr_GUI*) widget->getMainWindow();
948 if (!ui) return;
949
950 if (value)
951 {
952 for (int i = 0; i < 4; ++i)
953 {
954 if (widget == &ui->sharedDataButtons[i])
955 {
956 ui->sharedDataSelection.setValue (i + 1);
957 return;
958 }
959 }
960 }
961 ui->sharedDataSelection.setValue (0);
962 }
963
helpButtonClickedCallback(BEvents::Event * event)964 void BChoppr_GUI::helpButtonClickedCallback (BEvents::Event* event)
965 {
966 char cmd[] = WWW_BROWSER_CMD;
967 char param[] = HELP_URL;
968 char* argv[] = {cmd, param, NULL};
969 std::cerr << "BChoppr.lv2#GUI: Call " << HELP_URL << " for help.\n";
970 if (BUtilities::vsystem (argv) == -1) std::cerr << "BChoppr.lv2#GUI: Couldn't fork.\n";
971 }
972
ytButtonClickedCallback(BEvents::Event * event)973 void BChoppr_GUI::ytButtonClickedCallback (BEvents::Event* event)
974 {
975 char cmd[] = WWW_BROWSER_CMD;
976 char param[] = YT_URL;
977 char* argv[] = {cmd, param, NULL};
978 std::cerr << "BChoppr.lv2#GUI: Call " << YT_URL << " for tutorial video.\n";
979 if (BUtilities::vsystem (argv) == -1) std::cerr << "BChoppr.lv2#GUI: Couldn't fork.\n";
980 }
981
stepControlLabelMessageCallback(BEvents::Event * event)982 void BChoppr_GUI::stepControlLabelMessageCallback (BEvents::Event* event)
983 {
984 if (event && event->getWidget())
985 {
986 BWidgets::Label* l = (BWidgets::Label*)event->getWidget();
987 BChoppr_GUI* ui = (BChoppr_GUI*)l->getMainWindow();
988 if (ui)
989 {
990 for (int i = 0; i < MAXSTEPS; ++i)
991 {
992 if (l == &ui->stepControlLabel[i])
993 {
994 double val = ui->stepControl[i].getValue();
995 try {val = BUtilities::stof (l->getText());}
996 catch (std::invalid_argument &ia)
997 {
998 fprintf (stderr, "%s\n", ia.what());
999 l->setText (BUtilities::to_string (val, "%1.2f"));
1000 return;
1001 }
1002
1003 ui->stepControl[i].setValue (val);
1004 l->setText (BUtilities::to_string (ui->stepControl[i].getValue(), "%1.2f"));
1005 break;
1006 }
1007 }
1008 }
1009 }
1010 }
1011
init_Stepshape()1012 bool BChoppr_GUI::init_Stepshape ()
1013 {
1014 double height = stepshapeDisplay.getEffectiveHeight ();
1015 pat5 = cairo_pattern_create_linear (0, 0, 0, height);
1016
1017 return (pat5 && (cairo_pattern_status (pat5) == CAIRO_STATUS_SUCCESS));
1018 }
1019
destroy_Stepshape()1020 void BChoppr_GUI::destroy_Stepshape ()
1021 {
1022 //Destroy also mainMonitors cairo data
1023 if (pat5 && (cairo_pattern_status (pat5) == CAIRO_STATUS_SUCCESS)) cairo_pattern_destroy (pat5);
1024 }
1025
redrawStepshape()1026 void BChoppr_GUI::redrawStepshape ()
1027 {
1028 double width = stepshapeDisplay.getEffectiveWidth ();
1029 double height = stepshapeDisplay.getEffectiveHeight ();
1030
1031 cairo_t* cr = cairo_create (stepshapeDisplay.getDrawingSurface ());
1032 if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) return;
1033
1034 // Draw background
1035 cairo_set_source_rgba (cr, CAIRO_BG_COLOR);
1036 cairo_rectangle (cr, 0.0, 0.0, width, height);
1037 cairo_fill (cr);
1038 cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::grey));
1039 cairo_set_line_width (cr, 1);
1040 cairo_move_to (cr, 0, 0.2 * height);
1041 cairo_line_to (cr, width, 0.2 * height);
1042 cairo_move_to (cr, 0, 0.55 * height);
1043 cairo_line_to (cr, width, 0.55 * height);
1044 cairo_move_to (cr, 0, 0.9 * height);
1045 cairo_line_to (cr, width, 0.9 * height);
1046 cairo_move_to (cr, 0.25 * width, 0);
1047 cairo_line_to (cr, 0.25 * width, height);
1048 cairo_move_to (cr, 0.5 * width, 0);
1049 cairo_line_to (cr, 0.5 * width, height);
1050 cairo_move_to (cr, 0.75 * width, 0);
1051 cairo_line_to (cr, 0.75 * width, height);
1052 cairo_stroke (cr);
1053
1054 // Draw step shape
1055 cairo_set_source_rgba (cr, CAIRO_INK1, 1.0);
1056 cairo_set_line_width (cr, 3);
1057
1058 cairo_move_to (cr, 0, 0.9 * height);
1059 cairo_line_to (cr, width * 0.25, 0.9 * height);
1060
1061 const float attack = attackControl.getValue();
1062 const float release = releaseControl.getValue();
1063
1064 if (blendControl.getValue() == 1)
1065 {
1066 if ((attack + release) > 1)
1067 {
1068 float crosspointX = attack / (attack + release);
1069 float crosspointY = crosspointX / attack - (crosspointX - (1 - release)) / release;
1070 cairo_line_to (cr, width * 0.25 + crosspointX * width * 0.5, 0.9 * height - 0.7 * height * crosspointY);
1071 }
1072 else
1073 {
1074 cairo_line_to (cr, width * 0.25 + attack * width * 0.5, 0.2 * height);
1075 cairo_line_to (cr, width * 0.75 - release * width * 0.5, 0.2 * height);
1076
1077 }
1078 }
1079
1080 else if (blendControl.getValue() == 2)
1081 {
1082 for (double i = 0.0; i <= 1.0; i += 0.025)
1083 {
1084 double vol = 1.0;
1085 if (i < attack) vol = sin (M_PI * (i / attack - 0.5));
1086 if (i > (1 - release)) vol = vol * sin (M_PI * ((1 - i) / release - 0.5));
1087 cairo_line_to (cr, width * (0.25 + 0.5 * i), height * (0.55 - 0.35 * vol));
1088 }
1089 }
1090
1091 cairo_line_to (cr, width * 0.75, 0.9 * height);
1092 cairo_line_to (cr, width, 0.9 * height);
1093
1094 cairo_stroke_preserve (cr);
1095
1096 cairo_pattern_add_color_stop_rgba (pat5, 0.1, CAIRO_INK1, 1);
1097 cairo_pattern_add_color_stop_rgba (pat5, 0.9, CAIRO_INK1, 0);
1098 cairo_set_source (cr, pat5);
1099 cairo_line_to(cr, 0, 0.9 * height);
1100 cairo_set_line_width (cr, 0);
1101 cairo_fill (cr);
1102
1103 cairo_destroy (cr);
1104
1105 stepshapeDisplay.update ();
1106 }
1107
init_mainMonitor()1108 bool BChoppr_GUI::init_mainMonitor ()
1109 {
1110 //Initialize mainMonitor
1111 mainMonitor.record_on = true;
1112 mainMonitor.width = 0;
1113 mainMonitor.height = 0;
1114 mainMonitor.data.fill (defaultNotification);
1115 mainMonitor.horizonPos = 0;
1116
1117 //Initialize mainMonitors cairo data
1118 double width = monitorDisplay.getEffectiveWidth ();
1119 double height = monitorDisplay.getEffectiveHeight ();
1120 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
1121 cr1 = cairo_create (surface);
1122 cr2 = cairo_create (surface);
1123 cr3 = cairo_create (surface);
1124 cr4 = cairo_create (surface);
1125 pat1 = cairo_pattern_create_linear (0, 0, 0, height);
1126 cairo_pattern_add_color_stop_rgba (pat1, 0.1, CAIRO_INK1, 1);
1127 cairo_pattern_add_color_stop_rgba (pat1, 0.6, CAIRO_INK1, 0);
1128 pat2 = cairo_pattern_create_linear (0, 0, 0, height);
1129 cairo_pattern_add_color_stop_rgba (pat2, 0.1, CAIRO_INK2, 1);
1130 cairo_pattern_add_color_stop_rgba (pat2, 0.6, CAIRO_INK2, 0);
1131 pat3 = cairo_pattern_create_linear (0, height, 0, 0);
1132 cairo_pattern_add_color_stop_rgba (pat3, 0.1, CAIRO_INK1, 1);
1133 cairo_pattern_add_color_stop_rgba (pat3, 0.6, CAIRO_INK1, 0);
1134 pat4 = cairo_pattern_create_linear (0, height, 0, 0);
1135 cairo_pattern_add_color_stop_rgba (pat4, 0.1, CAIRO_INK2, 1);
1136 cairo_pattern_add_color_stop_rgba (pat4, 0.6, CAIRO_INK2, 0);
1137
1138 return (pat4 && (cairo_pattern_status (pat4) == CAIRO_STATUS_SUCCESS) &&
1139 pat3 && (cairo_pattern_status (pat3) == CAIRO_STATUS_SUCCESS) &&
1140 pat2 && (cairo_pattern_status (pat2) == CAIRO_STATUS_SUCCESS) &&
1141 pat1 && (cairo_pattern_status (pat1) == CAIRO_STATUS_SUCCESS) &&
1142 cr4 && (cairo_status (cr4) == CAIRO_STATUS_SUCCESS) &&
1143 cr3 && (cairo_status (cr3) == CAIRO_STATUS_SUCCESS)&&
1144 cr2 && (cairo_status (cr2) == CAIRO_STATUS_SUCCESS) &&
1145 cr1 && (cairo_status (cr1) == CAIRO_STATUS_SUCCESS) &&
1146 surface && (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS));
1147 }
1148
destroy_mainMonitor()1149 void BChoppr_GUI::destroy_mainMonitor ()
1150 {
1151 //Destroy also mainMonitors cairo data
1152 if (pat4 && (cairo_pattern_status (pat4) == CAIRO_STATUS_SUCCESS)) cairo_pattern_destroy (pat4);
1153 if (pat3 && (cairo_pattern_status (pat3) == CAIRO_STATUS_SUCCESS)) cairo_pattern_destroy (pat3);
1154 if (pat2 && (cairo_pattern_status (pat2) == CAIRO_STATUS_SUCCESS)) cairo_pattern_destroy (pat2);
1155 if (pat1 && (cairo_pattern_status (pat1) == CAIRO_STATUS_SUCCESS)) cairo_pattern_destroy (pat1);
1156 if (cr4 && (cairo_status (cr4) == CAIRO_STATUS_SUCCESS)) cairo_destroy (cr4);
1157 if (cr3 && (cairo_status (cr3) == CAIRO_STATUS_SUCCESS)) cairo_destroy (cr3);
1158 if (cr2 && (cairo_status (cr2) == CAIRO_STATUS_SUCCESS)) cairo_destroy (cr2);
1159 if (cr1 && (cairo_status (cr1) == CAIRO_STATUS_SUCCESS)) cairo_destroy (cr1);
1160 if (surface && (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS)) cairo_surface_destroy (surface);
1161 }
1162
add_monitor_data(BChopprNotifications * notifications,uint32_t notificationsCount,uint32_t & end)1163 void BChoppr_GUI::add_monitor_data (BChopprNotifications* notifications, uint32_t notificationsCount, uint32_t& end)
1164 {
1165 for (uint32_t i = 0; i < notificationsCount; ++i)
1166 {
1167 int monitorpos = notifications[i].position;
1168 if (monitorpos >= MONITORBUFFERSIZE) monitorpos = MONITORBUFFERSIZE;
1169 if (monitorpos < 0) monitorpos = 0;
1170
1171 mainMonitor.data[monitorpos].inputMin = notifications[i].inputMin;
1172 mainMonitor.data[monitorpos].inputMax = notifications[i].inputMax;
1173 mainMonitor.data[monitorpos].outputMin = notifications[i].outputMin;
1174 mainMonitor.data[monitorpos].outputMax = notifications[i].outputMax;
1175 mainMonitor.horizonPos = monitorpos;
1176 }
1177 }
1178
redrawMainMonitor()1179 void BChoppr_GUI::redrawMainMonitor ()
1180 {
1181 double width = monitorDisplay.getEffectiveWidth ();
1182 double height = monitorDisplay.getEffectiveHeight ();
1183
1184 cairo_t* cr = cairo_create (monitorDisplay.getDrawingSurface ());
1185 if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) return;
1186
1187 // Draw background
1188 cairo_set_source_rgba (cr, CAIRO_BG_COLOR);
1189 cairo_rectangle (cr, 0, 0, width, height);
1190 cairo_fill (cr);
1191
1192 cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::grey));
1193 cairo_set_line_width (cr, 1);
1194 cairo_move_to (cr, 0, 0.1 * height);
1195 cairo_line_to (cr, width, 0.1 * height);
1196 cairo_move_to (cr, 0, 0.5 * height);
1197 cairo_line_to (cr, width, 0.5 * height);
1198 cairo_move_to (cr, 0, 0.9 * height);
1199 cairo_line_to (cr, width, 0.9 * height);
1200
1201 uint32_t steps = (uint32_t) nrStepsControl.getValue() - 1;
1202 for (uint32_t i = 0; i < steps; ++i)
1203 {
1204 cairo_move_to (cr, markerWidgets[i].getValue() * width, 0);
1205 cairo_rel_line_to (cr, 0, height);
1206 }
1207 cairo_stroke (cr);
1208
1209 if (mainMonitor.record_on)
1210 {
1211 cairo_surface_clear (surface);
1212
1213 // Draw input (cr, cr3) and output (cr2, cr4) curves
1214 cairo_move_to (cr1, 0, height * (0.5 - (0.4 * LIM ((mainMonitor.data[0].inputMax / scale), 0.0f, 1.0f))));
1215 cairo_move_to (cr2, 0, height * (0.5 - (0.4 * LIM ((mainMonitor.data[0].outputMax / scale), 0.0f, 1.0f))));
1216 cairo_move_to (cr3, 0, height * (0.5 + (0.4 * LIM (-(mainMonitor.data[0].inputMin / scale), 0.0f, 1.0f))));
1217 cairo_move_to (cr4, 0, height * (0.5 + (0.4 * LIM (-(mainMonitor.data[0].outputMin / scale), 0.0f, 1.0f))));
1218
1219 for (int i = 0; i < MONITORBUFFERSIZE; ++i)
1220 {
1221 double pos = ((double) i) / (MONITORBUFFERSIZE - 1.0f);
1222 cairo_line_to (cr1, pos * width, height * (0.5 - (0.4 * LIM ((mainMonitor.data[i].inputMax / scale), 0.0f, 1.0f))));
1223 cairo_line_to (cr2, pos * width, height * (0.5 - (0.4 * LIM ((mainMonitor.data[i].outputMax / scale), 0.0f, 1.0f))));
1224 cairo_line_to (cr3, pos * width, height * (0.5 + (0.4 * LIM (-(mainMonitor.data[i].inputMin / scale), 0.0f, 1.0f))));
1225 cairo_line_to (cr4, pos * width, height * (0.5 + (0.4 * LIM (-(mainMonitor.data[i].outputMin / scale), 0.0f, 1.0f))));
1226 }
1227
1228 // Visualize input (cr, cr3) and output (cr2, cr4) curves
1229 cairo_set_source_rgba (cr1, CAIRO_INK1, 1.0);
1230 cairo_set_line_width (cr1, 3);
1231 cairo_set_source_rgba (cr2, CAIRO_INK2, 1.0);
1232 cairo_set_line_width (cr2, 3);
1233 cairo_stroke_preserve (cr1);
1234 cairo_stroke_preserve (cr2);
1235 cairo_set_source_rgba (cr3, CAIRO_INK1, 1.0);
1236 cairo_set_line_width (cr3, 3);
1237 cairo_set_source_rgba (cr4, CAIRO_INK2, 1.0);
1238 cairo_set_line_width (cr4, 3);
1239 cairo_stroke_preserve (cr3);
1240 cairo_stroke_preserve (cr4);
1241
1242 // Visualize input (cr, cr3) and output (cr2, cr4) areas under the curves
1243 cairo_line_to (cr1, width, height * 0.5);
1244 cairo_line_to (cr1, 0, height * 0.5);
1245 cairo_close_path (cr1);
1246 cairo_line_to (cr2, width, height * 0.5);
1247 cairo_line_to (cr2, 0, height * 0.5);
1248 cairo_close_path (cr2);
1249 cairo_set_source (cr1, pat1);
1250 cairo_set_line_width (cr1, 0);
1251 cairo_set_source (cr2, pat2);
1252 cairo_set_line_width (cr2, 0);
1253 cairo_fill (cr1);
1254 cairo_fill (cr2);
1255 cairo_line_to (cr3, width, height * 0.5);
1256 cairo_line_to (cr3, 0, height * 0.5);
1257 cairo_close_path (cr3);
1258 cairo_line_to (cr4, width, height * 0.5);
1259 cairo_line_to (cr4, 0, height * 0.5);
1260 cairo_close_path (cr4);
1261 cairo_set_source (cr3, pat3);
1262 cairo_set_line_width (cr3, 0);
1263 cairo_set_source (cr4, pat4);
1264 cairo_set_line_width (cr4, 0);
1265 cairo_fill (cr3);
1266 cairo_fill (cr4);
1267
1268 // Draw fade out
1269 double horizon = ((double) mainMonitor.horizonPos) / (MONITORBUFFERSIZE - 1.0f);
1270 cairo_pattern_t* pat6 = cairo_pattern_create_linear (horizon * width, 0, horizon * width + 63, 0);
1271 if (cairo_pattern_status (pat6) == CAIRO_STATUS_SUCCESS)
1272 {
1273 cairo_pattern_add_color_stop_rgba (pat6, 0.0, CAIRO_BG_COLOR);
1274 cairo_pattern_add_color_stop_rgba (pat6, 1.0, CAIRO_TRANSPARENT);
1275 cairo_set_line_width (cr1, 0.0);
1276 cairo_set_source (cr1, pat6);
1277 cairo_rectangle (cr1, horizon * width, 0, 63, height);
1278 cairo_fill (cr1);
1279 cairo_pattern_destroy (pat6);
1280 }
1281
1282 if (horizon * width > width - 63)
1283 {
1284 cairo_pattern_t* pat6 = cairo_pattern_create_linear ((horizon - 1) * width, 0, (horizon - 1) * width + 63, 0);
1285 if (cairo_pattern_status (pat6) == CAIRO_STATUS_SUCCESS)
1286 {
1287 cairo_pattern_add_color_stop_rgba (pat6, 0.0, CAIRO_BG_COLOR);
1288 cairo_pattern_add_color_stop_rgba (pat6, 1.0, CAIRO_TRANSPARENT);
1289 cairo_set_line_width (cr1, 0.0);
1290 cairo_set_source (cr1, pat6);
1291 cairo_rectangle (cr1, (horizon - 1) * width, 0, 63, height);
1292 cairo_fill (cr1);
1293 cairo_pattern_destroy (pat6);
1294 }
1295 }
1296
1297 // Draw horizon line
1298 cairo_set_source_rgba (cr1, CAIRO_FG_COLOR);
1299 cairo_set_line_width (cr1, 1);
1300 cairo_move_to (cr1, horizon * width, 0);
1301 cairo_line_to (cr1, horizon * width, height);
1302 cairo_stroke (cr1);
1303 }
1304
1305 cairo_set_source_surface (cr, surface, 0, 0);
1306 cairo_paint (cr);
1307
1308 cairo_destroy (cr);
1309 monitorDisplay.update ();
1310 }
1311
redrawSContainer()1312 void BChoppr_GUI::redrawSContainer ()
1313 {
1314 double width = sContainer.getEffectiveWidth ();
1315 double height = sContainer.getEffectiveHeight ();
1316
1317 cairo_surface_clear (sContainer.getDrawingSurface ());
1318 cairo_t* cr = cairo_create (sContainer.getDrawingSurface ());
1319 if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) return;
1320
1321 cairo_pattern_t* pat = cairo_pattern_create_linear (0, 0, 0, height);
1322 cairo_pattern_add_color_stop_rgba (pat, 0.0, CAIRO_RGBA (BColors::black));
1323 cairo_pattern_add_color_stop_rgba (pat, 1.0, 0.0, 0.0, 0.0, 0.5);
1324 cairo_rectangle (cr, 0, 0, width, height);
1325 cairo_set_source (cr, pat);
1326 cairo_fill (cr);
1327 cairo_pattern_destroy (pat);
1328
1329 for (int i = 0; i < nrStepsControl.getValue() - 1; ++i)
1330 {
1331 cairo_set_line_width (cr, 1.0);
1332 cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::grey));
1333 cairo_move_to (cr, markerWidgets[i].getValue() * width, 0);
1334 cairo_rel_line_to (cr, 0, 30 * sz);
1335 cairo_line_to (cr, (i + 1) / nrStepsControl.getValue() * width, 40 * sz);
1336 cairo_rel_line_to (cr, 0, 100 * sz);
1337 cairo_stroke (cr);
1338 }
1339
1340 cairo_destroy (cr);
1341 sContainer.update();
1342 }
1343
redrawButtons()1344 void BChoppr_GUI::redrawButtons ()
1345 {
1346 // rectButton
1347 double width = rectButton.getEffectiveWidth ();
1348 double height = rectButton.getEffectiveHeight ();
1349
1350 cairo_surface_clear (rectButton.getDrawingSurface ());
1351 cairo_t* cr = cairo_create (rectButton.getDrawingSurface ());
1352 if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) return;
1353
1354 cairo_set_source_rgba (cr, CAIRO_RGBA (*rectButton.getBorder()->getLine()->getColor()));
1355 cairo_set_line_width (cr, 2.0);
1356
1357 cairo_move_to (cr, 0.05 * width, 0.9 * height);
1358 cairo_line_to (cr, 0.25 * width, 0.9 * height);
1359 cairo_line_to (cr, 0.3 * width, 0.1 * height);
1360 cairo_line_to (cr, 0.7 * width, 0.1 * height);
1361 cairo_line_to (cr, 0.75 * width, 0.9 * height);
1362 cairo_line_to (cr, 0.95 * width, 0.9 * height);
1363 cairo_stroke (cr);
1364
1365 cairo_destroy (cr);
1366
1367 // sinButton
1368 width = sinButton.getEffectiveWidth ();
1369 height = sinButton.getEffectiveHeight ();
1370
1371 cairo_surface_clear (sinButton.getDrawingSurface ());
1372 cr = cairo_create (sinButton.getDrawingSurface ());
1373 if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) return;
1374
1375 cairo_set_source_rgba (cr, CAIRO_RGBA (*sinButton.getBorder()->getLine()->getColor()));
1376 cairo_set_line_width (cr, 2.0);
1377
1378 cairo_move_to (cr, 0.05 * width, 0.9 * height);
1379 cairo_line_to (cr, 0.15 * width, 0.9 * height);
1380 for (int i = 0; i <= 10; ++i) cairo_line_to (cr, (0.15 + i * 0.03) * width, (0.5 - 0.4 * sin (double (i - 5) * M_PI / 10)) * height);
1381 cairo_line_to (cr, 0.55 * width, 0.1 * height);
1382 for (int i = 0; i <= 10; ++i) cairo_line_to (cr, (0.55 + i * 0.03) * width, (0.5 - 0.4 * sin (double (i + 5) * M_PI / 10)) * height);
1383 cairo_line_to (cr, 0.95 * width, 0.9 * height);
1384 cairo_stroke (cr);
1385
1386 cairo_destroy (cr);
1387
1388 }
1389
instantiate(const LV2UI_Descriptor * descriptor,const char * plugin_uri,const char * bundle_path,LV2UI_Write_Function write_function,LV2UI_Controller controller,LV2UI_Widget * widget,const LV2_Feature * const * features)1390 static LV2UI_Handle instantiate (const LV2UI_Descriptor *descriptor, const char *plugin_uri, const char *bundle_path,
1391 LV2UI_Write_Function write_function, LV2UI_Controller controller, LV2UI_Widget *widget,
1392 const LV2_Feature *const *features)
1393 {
1394 PuglNativeView parentWindow = 0;
1395 LV2UI_Resize* resize = NULL;
1396
1397 if (strcmp(plugin_uri, BCHOPPR_URI) != 0)
1398 {
1399 std::cerr << "BChoppr.lv2#GUI: GUI does not support plugin with URI " << plugin_uri << std::endl;
1400 return NULL;
1401 }
1402
1403 for (int i = 0; features[i]; ++i)
1404 {
1405 if (!strcmp(features[i]->URI, LV2_UI__parent)) parentWindow = (PuglNativeView) features[i]->data;
1406 else if (!strcmp(features[i]->URI, LV2_UI__resize)) resize = (LV2UI_Resize*)features[i]->data;
1407 }
1408 if (parentWindow == 0) std::cerr << "BChoppr.lv2#GUI: No parent window.\n";
1409
1410 // New instance
1411 BChoppr_GUI* ui;
1412 try {ui = new BChoppr_GUI (bundle_path, features, parentWindow);}
1413 catch (std::exception& exc)
1414 {
1415 std::cerr << "BChoppr.lv2#GUI: Instantiation failed. " << exc.what () << std::endl;
1416 return NULL;
1417 }
1418
1419 ui->controller = controller;
1420 ui->write_function = write_function;
1421
1422 // Reduce min GUI size for small displays
1423 double sz = 1.0;
1424 int screenWidth = getScreenWidth ();
1425 int screenHeight = getScreenHeight ();
1426 if ((screenWidth < 820) || (screenHeight < 600)) sz = 0.66;
1427 if (resize) resize->ui_resize(resize->handle, 760 * sz, 560 * sz);
1428
1429 *widget = (LV2UI_Widget) puglGetNativeWindow (ui->getPuglView ());
1430 ui->send_record_on();
1431 return (LV2UI_Handle) ui;
1432 }
1433
cleanup(LV2UI_Handle ui)1434 static void cleanup(LV2UI_Handle ui)
1435 {
1436 BChoppr_GUI* pluginGui = (BChoppr_GUI*) ui;
1437 if (pluginGui) delete pluginGui;
1438 }
1439
portEvent(LV2UI_Handle ui,uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)1440 static void portEvent(LV2UI_Handle ui, uint32_t port_index, uint32_t buffer_size,
1441 uint32_t format, const void* buffer)
1442 {
1443 BChoppr_GUI* pluginGui = (BChoppr_GUI*) ui;
1444 if (pluginGui) pluginGui->portEvent(port_index, buffer_size, format, buffer);
1445 }
1446
callIdle(LV2UI_Handle ui)1447 static int callIdle (LV2UI_Handle ui)
1448 {
1449 BChoppr_GUI* pluginGui = (BChoppr_GUI*) ui;
1450 if (pluginGui) pluginGui->handleEvents ();
1451 return 0;
1452 }
1453
callResize(LV2UI_Handle ui,int width,int height)1454 static int callResize (LV2UI_Handle ui, int width, int height)
1455 {
1456 BChoppr_GUI* self = (BChoppr_GUI*) ui;
1457 if (!self) return 0;
1458
1459 BEvents::ExposeEvent* ev = new BEvents::ExposeEvent (self, self, BEvents::CONFIGURE_REQUEST_EVENT, self->getPosition().x, self->getPosition().y, width, height);
1460 self->addEventToQueue (ev);
1461 return 0;
1462 }
1463
1464 static const LV2UI_Idle_Interface idle = {callIdle};
1465 static const LV2UI_Resize resize = {nullptr, callResize} ;
1466
extensionData(const char * uri)1467 static const void* extensionData(const char* uri)
1468 {
1469 if (!strcmp(uri, LV2_UI__idleInterface)) return &idle;
1470 else if(!strcmp(uri, LV2_UI__resize)) return &resize;
1471 else return NULL;
1472 }
1473
1474 static const LV2UI_Descriptor guiDescriptor = {
1475 BCHOPPR_GUI_URI,
1476 instantiate,
1477 cleanup,
1478 portEvent,
1479 extensionData
1480 };
1481
1482 // LV2 Symbol Export
lv2ui_descriptor(uint32_t index)1483 LV2_SYMBOL_EXPORT const LV2UI_Descriptor *lv2ui_descriptor(uint32_t index)
1484 {
1485 switch (index) {
1486 case 0: return &guiDescriptor;
1487 default:return NULL;
1488 }
1489 }
1490