1 /* B.SEQuencer
2 * MIDI Step Sequencer LV2 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 "BSEQuencer_GUI.hpp"
22 #include "BUtilities/to_string.hpp"
23 #include "BUtilities/vsystem.hpp"
24 #include <exception>
25
BSEQuencer_GUI(const char * bundle_path,const LV2_Feature * const * features,PuglNativeView parentWindow)26 BSEQuencer_GUI::BSEQuencer_GUI (const char *bundle_path, const LV2_Feature *const *features, PuglNativeView parentWindow) :
27 Window (1250, 820, "B.SEQuencer", parentWindow, true, PUGL_MODULE, 0),
28 controller (NULL), write_function (NULL),
29 pluginPath (bundle_path ? std::string (bundle_path) : std::string ("")),
30 sz (1.0), bgImageSurface (nullptr),
31 uris (), forge (), clipBoard (),
32 cursorBits {0}, noteBits (0), chBits (0),
33 tempTool (false), tempToolCh (0), wheelScrolled (false), modifier (MODIFIER_VELOCITY),
34 mContainer (0, 0, 1250, 820, "main"),
35 padSurface (98, 88, 804, 484, "box"),
36 captionSurface (18, 88, 64, 484, "box"),
37
38 modeBox (920, 88, 310, 170, "box"),
39 modeBoxLabel (10, 10, 290, 20, "ctlabel", BSEQUENCER_LABEL_PLAY_MODE),
40 modeLabel (10, 80, 60, 20, "lflabel", BSEQUENCER_LABEL_MODE),
41 modeListBox (80, 80, 220, 20, 220, 80, "menu", BItems::ItemList ({{1, BSEQUENCER_LABEL_AUTOPLAY}, {3, BSEQUENCER_LABEL_HOST_CONTROLLED} , {2, BSEQUENCER_LABEL_HOST_AND_MIDI}}), 2.0),
42 modeAutoplayBpmLabel (10, 115, 120, 20, "lflabel", BSEQUENCER_LABEL_BPM),
43 modeAutoplayBpmSlider (120, 105, 180, 25, "slider", 120.0, 1.0, 300.0, 0.0, "%3.1f"),
44 modeAutoplayBpbLabel (10, 145, 120, 20, "lflabel", BSEQUENCER_LABEL_BPB),
45 modeAutoplayBpbSlider (120, 135, 180, 25, "slider", 4.0, 1.0, 16.0, 1.0, "%2.0f"),
46 modeMidiInChannelLabel (10, 110, 130, 20 , "lflabel", BSEQUENCER_LABEL_MIDI_INPUT_CH),
47 modeMidiInChannelListBox (180, 110, 120, 20, 120, 200, "menu",
48 BItems::ItemList ({{0, BSEQUENCER_LABEL_ALL}, {1, "1"}, {2, "2"}, {3, "3"}, {4, "4"}, {5, "5"}, {6, "6"}, {7, "7"}, {8, "8"}, {9, "9"},
49 {10, "10"}, {11, "11"}, {12, "12"}, {13, "13"}, {14, "14"}, {15, "15"}, {16, "16"}})),
50 modeOnKeyLabel (10, 140, 100, 20, "lflabel", BSEQUENCER_LABEL_ON_NOTE_ON),
51 modeOnKeyListBox (180, 140, 120, 20, 120, 80, "menu", BItems::ItemList ({{0, BSEQUENCER_LABEL_RESTART}, {2, BSEQUENCER_LABEL_RESTART_SYNC}, {1, BSEQUENCER_LABEL_CONTINUE}})),
52 modePlayLabel (10, 50, 205, 20, "lflabel", BSEQUENCER_LABEL_STATUS_PLAYING),
53 modePlayButton (270, 40, 30, 30, "box", 1.0),
54
55 toolBox (920, 280, 310, 292, "box"),
56 toolBoxLabel (10, 10, 290, 20, "ctlabel", BSEQUENCER_LABEL_TOOLBOX),
57 toolButtonBox (0, 30, 310, 160, "widget"),
58 toolWholeStepButton (210, 40, 80, 20, "tgbutton", BSEQUENCER_LABEL_WHOLE_STEP, 0.0),
59 toolResetButton (90, 130, 20, 20, "tgbutton"),
60 toolUndoButton (120, 130, 20, 20, "tgbutton"),
61 toolRedoButton (150, 130, 20, 20, "tgbutton"),
62 toolButtonBoxCtrlLabel (10, 10, 80, 20, "lflabel", BSEQUENCER_LABEL_CONTROLS),
63 toolButtonBoxChLabel (10, 70, 80, 20, "lflabel", BSEQUENCER_LABEL_CHANNELS),
64 toolButtonBoxEditLabel (10, 100, 80, 20, "lflabel", BSEQUENCER_LABEL_EDIT),
65 toolGateLabel (10, 260, 30, 20, "ctlabel", BSEQUENCER_LABEL_GATE),
66 toolGateSlider (12.5, 200, 25, 60, "dial", 1.0, 0.0, 1.0, 0.0, "%1.2f", BWidgets::LABEL_BOTTOM),
67 toolNoteLabel (45, 260, 60, 20, "ctlabel", BSEQUENCER_LABEL_NOTE),
68 toolNoteDial (50, 200, 50, 60, "dial", 0.0, -16.0, 16.0, 1.0, "%1.0f"),
69 toolOctaveLabel (110, 260, 60, 20, "ctlabel", BSEQUENCER_LABEL_OCTAVE),
70 toolOctaveDial (115, 200, 50, 60, "dial", 0.0, -8.0, 8.0, 1.0, "%1.0f"),
71 toolVelocityLabel (175, 260, 60, 20, "ctlabel", BSEQUENCER_LABEL_VELOCITY),
72 toolVelocityDial (180, 200, 50, 60, "dial", 1.0, 0.0, 2.0, 0.0, "%1.2f"),
73 toolDurationLabel (240, 260, 60, 20, "ctlabel", BSEQUENCER_LABEL_DURATION),
74 toolDurationDial (245, 200, 50, 60, "dial", 1.0, 0.0, 1.0, 0.0, "%1.2f", UNIDIRECTIONAL),
75
76 propertiesBox (920, 590, 310, 210, "box"),
77 propertiesBoxLabel (10, 10, 290, 20, "ctlabel", BSEQUENCER_LABEL_PROPERTIES),
78 propertiesNrStepsLabel (10, 50, 170, 20, "lflabel", BSEQUENCER_LABEL_TOTAL_NUMBER_OF_STEPS),
79 propertiesNrStepsListBox (210, 50, 90, 20, 90, 100, "menu",
80 BItems::ItemList ({{8, "8"}, {16, "16"}, {24, "24"}, {32, "32"}}), 16.0),
81 propertiesStepsPerLabel (110, 85, 80, 20, "lflabel", BSEQUENCER_LABEL_STEPS_PER),
82 propertiesStepsPerSlider (10, 75, 90, 25, "slider", 4.0, 1.0, 8.0, 1.0, "%2.0f"),
83 propertiesBaseListBox (210, 85, 90, 20, 90, 60, "menu",
84 BItems::ItemList ({BSEQUENCER_LABEL_BEAT, BSEQUENCER_LABEL_BAR}), 1.0),
85 propertiesRootLabel (10, 115, 60, 20, "lflabel", BSEQUENCER_LABEL_ROOT),
86 propertiesRootListBox (110, 115, 90, 20, 0, -160, 90, 160, "menu",
87 BItems::ItemList ({{0, "C"}, {2, "D"}, {4, "E"}, {5, "F"}, {7, "G"}, {9, "A"}, {11, "B"}}), 0.0),
88 propertiesSignatureListBox (210, 115, 90, 20, 90, 80, "menu",
89 BItems::ItemList ({{-1, "b"}, {0, ""}, {1, "#"}}), 0.0),
90 propertiesOctaveLabel (10, 145, 60, 20, "lflabel", BSEQUENCER_LABEL_OCTAVE),
91 propertiesOctaveListBox (210, 145, 90, 20, 0, -220, 90, 220, "menu",
92 BItems::ItemList ({{-1, "-1"}, {0, "0"}, {1, "1"}, {2, "2"}, {3, "3"}, {4, "4"}, {5, "5"}, {6, "6"}, {7, "7"}, {8, "8"}}), 4.0),
93 propertiesScaleLabel (10, 175, 60, 20, "lflabel", BSEQUENCER_LABEL_SCALE),
94 propertiesScaleEditIcon (70, 175, 20, 20, "widget", (bundle_path ? std::string (bundle_path) + EDIT_SYMBOL : std::string (""))),
95 propertiesScaleListBox (),
96
97 helpButton (1166, 50, 24, 24, "halobutton", BSEQUENCER_LABEL_HELP),
98 ytButton (1196, 50, 24, 24, "halobutton", BSEQUENCER_LABEL_FEATURE_TOUR),
99 scaleEditor (nullptr)
100 {
101 // Init scale maps
102 for (int scaleNr = 0; scaleNr < NR_SYSTEM_SCALES + NR_USER_SCALES; ++scaleNr)
103 {
104 scaleMaps[scaleNr] = defaultScaleMaps[scaleNr];
105 }
106
107 // Init propertiesScaleListBox
108 BItems::ItemList scaleItems;
109 for (int scaleNr = 0; scaleNr < NR_SYSTEM_SCALES + NR_USER_SCALES; ++scaleNr)
110 {
111 scaleItems.push_back (BItems::Item (scaleNr, scaleMaps[scaleNr].name));
112 }
113 propertiesScaleListBox = BWidgets::PopupListBox (100, 175, 200, 20, 0, -420, 200, 420, "menu", scaleItems, 0.0);
114 propertiesScaleListBox.rename ("menu");
115
116 // Init toolbox buttons
117 toolButtonBox.addButton (90, 70, 20, 20, {{0.0, 0.03, 0.06, 1.0}, NO_CTRL, BSEQUENCER_LABEL_NO_CHANNEL});
118 for (int i = 1; i < NR_SEQUENCER_CHS + 1; ++i) toolButtonBox.addButton (90 + i * 30, 70, 20, 20, chButtonStyles[i]);
119 toolButtonBox.addButton (90 , 10, 20, 20, {{0.0, 0.03, 0.06, 1.0}, NO_CTRL, BSEQUENCER_LABEL_NO_CONTROL});
120 for (int i = 1; i < NR_CTRL_BUTTONS; ++i) toolButtonBox.addButton (90 + (i % 7) * 30, 10 + ((int) (i / 7)) * 30, 20, 20, ctrlButtonStyles[i]);
121 for (int i = 0; i < NR_EDIT_BUTTONS; ++i) toolButtonBox.addButton (90 + i * 30, 100, 20, 20, editButtonStyles[i]);
122
123 // Init ChBoxes
124 for (int i = 0; i < NR_SEQUENCER_CHS; ++i)
125 {
126 chBoxes[i].box = BWidgets::Widget (98 + i * 203.5, 590, 193.5, 210, "box");
127 chBoxes[i].chSymbol = BWidgets::DrawingSurface (7, 7, 26, 26, "button");
128 chBoxes[i].chLabel = BWidgets::Label (40, 10, 133.5, 20, "ctlabel", BSEQUENCER_LABEL_CHANNEL " " + std::to_string (i + 1));
129 chBoxes[i].channelLabel = BWidgets::Label (10, 50, 100, 20, "lflabel", BSEQUENCER_LABEL_MIDI_CH);
130 chBoxes[i].channelListBox = BWidgets::PopupListBox (123.5, 50, 60, 20, 60, 120, "menu",
131 BItems::ItemList({"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"}),
132 i + 1);
133 chBoxes[i].pitchLabel = BWidgets::Label (10, 80, 100, 20, "lflabel", BSEQUENCER_LABEL_INPUT_PITCH);
134 chBoxes[i].pitchSwitch = BWidgets::HSwitch (132.5, 82, 42, 16, "ch" + std::to_string (i + 1), 0.0);
135 chBoxes[i].pitchScreen = BWidgets::Widget (10, 80, 173.5, 20, "screen");
136 chBoxes[i].pitchScreen.hide ();
137 chBoxes[i].velocityDial = BWidgets::DialValue (25, 120, 50, 60, "ch" + std::to_string (i + 1), 1.0, 0.0, 2.0, 0.0, "%1.2f");
138 chBoxes[i].velocityLabel = BWidgets::Label (20, 180, 60, 20, "ctlabel", BSEQUENCER_LABEL_VELOCITY);
139 chBoxes[i].noteOffsetDial = BWidgets::DialValue (118.5, 120, 50, 60, "ch" + std::to_string (i + 1), 0.0, -127.0, 127.0, 1.0, "%1.0f");
140 chBoxes[i].noteOffsetLabel = BWidgets::Label (113.5, 180, 60, 20, "ctlabel", BSEQUENCER_LABEL_OFFSET);
141 }
142
143 // Link controllerWidgets
144 controllerWidgets[MIDI_IN_CHANNEL] = (BWidgets::ValueWidget*) &modeMidiInChannelListBox;
145 controllerWidgets[PLAY] = (BWidgets::ValueWidget*) &modePlayButton;
146 controllerWidgets[MODE] = (BWidgets::ValueWidget*) &modeListBox;
147 controllerWidgets[ON_KEY_PRESSED] = (BWidgets::ValueWidget*) &modeOnKeyListBox;
148 controllerWidgets[NR_OF_STEPS] = (BWidgets::ValueWidget*) &propertiesNrStepsListBox;
149 controllerWidgets[STEPS_PER] = (BWidgets::ValueWidget*) &propertiesStepsPerSlider;
150 controllerWidgets[BASE] = (BWidgets::ValueWidget*) &propertiesBaseListBox;
151 controllerWidgets[ROOT] = (BWidgets::ValueWidget*) &propertiesRootListBox;
152 controllerWidgets[SIGNATURE] = (BWidgets::ValueWidget*) &propertiesSignatureListBox;
153 controllerWidgets[OCTAVE] = (BWidgets::ValueWidget*) &propertiesOctaveListBox;
154 controllerWidgets[SCALE] = (BWidgets::ValueWidget*) &propertiesScaleListBox;
155 controllerWidgets[AUTOPLAY_BPM] = (BWidgets::ValueWidget*) &modeAutoplayBpmSlider;
156 controllerWidgets[AUTOPLAY_BPB] = (BWidgets::ValueWidget*) &modeAutoplayBpbSlider;
157 controllerWidgets[SELECTION_CH] = (BWidgets::ValueWidget*) &toolButtonBox;
158 controllerWidgets[SELECTION_NOTE] = (BWidgets::ValueWidget*) &toolNoteDial;
159 controllerWidgets[SELECTION_OCTAVE] = (BWidgets::ValueWidget*) &toolOctaveDial;
160 controllerWidgets[SELECTION_VELOCITY] = (BWidgets::ValueWidget*) &toolVelocityDial;
161 controllerWidgets[SELECTION_DURATION] = (BWidgets::ValueWidget*) &toolDurationDial;
162 controllerWidgets[SELECTION_GATE_RAND] = (BWidgets::ValueWidget*) &toolGateSlider;
163 controllerWidgets[SELECTION_NOTE_RAND] = (BWidgets::ValueWidget*) &toolNoteDial.range;
164 controllerWidgets[SELECTION_OCTAVE_RAND] = (BWidgets::ValueWidget*) &toolOctaveDial.range;
165 controllerWidgets[SELECTION_VELOCITY_RAND] = (BWidgets::ValueWidget*) &toolVelocityDial.range;
166 controllerWidgets[SELECTION_DURATION_RAND] = (BWidgets::ValueWidget*) &toolDurationDial.range;
167
168 for (int i = 0; i < NR_SEQUENCER_CHS; ++i)
169 {
170 controllerWidgets[CH + i * CH_SIZE + PITCH] = (BWidgets::ValueWidget*) &chBoxes[i].pitchSwitch;
171 controllerWidgets[CH + i * CH_SIZE + VELOCITY] = (BWidgets::ValueWidget*) &chBoxes[i].velocityDial;
172 controllerWidgets[CH + i * CH_SIZE + MIDI_CHANNEL] = (BWidgets::ValueWidget*) &chBoxes[i].channelListBox;
173 controllerWidgets[CH + i * CH_SIZE + NOTE_OFFSET] = (BWidgets::ValueWidget*) &chBoxes[i].noteOffsetDial;
174 }
175
176 // Init controller values
177 for (int i = 0; i < KNOBS_SIZE; ++i) controllers[i] = controllerWidgets[i]->getValue ();
178
179 // Set callback functions
180 for (int i = 0; i < KNOBS_SIZE; ++i) controllerWidgets[i]->setCallbackFunction (BEvents::VALUE_CHANGED_EVENT, valueChangedCallback);
181
182 padSurface.setDraggable (true);
183 padSurface.setCallbackFunction (BEvents::BUTTON_PRESS_EVENT, padsPressedCallback);
184 padSurface.setCallbackFunction (BEvents::BUTTON_RELEASE_EVENT, padsPressedCallback);
185 padSurface.setCallbackFunction (BEvents::POINTER_DRAG_EVENT, padsPressedCallback);
186
187 padSurface.setScrollable (true);
188 padSurface.setCallbackFunction (BEvents::WHEEL_SCROLL_EVENT, padsScrolledCallback);
189
190 padSurface.setFocusable (true);
191 padSurface.setCallbackFunction (BEvents::FOCUS_IN_EVENT, padsFocusedCallback);
192 padSurface.setCallbackFunction (BEvents::FOCUS_OUT_EVENT, padsFocusedCallback);
193 padSurface.setMergeable (BEvents::POINTER_DRAG_EVENT, false);
194
195 toolResetButton.setCallbackFunction(BEvents::VALUE_CHANGED_EVENT, resetClickedCallback);
196 toolUndoButton.setCallbackFunction(BEvents::VALUE_CHANGED_EVENT, undoClickedCallback);
197 toolRedoButton.setCallbackFunction(BEvents::VALUE_CHANGED_EVENT, undoClickedCallback);
198
199 helpButton.setCallbackFunction(BEvents::BUTTON_PRESS_EVENT, helpPressedCallback);
200 ytButton.setCallbackFunction(BEvents::BUTTON_PRESS_EVENT, ytPressedCallback);
201 propertiesScaleEditIcon.setCallbackFunction(BEvents::BUTTON_PRESS_EVENT, editPressedCallback);
202
203 // Apply theme
204 bgImageSurface = cairo_image_surface_create_from_png ((pluginPath + BG_FILE).c_str());
205 widgetBg.loadFillFromCairoSurface (bgImageSurface);
206 applyTheme (theme);
207
208 toolDurationDial.range.setMax (0.0);
209 toolGateSlider.setHardChangeable (false);
210 toolNoteDial.setHardChangeable (false);
211 toolOctaveDial.setHardChangeable (false);
212 toolVelocityDial.setHardChangeable (false);
213 toolDurationDial.setHardChangeable (false);
214
215 modeAutoplayBpmLabel.hide ();
216 modeAutoplayBpmSlider.hide ();
217 modeAutoplayBpbLabel.hide ();
218 modeAutoplayBpbSlider.hide ();
219
220 modeBoxLabel.setState (BColors::ACTIVE);
221 toolBoxLabel.setState (BColors::ACTIVE);
222 propertiesBoxLabel.setState (BColors::ACTIVE);
223 propertiesScaleEditIcon.hide ();
224 for (int i = 0; i < NR_SEQUENCER_CHS; ++i)
225 {
226 chBoxes[i].chLabel.setState (BColors::ACTIVE);
227 drawButton (chBoxes[i].chSymbol.getDrawingSurface(), 0, 0, 20, 20, chButtonStyles[i + 1].color, chButtonStyles[i + 1].symbol);
228 chBoxes[i].noteOffsetDial.setHardChangeable (false);
229 chBoxes[i].velocityDial.setHardChangeable (false);
230 }
231
232
233 // Pack widgets
234
235 modeBox.add (modeBoxLabel);
236 modeBox.add (modeLabel);
237 modeBox.add (modeListBox);
238 modeBox.add (modeAutoplayBpmLabel);
239 modeBox.add (modeAutoplayBpmSlider);
240 modeBox.add (modeAutoplayBpbLabel);
241 modeBox.add (modeAutoplayBpbSlider);
242 modeBox.add (modeMidiInChannelLabel);
243 modeBox.add (modeMidiInChannelListBox);
244 modeBox.add (modeOnKeyLabel);
245 modeBox.add (modeOnKeyListBox);
246 modeBox.add (modePlayLabel);
247 modeBox.add (modePlayButton);
248
249 toolBox.add (toolBoxLabel);
250 toolBox.add (toolButtonBox);
251
252 toolButtonBox.add (toolButtonBoxCtrlLabel);
253 toolButtonBox.add (toolButtonBoxChLabel);
254 toolButtonBox.add (toolButtonBoxEditLabel);
255 toolButtonBox.add (toolWholeStepButton);
256 toolButtonBox.add (toolResetButton);
257 toolButtonBox.add (toolUndoButton);
258 toolButtonBox.add (toolRedoButton);
259
260 toolBox.add (toolGateLabel);
261 toolBox.add (toolGateSlider);
262 toolBox.add (toolNoteLabel);
263 toolBox.add (toolNoteDial);
264 toolBox.add (toolOctaveLabel);
265 toolBox.add (toolOctaveDial);
266 toolBox.add (toolVelocityLabel);
267 toolBox.add (toolVelocityDial);
268 toolBox.add (toolDurationLabel);
269 toolBox.add (toolDurationDial);
270
271 propertiesBox.add (propertiesBoxLabel);
272 propertiesBox.add (propertiesNrStepsLabel);
273 propertiesBox.add (propertiesNrStepsListBox);
274 propertiesBox.add (propertiesStepsPerLabel);
275 propertiesBox.add (propertiesStepsPerSlider);
276 propertiesBox.add (propertiesBaseListBox);
277 propertiesBox.add (propertiesRootLabel);
278 propertiesBox.add (propertiesRootListBox);
279 propertiesBox.add (propertiesSignatureListBox);
280 propertiesBox.add (propertiesOctaveLabel);
281 propertiesBox.add (propertiesOctaveListBox);
282 propertiesBox.add (propertiesScaleLabel);
283 propertiesBox.add (propertiesScaleEditIcon);
284 propertiesBox.add (propertiesScaleListBox);
285
286 for (int i = 0; i < NR_SEQUENCER_CHS; ++i)
287 {
288 chBoxes[i].box.add (chBoxes[i].chSymbol);
289 chBoxes[i].box.add (chBoxes[i].chLabel);
290 chBoxes[i].box.add (chBoxes[i].channelLabel);
291 chBoxes[i].box.add (chBoxes[i].channelListBox);
292 chBoxes[i].box.add (chBoxes[i].pitchLabel);
293 chBoxes[i].box.add (chBoxes[i].pitchSwitch);
294 chBoxes[i].box.add (chBoxes[i].pitchScreen);
295 chBoxes[i].box.add (chBoxes[i].velocityDial);
296 chBoxes[i].box.add (chBoxes[i].velocityLabel);
297 chBoxes[i].box.add (chBoxes[i].noteOffsetDial);
298 chBoxes[i].box.add (chBoxes[i].noteOffsetLabel);
299 }
300
301 mContainer.add (captionSurface);
302 mContainer.add (toolBox);
303 mContainer.add (modeBox);
304 mContainer.add (propertiesBox);
305 mContainer.add (helpButton);
306 mContainer.add (ytButton);
307 mContainer.add (padSurface);
308 for (int i = 0; i < NR_SEQUENCER_CHS; ++i) mContainer.add (chBoxes[i].box);
309
310 drawCaption ();
311 drawPad();
312 add (mContainer);
313
314 getKeyGrabStack()->add (this);
315
316 pattern.clear ();
317
318 //Scan host features for URID map
319 LV2_URID_Map* map = NULL;
320 for (int i = 0; features[i]; ++i)
321 {
322 if (strcmp(features[i]->URI, LV2_URID__map) == 0)
323 {
324 map = (LV2_URID_Map*) features[i]->data;
325 }
326 }
327 if (!map) throw std::invalid_argument ("Host does not support urid:map");
328
329 //Map URIS
330 getURIs (map, &uris);
331
332 // Initialize forge
333 lv2_atom_forge_init (&forge, map);
334 }
335
~BSEQuencer_GUI()336 BSEQuencer_GUI::~BSEQuencer_GUI ()
337 {
338 if (scaleEditor) delete scaleEditor;
339
340 send_ui_off ();
341 }
342
port_event(uint32_t port,uint32_t buffer_size,uint32_t format,const void * buffer)343 void BSEQuencer_GUI::port_event(uint32_t port, uint32_t buffer_size,
344 uint32_t format, const void* buffer)
345 {
346 // Notify port
347 if ((format == uris.atom_eventTransfer) && (port == OUTPUT))
348 {
349 const LV2_Atom* atom = (const LV2_Atom*) buffer;
350 if ((atom->type == uris.atom_Blank) || (atom->type == uris.atom_Object))
351 {
352 const LV2_Atom_Object* obj = (const LV2_Atom_Object*) atom;
353
354 // Pad notification
355 if (obj->body.otype == uris.notify_padEvent)
356 {
357 LV2_Atom *oPad = NULL;
358 lv2_atom_object_get(obj, uris.notify_pad, &oPad, NULL);
359
360 if (oPad && (oPad->type == uris.atom_Vector))
361 {
362 const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oPad;
363 if (vec->body.child_type == uris.atom_Float)
364 {
365 if (wheelScrolled)
366 {
367 pattern.store ();
368 wheelScrolled = false;
369 }
370
371 uint32_t size = (uint32_t) ((oPad->size - sizeof(LV2_Atom_Vector_Body)) / sizeof (PadMessage));
372 PadMessage* pMes = (PadMessage*)(&vec->body + 1);
373 for (unsigned int i = 0; i < size; ++i)
374
375 {
376 int step = (int) pMes[i].step;
377 int row = (int) pMes[i].row;
378 if ((step >= 0) && (step < MAXSTEPS) && (row >= 0) && (row < ROWS))
379 {
380 pattern.setPad (row, step, Pad (pMes[i]));
381 }
382 }
383 pattern.store ();
384 drawPad ();
385 }
386 }
387 }
388
389 // Status notifications
390 else if (obj->body.otype == uris.notify_statusEvent)
391 {
392 LV2_Atom *oCursors = NULL, *oNotes = NULL, *oChs = NULL;
393 lv2_atom_object_get
394 (
395 obj, uris.notify_cursors, &oCursors,
396 uris.notify_notes, &oNotes,
397 uris.notify_channels, &oChs,
398 NULL
399 );
400
401 // Cursor notifications
402 if (oCursors && (oCursors->type == uris.atom_Vector))
403 {
404 const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oCursors;
405 if (vec->body.child_type == uris.atom_Int)
406 {
407 int* cb = (int*)(&vec->body + 1);
408 if (memcmp (cursorBits, cb, MAXSTEPS * sizeof(int)))
409 {
410 memcpy (&cursorBits, cb, MAXSTEPS * sizeof(int));
411 drawPad ();
412 }
413 }
414 }
415
416 // Note notifications
417 if (oNotes && (oNotes->type == uris.atom_Int) && (noteBits != ((uint32_t) ((LV2_Atom_Int*)oNotes)->body)))
418 {
419 noteBits = ((LV2_Atom_Int*)oNotes)->body;
420 drawCaption ();
421 }
422
423 // BSEQuencer channel (CH) notifications
424 if (oChs && (oChs->type == uris.atom_Int))
425 {
426 chBits = ((LV2_Atom_Int*)oChs)->body;
427 for (int i = 0; i < NR_SEQUENCER_CHS; ++i)
428 {
429 if ((1 << i) & chBits) chBoxes[i].chLabel.setTextColors (ltColors);
430 else chBoxes[i].chLabel.setTextColors (txColors);
431 }
432 }
433 }
434
435 // GUI user scales changed notifications
436 else if (obj->body.otype == uris.notify_scaleMapsEvent)
437 {
438 int iD = 0;
439 int scaleNr = 0;
440
441 LV2_Atom *oId = NULL, *oName = NULL, *oElements = NULL, *oAltSymbols = NULL, *oScale = NULL;
442 lv2_atom_object_get
443 (
444 obj, uris.notify_scaleID, &oId,
445 uris.notify_scaleName, &oName,
446 uris.notify_scaleElements, &oElements,
447 uris.notify_scaleAltSymbols, &oAltSymbols,
448 uris.notify_scale, &oScale,
449 NULL
450 );
451
452 if (oId && (oId->type == uris.atom_Int))
453 {
454 iD = ((LV2_Atom_Int*)oId)->body;
455 for (int i = 0; i < NR_SYSTEM_SCALES + NR_USER_SCALES; ++i)
456 {
457 if (iD == scaleMaps[i].iD)
458 {
459 scaleNr = i;
460 break;
461 }
462 }
463 }
464
465 if ((scaleNr >= NR_SYSTEM_SCALES) && (scaleNr < NR_SYSTEM_SCALES + NR_USER_SCALES))
466 {
467 // Name
468 if (oName && (oName->type == uris.atom_String))
469 {
470 scaleMaps[scaleNr].name = std::string ((char*) LV2_ATOM_BODY(oName));
471 std::string s = (char*) LV2_ATOM_BODY(oName);
472 if (propertiesScaleListBox.getItemList())
473 {
474 BItems::ItemList::iterator it = std::next (propertiesScaleListBox.getItemList()->begin (), scaleNr);
475 BWidgets::Label* l = (BWidgets::Label*) (*it).getWidget ();
476 if (l) l->setText (s);
477 propertiesScaleListBox.update();
478 }
479 }
480
481 // Elements TODO exceptions
482 if (oElements && (oElements->type == uris.atom_Vector))
483 {
484 const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oElements;
485 if (vec->body.child_type == uris.atom_Int)
486 {
487 memcpy (scaleMaps[scaleNr].elements.data(), &vec->body + 1, 16 * sizeof (int));
488 }
489 }
490
491 // Alt Symbols TODO exceptions
492 if (oAltSymbols && (oAltSymbols->type == uris.atom_Vector))
493 {
494 const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oAltSymbols;
495 if (vec->body.child_type == uris.atom_String)
496 {
497 char rtAltSymbols[16][16];
498 memcpy (rtAltSymbols, (&vec->body + 1), 16 * 16);
499
500 for (size_t r = 0; r < ROWS; ++r)
501 {
502 scaleMaps[scaleNr].altSymbols[r] = std::string (rtAltSymbols[r]);
503 }
504 }
505 }
506
507 // Scale TODO exceptions
508 if (oScale && (oScale->type == uris.atom_Vector))
509 {
510 const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oScale;
511 if (vec->body.child_type == uris.atom_Int)
512 {
513 BScaleNotes* notes = (BScaleNotes*) (&vec->body + 1);
514 scaleMaps[scaleNr].scaleNotes = *notes;
515 }
516 }
517
518 if (scaleNr == controllers[SCALE]) drawCaption ();
519
520 }
521 }
522 }
523 }
524
525 // Scan remaining ports
526 else if ((format == 0) && (port >= KNOBS))
527 {
528 float* pval = (float*) buffer;
529 controllerWidgets[port-KNOBS]->setValue (*pval);
530 }
531
532 }
533
scale()534 void BSEQuencer_GUI::scale ()
535 {
536 hide ();
537 //Scale fonts
538 ctLabelFont.setFontSize (12 * sz);
539 tgLabelFont.setFontSize (12 * sz);
540 iLabelFont.setFontSize (18 * sz);
541 lfLabelFont.setFontSize (12 * sz);
542
543 //Background
544 cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1250 * sz, 820 * sz);
545 cairo_t* cr = cairo_create (surface);
546 cairo_scale (cr, sz, sz);
547 cairo_set_source_surface(cr, bgImageSurface, 0, 0);
548 cairo_paint(cr);
549 widgetBg.loadFillFromCairoSurface(surface);
550 cairo_destroy (cr);
551 cairo_surface_destroy (surface);
552
553 //Scale widgets
554 RESIZE (mContainer, 0, 0, 1250, 820, sz);
555 RESIZE (padSurface, 98, 88, 804, 484, sz);
556
557 //RESIZE (padSurfaceFocusText, 0, 0, 100, 60, sz);
558 scaleFocus ();
559 RESIZE (captionSurface, 18, 88, 64, 484, sz);
560
561 RESIZE (modeBox, 920, 88, 310, 180, sz);
562 RESIZE (modeBoxLabel, 10, 10, 290, 20, sz);
563 RESIZE (modeLabel, 10, 80, 60, 20, sz);
564 RESIZE (modeListBox, 80, 80, 220, 20, sz);
565 modeListBox.resizeListBox(BUtilities::Point (220 * sz, 80 * sz));
566 modeListBox.resizeListBoxItems(BUtilities::Point (220 * sz, 20 * sz));
567 RESIZE (modeAutoplayBpmLabel, 10, 115, 120, 20, sz);
568 RESIZE (modeAutoplayBpmSlider, 120, 105, 180, 25, sz);
569 RESIZE (modeAutoplayBpbLabel, 10, 145, 120, 20, sz);
570 RESIZE (modeAutoplayBpbSlider, 120, 135, 180, 25, sz);
571 RESIZE (modeMidiInChannelLabel, 10, 110, 130, 20, sz);
572 RESIZE (modeMidiInChannelListBox, 180, 110, 120, 20, sz);
573 modeMidiInChannelListBox.resizeListBox (BUtilities::Point (120 * sz, 200 * sz));
574 modeMidiInChannelListBox.resizeListBoxItems (BUtilities::Point (120 * sz, 20 * sz));
575 RESIZE (modeOnKeyLabel, 10, 140, 100, 20, sz);
576 RESIZE (modeOnKeyListBox, 180, 140, 120, 20, sz);
577 modeOnKeyListBox.resizeListBox (BUtilities::Point (120 * sz, 80 * sz));
578 modeOnKeyListBox.resizeListBoxItems (BUtilities::Point (120 * sz, 20 * sz));
579 RESIZE (modePlayLabel, 10, 50, 205, 20, sz);
580 RESIZE (modePlayButton, 270, 40, 30, 30, sz);
581
582 RESIZE (toolBox, 920, 280, 310, 292, sz);
583 RESIZE (toolBoxLabel, 10, 10, 290, 20, sz);
584 RESIZE (toolButtonBox, 0, 30, 310, 160, sz);
585 RESIZE (toolWholeStepButton, 210, 40, 80, 20, sz);
586 RESIZE (toolResetButton, 90, 130, 20, 20, sz);
587 RESIZE (toolUndoButton, 120, 130, 20, 20, sz);
588 RESIZE (toolRedoButton, 150, 130, 20, 20, sz);
589 RESIZE (toolButtonBoxCtrlLabel, 10, 10, 80, 20, sz);
590 RESIZE (toolButtonBoxChLabel, 10, 70, 80, 20, sz);
591 RESIZE (toolButtonBoxEditLabel, 10, 100, 80, 20, sz);
592 RESIZE (toolGateLabel, 10, 260, 30, 20, sz);
593 RESIZE (toolGateSlider, 12.5, 200, 25, 60, sz);
594 RESIZE (toolNoteLabel, 45, 260, 60, 20, sz);
595 RESIZE (toolNoteDial, 50, 200, 50, 60, sz);
596 RESIZE (toolOctaveLabel, 110, 260, 60, 20, sz);
597 RESIZE (toolOctaveDial, 115, 200, 50, 60, sz);
598 RESIZE (toolVelocityLabel, 175, 260, 60, 20, sz);
599 RESIZE (toolVelocityDial, 180, 200, 50, 60, sz);
600 RESIZE (toolDurationLabel, 240, 260, 60, 20, sz);
601 RESIZE (toolDurationDial, 245, 200, 50, 60, sz);
602
603 RESIZE (propertiesBox, 920, 590, 310, 210, sz);
604 RESIZE (propertiesBoxLabel, 10, 10, 290, 20, sz);
605 RESIZE (propertiesNrStepsLabel, 10, 50, 170, 20, sz);
606 RESIZE (propertiesNrStepsListBox, 210, 50, 90, 20, sz);
607 propertiesNrStepsListBox.resizeListBox (BUtilities::Point (90 * sz, 100 * sz));
608 propertiesNrStepsListBox.resizeListBoxItems (BUtilities::Point (90 * sz, 20 * sz));
609 RESIZE (propertiesStepsPerSlider, 10, 75, 90, 25, sz);
610 RESIZE (propertiesStepsPerLabel, 110, 85, 80, 20, sz);
611 RESIZE (propertiesBaseListBox, 210, 85, 90, 20, sz);
612 propertiesBaseListBox.resizeListBox (BUtilities::Point (90 * sz, 60 * sz));
613 propertiesBaseListBox.resizeListBoxItems (BUtilities::Point (90 * sz, 20 * sz));
614 RESIZE (propertiesRootLabel, 10, 115, 60, 20, sz);
615 RESIZE (propertiesRootListBox, 110, 115, 90, 20, sz);
616 propertiesRootListBox.resizeListBox (BUtilities::Point (90 * sz, 160 * sz));
617 propertiesRootListBox.moveListBox (BUtilities::Point (0, -160 * sz));
618 propertiesRootListBox.resizeListBoxItems (BUtilities::Point (90 * sz, 20 * sz));
619 RESIZE (propertiesSignatureListBox, 210, 115, 90, 20, sz);
620 propertiesSignatureListBox.resizeListBox (BUtilities::Point (90 * sz, 80 * sz));
621 propertiesSignatureListBox.resizeListBoxItems (BUtilities::Point (90 * sz, 20 * sz));
622 RESIZE (propertiesOctaveLabel, 10, 145, 65, 20, sz);
623 RESIZE (propertiesOctaveListBox, 210, 145, 90, 20, sz);
624 propertiesOctaveListBox.resizeListBox (BUtilities::Point (90 * sz, 220 * sz));
625 propertiesOctaveListBox.moveListBox (BUtilities::Point (0, -220 * sz));
626 propertiesOctaveListBox.resizeListBoxItems (BUtilities::Point (90 * sz, 20 * sz));
627 RESIZE (propertiesScaleLabel, 10, 175, 60, 20, sz);
628 RESIZE (propertiesScaleEditIcon, 70, 175, 20, 20, sz);
629 RESIZE (propertiesScaleListBox, 100, 175, 200, 20, sz);
630 propertiesScaleListBox.resizeListBox (BUtilities::Point (200 * sz, 420 * sz));
631 propertiesScaleListBox.moveListBox (BUtilities::Point (0, -420 * sz));
632 propertiesScaleListBox.resizeListBoxItems (BUtilities::Point (200 * sz, 20 * sz));
633
634 RESIZE (helpButton, 1166, 50, 24, 24, sz);
635 RESIZE (ytButton, 1196, 50, 24, 24, sz);
636 if (scaleEditor) {RESIZE ((*scaleEditor), 420, 20, 360, 760, sz);}
637
638 for (int i = 0; i < NR_SEQUENCER_CHS; ++i)
639 {
640 RESIZE (chBoxes[i].box, 98 + i * 203.5, 590, 193.5, 210, sz);
641 RESIZE (chBoxes[i].chSymbol, 7, 7, 26, 26, sz);
642 RESIZE (chBoxes[i].chLabel, 40, 10, 133.5, 20, sz);
643 RESIZE (chBoxes[i].channelLabel, 10, 50, 100, 20, sz);
644 RESIZE (chBoxes[i].channelListBox, 123.5, 50, 60, 20, sz);
645 chBoxes[i].channelListBox.resizeListBox (BUtilities::Point (60 * sz, 120 * sz));
646 chBoxes[i].channelListBox.resizeListBoxItems (BUtilities::Point (60 * sz, 20 * sz));
647 RESIZE (chBoxes[i].pitchLabel, 10, 80, 100, 20, sz);
648 RESIZE (chBoxes[i].pitchSwitch, 132.5, 82, 42, 16, sz);
649 RESIZE (chBoxes[i].pitchScreen, 10, 80, 173.5, 20, sz);
650 RESIZE (chBoxes[i].velocityDial, 25, 120, 50, 60, sz);
651 RESIZE (chBoxes[i].velocityLabel, 20, 180, 60, 20, sz);
652 RESIZE (chBoxes[i].noteOffsetDial, 118.5, 120, 50, 60, sz);
653 RESIZE (chBoxes[i].noteOffsetLabel, 113.5, 180, 60, 20, sz);
654
655 drawButton
656 (
657 chBoxes[i].chSymbol.getDrawingSurface(),
658 0, 0,
659 chBoxes[i].chSymbol.getEffectiveWidth(),
660 chBoxes[i].chSymbol.getEffectiveHeight(),
661 chButtonStyles[i + 1].color,
662 chButtonStyles[i + 1].symbol
663 );
664 }
665 applyTheme (theme);
666 drawCaption ();
667 drawPad ();
668 show ();
669 }
670
scaleFocus()671 void BSEQuencer_GUI::scaleFocus ()
672 {
673 cairo_surface_t* surface = padSurface.getDrawingSurface();
674 cairo_t* cr = cairo_create (surface);
675
676 padSurface.focusText.resize (400,100); // Maximize size first to omit breaks
677 std::vector<std::string> textblock = padSurface.focusText.getTextBlock ();
678 double blockheight = padSurface.focusText.getTextBlockHeight (textblock);
679 double blockwidth = 0.0;
680 for (std::string textline : textblock)
681 {
682 cairo_text_extents_t ext = padSurface.focusText.getFont ()->getTextExtents (cr, textline);
683 if (ext.width > blockwidth) blockwidth = ext.width;
684 }
685 padSurface.focusText.resize (blockwidth + 2 * padSurface.focusText.getXOffset (), blockheight + 2 * padSurface.focusText.getYOffset ());
686
687 padSurface.focusText.resize();
688
689 cairo_destroy (cr);
690 }
691
applyTheme(BStyles::Theme & theme)692 void BSEQuencer_GUI::applyTheme (BStyles::Theme& theme)
693 {
694 mContainer.applyTheme (theme);
695
696 padSurface.applyTheme (theme);
697 captionSurface.applyTheme (theme);
698
699 modeBox.applyTheme (theme);
700 modeBoxLabel.applyTheme (theme);
701 modeLabel.applyTheme (theme);
702 modeListBox.applyTheme (theme);
703 modeAutoplayBpmLabel.applyTheme (theme);
704 modeAutoplayBpmSlider.applyTheme (theme);
705 modeAutoplayBpbLabel.applyTheme (theme);
706 modeAutoplayBpbSlider.applyTheme (theme);
707 modeMidiInChannelLabel.applyTheme (theme);
708 modeMidiInChannelListBox.applyTheme (theme);
709 modeOnKeyLabel.applyTheme (theme);
710 modeOnKeyListBox.applyTheme (theme);
711 modePlayLabel.applyTheme (theme);
712 modePlayButton.applyTheme (theme);
713
714 toolBox.applyTheme (theme);
715 toolBoxLabel.applyTheme (theme);
716 toolButtonBox.applyTheme (theme);
717 toolWholeStepButton.applyTheme (theme);
718 toolResetButton.applyTheme (theme);
719 toolUndoButton.applyTheme (theme);
720 toolRedoButton.applyTheme (theme);
721 toolButtonBoxCtrlLabel.applyTheme (theme);
722 toolButtonBoxChLabel.applyTheme (theme);
723 toolButtonBoxEditLabel.applyTheme (theme);
724 toolGateLabel.applyTheme (theme);
725 toolGateSlider.applyTheme (theme);
726 toolNoteLabel.applyTheme (theme);
727 toolNoteDial.applyTheme (theme);
728 toolOctaveLabel.applyTheme (theme);
729 toolOctaveDial.applyTheme (theme);
730 toolVelocityLabel.applyTheme (theme);
731 toolVelocityDial.applyTheme (theme);
732 toolDurationLabel.applyTheme (theme);
733 toolDurationDial.applyTheme (theme);
734
735 propertiesBox.applyTheme (theme);
736 propertiesNrStepsLabel.applyTheme (theme);
737 propertiesNrStepsListBox.applyTheme (theme);
738 propertiesBoxLabel.applyTheme (theme);
739 propertiesStepsPerLabel.applyTheme (theme);
740 propertiesStepsPerSlider.applyTheme (theme);
741 propertiesBaseListBox.applyTheme (theme);
742 propertiesRootLabel.applyTheme (theme);
743 propertiesRootListBox.applyTheme (theme);
744 propertiesSignatureListBox.applyTheme (theme);
745 propertiesOctaveLabel.applyTheme (theme);
746 propertiesOctaveListBox.applyTheme (theme);
747 propertiesScaleLabel.applyTheme (theme);
748 propertiesScaleEditIcon.applyTheme (theme);
749 propertiesScaleListBox.applyTheme (theme);
750
751 for (int i = 0; i < NR_SEQUENCER_CHS; ++i)
752 {
753 chBoxes[i].box.applyTheme (theme);
754 chBoxes[i].chSymbol.applyTheme (theme);
755 chBoxes[i].chLabel.applyTheme (theme);
756 chBoxes[i].channelLabel.applyTheme (theme);
757 chBoxes[i].channelListBox.applyTheme (theme);
758 chBoxes[i].pitchLabel.applyTheme (theme);
759 chBoxes[i].pitchSwitch.applyTheme (theme);
760 chBoxes[i].pitchScreen.applyTheme (theme);
761 chBoxes[i].velocityDial.applyTheme (theme);
762 chBoxes[i].velocityLabel.applyTheme (theme);
763 chBoxes[i].noteOffsetDial.applyTheme (theme);
764 chBoxes[i].noteOffsetLabel.applyTheme (theme);
765 }
766
767 helpButton.applyTheme (theme);
768 ytButton.applyTheme (theme);
769 //scaleEditor.applyTheme (theme);
770 }
771
onConfigureRequest(BEvents::ExposeEvent * event)772 void BSEQuencer_GUI::onConfigureRequest (BEvents::ExposeEvent* event)
773 {
774 Window::onConfigureRequest (event);
775
776 sz = (getWidth() / 1250 > getHeight() / 820 ? getHeight() / 820 : getWidth() / 1250);
777 scale ();
778 }
779
onCloseRequest(BEvents::WidgetEvent * event)780 void BSEQuencer_GUI::onCloseRequest (BEvents::WidgetEvent* event)
781 {
782 if (event && (scaleEditor) && (event->getRequestWidget () == scaleEditor))
783 {
784 int mapNr = scaleEditor->getMapNr();
785 scaleMaps[mapNr] = scaleEditor->getScaleMap();
786
787 // Update captions
788 if (controllers[SCALE] == mapNr) drawCaption();
789
790 // Update scale listbox
791 BItems::ItemList::iterator it = std::next (propertiesScaleListBox.getItemList()->begin (), mapNr);
792 BWidgets::Label* l = (BWidgets::Label*) (*it).getWidget ();
793 if (l) l->setText (scaleMaps[mapNr].name);
794 propertiesScaleListBox.update();
795
796 // Notify plugin
797 send_scaleMaps (mapNr);
798
799 // And close / delete
800 delete scaleEditor;
801 scaleEditor = nullptr;
802 }
803 else Window::onCloseRequest (event);
804 }
805
onKeyPressed(BEvents::KeyEvent * event)806 void BSEQuencer_GUI::onKeyPressed (BEvents::KeyEvent* event)
807 {
808 if (!event) return;
809 if (event->getKey() == BDevices::KEY_SHIFT) modifier = MODIFIER_DURATION;
810 else if (event->getKey() == BDevices::KEY_CTRL) modifier = MODIFIER_OCTAVE;
811 }
812
onKeyReleased(BEvents::KeyEvent * event)813 void BSEQuencer_GUI::onKeyReleased (BEvents::KeyEvent* event)
814 {
815 if ((event) && ((event->getKey() == BDevices::KEY_SHIFT) || (event->getKey() == BDevices::KEY_CTRL))) modifier = MODIFIER_VELOCITY;
816 }
817
send_ui_on()818 void BSEQuencer_GUI::send_ui_on ()
819 {
820 uint8_t obj_buf[64];
821 lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
822
823 LV2_Atom_Forge_Frame frame;
824 LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, uris.ui_on);
825 lv2_atom_forge_pop(&forge, &frame);
826 write_function(controller, INPUT, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
827 }
828
send_ui_off()829 void BSEQuencer_GUI::send_ui_off ()
830 {
831 uint8_t obj_buf[64];
832 lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
833
834 LV2_Atom_Forge_Frame frame;
835 LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, uris.ui_off);
836 lv2_atom_forge_pop(&forge, &frame);
837 write_function(controller, INPUT, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
838 }
839
send_pad(int row,int step)840 void BSEQuencer_GUI::send_pad (int row, int step)
841 {
842 PadMessage padmsg (step, row, pattern.getPad (row, step));
843
844 uint8_t obj_buf[128];
845 lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
846
847 LV2_Atom_Forge_Frame frame;
848 LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, uris.notify_padEvent);
849 lv2_atom_forge_key(&forge, uris.notify_pad);
850 lv2_atom_forge_vector(&forge, sizeof(float), uris.atom_Float, sizeof(PadMessage) / sizeof(float), (void*) &padmsg);
851 lv2_atom_forge_pop(&forge, &frame);
852 write_function(controller, INPUT, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
853 }
854
send_scaleMaps(int scaleNr)855 void BSEQuencer_GUI::send_scaleMaps (int scaleNr)
856 {
857 RTScaleMap rtScaleMap; rtScaleMap = scaleMaps[scaleNr];
858 uint8_t obj_buf[2048];
859 lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
860
861 LV2_Atom_Forge_Frame frame;
862 LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, uris.notify_scaleMapsEvent);
863 lv2_atom_forge_key(&forge, uris.notify_scaleID);
864 lv2_atom_forge_int(&forge, rtScaleMap.iD);
865 lv2_atom_forge_key(&forge, uris.notify_scaleName);
866 lv2_atom_forge_string (&forge, rtScaleMap.name, 64);
867 lv2_atom_forge_key(&forge, uris.notify_scaleElements);
868 lv2_atom_forge_vector(&forge, sizeof (int), uris.atom_Int, 16, (void*) rtScaleMap.elements);
869 lv2_atom_forge_key(&forge, uris.notify_scaleAltSymbols);
870 lv2_atom_forge_vector(&forge, 16, uris.atom_String, 16, (void*) rtScaleMap.altSymbols);
871 lv2_atom_forge_key(&forge, uris.notify_scale);
872 BScaleNotes* notes = &rtScaleMap.scaleNotes;
873 lv2_atom_forge_vector(&forge, sizeof (int), uris.atom_Int, 12, (void*) notes);
874 lv2_atom_forge_pop(&forge, &frame);
875 write_function(controller, INPUT, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
876 }
877
valueChangedCallback(BEvents::Event * event)878 void BSEQuencer_GUI::valueChangedCallback(BEvents::Event* event)
879 {
880 if ((event) && (event->getWidget ()))
881 {
882 BWidgets::ValueWidget* widget = (BWidgets::ValueWidget*) event->getWidget ();
883 float value = widget->getValue();
884
885 if (widget->getMainWindow())
886 {
887 BSEQuencer_GUI* ui = (BSEQuencer_GUI*) widget->getMainWindow();
888 int widgetNr = -1;
889
890 for (int i = 0; i < KNOBS_SIZE; ++i)
891 {
892 if (widget == ui->controllerWidgets[i])
893 {
894 widgetNr = i;
895 break;
896 }
897 }
898
899 if (widgetNr >= 0)
900 {
901 //std::cerr << "BSEQuencer.lv2#GUI valueChangedCallback() for widget #" << widgetNr << ": " << value << "\n";
902 ui->controllers[widgetNr] = value;
903 ui->write_function(ui->controller, KNOBS + widgetNr, sizeof(float), 0, &ui->controllers[widgetNr]);
904
905 // Playing status changed
906 if (widgetNr == PLAY)
907 {
908 if (value) ui->modePlayLabel.setText (BSEQUENCER_LABEL_STATUS_PLAYING);
909 else ui->modePlayLabel.setText (BSEQUENCER_LABEL_STATUS_STOPPED);
910 }
911
912 // Mode changed
913 if (widgetNr == MODE)
914 {
915 if (value == AUTOPLAY)
916 {
917 ui->modeAutoplayBpmLabel.show ();
918 ui->modeAutoplayBpmSlider.show ();
919 ui->modeAutoplayBpbLabel.show ();
920 ui->modeAutoplayBpbSlider.show ();
921 ui->modeMidiInChannelLabel.hide ();
922 ui->modeMidiInChannelListBox.hide ();
923 ui->modeOnKeyLabel.hide ();
924 ui->modeOnKeyListBox.hide ();
925 for (int i = 0; i < NR_SEQUENCER_CHS; ++i) ui->chBoxes[i].pitchScreen.show ();
926 }
927
928 else if (value == HOST_CONTROLLED)
929 {
930 ui->modeAutoplayBpmLabel.hide ();
931 ui->modeAutoplayBpmSlider.hide ();
932 ui->modeAutoplayBpbLabel.hide ();
933 ui->modeAutoplayBpbSlider.hide ();
934 ui->modeMidiInChannelLabel.show ();
935 ui->modeMidiInChannelListBox.show ();
936 ui->modeOnKeyLabel.show ();
937 ui->modeOnKeyListBox.show ();
938 for (int i = 0; i < NR_SEQUENCER_CHS; ++i) ui->chBoxes[i].pitchScreen.hide ();
939 }
940
941 else
942 {
943 ui->modeAutoplayBpmLabel.hide ();
944 ui->modeAutoplayBpmSlider.hide ();
945 ui->modeAutoplayBpbLabel.hide ();
946 ui->modeAutoplayBpbSlider.hide ();
947 ui->modeMidiInChannelLabel.hide ();
948 ui->modeMidiInChannelListBox.hide ();
949 ui->modeOnKeyLabel.hide ();
950 ui->modeOnKeyListBox.hide ();
951 for (int i = 0; i < NR_SEQUENCER_CHS; ++i) ui->chBoxes[i].pitchScreen.show ();
952 }
953 }
954
955 // Scale changed
956 if (widgetNr == SCALE)
957 {
958 if (value < NR_SYSTEM_SCALES) ui->propertiesScaleEditIcon.hide();
959 else ui->propertiesScaleEditIcon.show();
960 }
961
962 // Pad relevant changes
963 if ((widgetNr == NR_OF_STEPS) || (widgetNr == STEPS_PER) ||(widgetNr == ROOT) || (widgetNr == SIGNATURE) ||
964 (widgetNr == SCALE)) ui->drawPad ();
965
966 // Caption relevant changes
967 if ((widgetNr == ROOT) || (widgetNr == SIGNATURE) || (widgetNr == SCALE)) ui->drawCaption ();
968
969 // Range dials: also update parents
970 if
971 (
972 (widgetNr == SELECTION_NOTE_RAND) ||
973 (widgetNr == SELECTION_OCTAVE_RAND) ||
974 (widgetNr == SELECTION_VELOCITY_RAND) ||
975 (widgetNr == SELECTION_DURATION_RAND)
976 )
977 {
978 if (widget->getParent()) widget->getParent()->update();
979 }
980 }
981 }
982 }
983 }
984
helpPressedCallback(BEvents::Event * event)985 void BSEQuencer_GUI::helpPressedCallback (BEvents::Event* event)
986 {
987 char cmd[] = WWW_BROWSER_CMD;
988 char param[] = HELP_URL;
989 char* argv[] = {cmd, param, NULL};
990 std::cerr << "BSEQuencer.lv2#GUI: Call " << HELP_URL << " for help.\n";
991 if (BUtilities::vsystem (argv) == -1) std::cerr << "BSEQuencer.lv2#GUI: Couldn't fork.\n";
992 }
993
ytPressedCallback(BEvents::Event * event)994 void BSEQuencer_GUI::ytPressedCallback (BEvents::Event* event)
995 {
996 char cmd[] = WWW_BROWSER_CMD;
997 char param[] = YT_URL;
998 char* argv[] = {cmd, param, NULL};
999 std::cerr << "BSEQuencer.lv2#GUI: Call " << YT_URL << " for tutorial video.\n";
1000 if (BUtilities::vsystem (argv) == -1) std::cerr << "BSEQuencer.lv2#GUI: Couldn't fork.\n";
1001 }
1002
editPressedCallback(BEvents::Event * event)1003 void BSEQuencer_GUI::editPressedCallback (BEvents::Event* event)
1004 {
1005 if ((event) && (event->getWidget ()) && (event->getWidget()->getMainWindow()))
1006 {
1007 BSEQuencer_GUI* ui = (BSEQuencer_GUI*)(((BWidgets::Widget*)(event->getWidget()))->getMainWindow());
1008 int mapNr = ui->propertiesScaleListBox.getValue();
1009
1010 if (!ui->scaleEditor)
1011 {
1012 ui->scaleEditor = new ScaleEditor
1013 (
1014 420 * ui->sz, 20 * ui->sz, 360 * ui->sz, 760 * ui->sz, "scaleeditor",
1015 ui->pluginPath,
1016 mapNr,
1017 ui->scaleMaps[mapNr],
1018 BScale
1019 (
1020 (int (ui->controllers[ROOT] + ui->controllers[SIGNATURE] + 12)) % 12,
1021 (SignatureIndex) ui->controllers[SIGNATURE],
1022 ui->scaleMaps[mapNr].scaleNotes
1023 )
1024 );
1025 ui->add (*ui->scaleEditor);
1026 }
1027 }
1028 }
1029
resetClickedCallback(BEvents::Event * event)1030 void BSEQuencer_GUI::resetClickedCallback (BEvents::Event* event)
1031 {
1032 if ((event) && (event->getWidget ()) && (event->getWidget()->getMainWindow()))
1033 {
1034 BWidgets::Button* widget = (BWidgets::Button*)event->getWidget();
1035 BSEQuencer_GUI* ui = (BSEQuencer_GUI*)(widget->getMainWindow());
1036 double value = ((BEvents::ValueChangedEvent*) event)->getValue ();
1037
1038 if ((value == 1) && (widget == (BWidgets::Button*)&ui->toolResetButton))
1039 {
1040 if (ui->wheelScrolled)
1041 {
1042 ui->pattern.store ();
1043 ui->wheelScrolled = false;
1044 }
1045
1046 Pad p0 = Pad ();
1047 for (int r = 0; r < ROWS; ++r)
1048 {
1049 for (int s = 0; s < MAXSTEPS; ++s)
1050 {
1051 ui->pattern.setPad (r, s, p0);
1052 ui->send_pad (r, s);
1053 }
1054 }
1055
1056 ui->drawPad ();
1057 ui->pattern.store ();
1058 }
1059 }
1060 }
1061
undoClickedCallback(BEvents::Event * event)1062 void BSEQuencer_GUI::undoClickedCallback (BEvents::Event* event)
1063 {
1064 if ((event) && (event->getWidget ()) && (event->getWidget()->getMainWindow()))
1065 {
1066 BWidgets::Button* widget = (BWidgets::Button*)event->getWidget();
1067 BSEQuencer_GUI* ui = (BSEQuencer_GUI*)(widget->getMainWindow());
1068 double value = ((BEvents::ValueChangedEvent*) event)->getValue ();
1069
1070 if (value == 1)
1071 {
1072 if ((widget == (BWidgets::Button*)&ui->toolUndoButton) || (widget == (BWidgets::Button*)&ui->toolRedoButton))
1073 {
1074 std::vector<PadMessage> padMessages = (widget == (BWidgets::Button*)&ui->toolUndoButton ? ui->pattern.undo () : ui->pattern.redo ());
1075 for (PadMessage const& p : padMessages)
1076 {
1077 size_t r = LIMIT (p.row, 0, ROWS);
1078 size_t s = LIMIT (p.step, 0, MAXSTEPS);
1079 ui->send_pad (r, s);
1080 }
1081 ui->drawPad ();
1082 }
1083 }
1084 }
1085 }
1086
padsPressedCallback(BEvents::Event * event)1087 void BSEQuencer_GUI::padsPressedCallback (BEvents::Event* event)
1088 {
1089 if ((event) && (event->getWidget ()) && (event->getWidget()->getMainWindow()) &&
1090 ((event->getEventType () == BEvents::BUTTON_PRESS_EVENT) ||
1091 (event->getEventType () == BEvents::BUTTON_RELEASE_EVENT) ||
1092 (event->getEventType () == BEvents::POINTER_DRAG_EVENT)))
1093 {
1094 BWidgets::DrawingSurface* widget = (BWidgets::DrawingSurface*) event->getWidget ();
1095 BSEQuencer_GUI* ui = (BSEQuencer_GUI*) widget->getMainWindow();
1096 BEvents::PointerEvent* pointerEvent = (BEvents::PointerEvent*) event;
1097
1098 if (ui->wheelScrolled)
1099 {
1100 ui->pattern.store ();
1101 ui->wheelScrolled = false;
1102 }
1103
1104 // Get size of drawing area
1105 const double width = ui->padSurface.getEffectiveWidth ();
1106 const double height = ui->padSurface.getEffectiveHeight ();
1107
1108 int row = (ROWS - 1) - ((int) ((pointerEvent->getPosition ().y - widget->getYOffset()) / (height / ROWS)));
1109 int step = (pointerEvent->getPosition ().x - widget->getXOffset()) / (width / ui->controllerWidgets[NR_OF_STEPS]->getValue ());
1110
1111 if ((event->getEventType () == BEvents::BUTTON_PRESS_EVENT) || (event->getEventType () == BEvents::POINTER_DRAG_EVENT))
1112 {
1113
1114 if ((row >= 0) && (row < ROWS) && (step >= 0) && (step < (int (ui->controllerWidgets[NR_OF_STEPS]->getValue ()))))
1115 {
1116 int start = step;
1117 while (ui->pattern.padHasAntecessor (row, start)) --start;
1118 Pad pd = ui->pattern.getPad (row, start);
1119 int pdctrl = (int (pd.ch) & 0xF0) / 0x10;
1120
1121 // Left button: apply properties to pad
1122 if (pointerEvent->getButton() == BDevices::LEFT_BUTTON)
1123 {
1124 if (ui->controllerWidgets[SELECTION_CH]->getValue() <= NR_SEQUENCER_CHS)
1125 {
1126 Pad props
1127 (
1128 ui->controllerWidgets[SELECTION_CH]->getValue() + pdctrl * 0x10,
1129 ui->controllerWidgets[SELECTION_NOTE]->getValue(),
1130 ui->controllerWidgets[SELECTION_OCTAVE]->getValue(),
1131 ui->controllerWidgets[SELECTION_VELOCITY]->getValue(),
1132 ui->controllerWidgets[SELECTION_DURATION]->getValue() + int (pd.duration - 0.00000001),
1133 ui->controllerWidgets[SELECTION_GATE_RAND]->getValue(),
1134 ui->controllerWidgets[SELECTION_NOTE_RAND]->getValue(),
1135 ui->controllerWidgets[SELECTION_OCTAVE_RAND]->getValue(),
1136 ui->controllerWidgets[SELECTION_VELOCITY_RAND]->getValue(),
1137 ui->controllerWidgets[SELECTION_DURATION_RAND]->getValue()
1138 );
1139
1140 // Delete ch if duration == 0
1141 if (props.duration == 0.0f) props.ch = pdctrl * 0x10;
1142
1143 // Click on a pad with same settings as in toolbox => temporarily switch to delete
1144 if ((props == pd) && (!ui->tempTool) && (event->getEventType () == BEvents::BUTTON_PRESS_EVENT))
1145 {
1146 ui->tempTool = true;
1147 ui->tempToolCh = ui->controllerWidgets[SELECTION_CH]->getValue();
1148 props.ch = pdctrl * 0x10;
1149 ui->controllerWidgets[SELECTION_CH]->setValue (0);
1150 }
1151
1152 // Overwrite if new data
1153 if (!(props == pd))
1154 {
1155 int s = 0;
1156 float d = props.duration;
1157 do
1158 {
1159 props.duration = ((int (props.ch) & 0x0F) == 0 ? ui->controllerWidgets[SELECTION_DURATION]->getValue() : d);
1160 ui->pattern.setPad (row, start + s, props);
1161 ui->drawPad (row, start + s);
1162 ui->send_pad (row, start + s);
1163 --d;
1164 ++s;
1165 } while (d > 0.0);
1166 }
1167 }
1168
1169 // CTRL function set
1170 else if (ui->controllerWidgets[SELECTION_CH]->getValue() <= NR_SEQUENCER_CHS + NR_CTRL_BUTTONS)
1171 {
1172 int ctrl = ((int)ui->controllerWidgets[SELECTION_CH]->getValue() - NR_SEQUENCER_CHS - 1) * 0x10;
1173
1174 // Click on a pad with same settings as in toolbox => temporarily switch to delete
1175 if (((int (pd.ch) & 0xF0) == ctrl) && (!ui->tempTool) && (event->getEventType () == BEvents::BUTTON_PRESS_EVENT))
1176 {
1177 ui->tempTool = true;
1178 ui->tempToolCh = ui->controllerWidgets[SELECTION_CH]->getValue();
1179 ui->controllerWidgets[SELECTION_CH]->setValue(NR_SEQUENCER_CHS + 1);
1180 ctrl = ((int)ui->controllerWidgets[SELECTION_CH]->getValue() - NR_SEQUENCER_CHS - 1) * 0x10;
1181 }
1182
1183 // Apply controller data
1184 // Whole step button pressed ?
1185 int startrow = row;
1186 int endrow = row;
1187 if (ui->toolWholeStepButton.getValue() == 1.0)
1188 {
1189 startrow = 0;
1190 endrow = ROWS - 1;
1191 }
1192
1193 for (int irow = startrow; irow <= endrow; ++irow)
1194 {
1195 int istart = step;
1196 while (ui->pattern.padHasAntecessor (irow, istart)) --istart;
1197
1198 // Overwrite if new data
1199 if ((int (ui->pattern.getPad (irow, istart).ch) & 0xF0) != ctrl)
1200 {
1201 int istep = istart;
1202 do
1203 {
1204 Pad iPad = ui->pattern.getPad (irow, istep);
1205 iPad.ch = (int (iPad.ch) & 0x0F) + (istep == istart ? ctrl : 0);
1206 ui->pattern.setPad (irow, istep, iPad);
1207 ui->drawPad (irow, istep);
1208 ui->send_pad (irow, istep);
1209 ++istep;
1210 } while (ui->pattern.padHasSuccessor (irow, istep - 1));
1211 }
1212 }
1213 }
1214
1215 // Edit mode
1216 else
1217 {
1218 int edit = ((int)ui->controllerWidgets[SELECTION_CH]->getValue() - NR_SEQUENCER_CHS - NR_CTRL_BUTTONS) * 0x100;
1219
1220 if (edit == EDIT_PICK)
1221 {
1222 ui->controllerWidgets[SELECTION_CH]->setValue (int (pd.ch) & 0x0F);
1223 ui->controllerWidgets[SELECTION_OCTAVE]->setValue(pd.pitchOctave);
1224 ui->controllerWidgets[SELECTION_VELOCITY]->setValue(pd.velocity);
1225 ui->controllerWidgets[SELECTION_DURATION]->setValue(pd.duration);
1226 }
1227
1228 else if (edit == EDIT_MERGE)
1229 {
1230 if (ui->clipBoard.ready)
1231 {
1232 ui->clipBoard.origin = std::make_pair (row, start);
1233 ui->clipBoard.extends = std::make_pair (0, ui->pattern.padGetSize (row, start) - 1);
1234 ui->clipBoard.ready = false;
1235 ui->drawPad (row, step);
1236 }
1237
1238 else
1239 {
1240 std::pair<int, int> newExtends = std::make_pair (0, step - ui->clipBoard.origin.second);
1241 if (newExtends != ui->clipBoard.extends)
1242 {
1243 ui->clipBoard.extends = newExtends;
1244 ui->drawPad ();
1245 }
1246 }
1247 }
1248
1249 else if ((edit == EDIT_CUT) || (edit == EDIT_COPY) || (edit == EDIT_FLIPX) || (edit == EDIT_FLIPY))
1250 {
1251 if (ui->clipBoard.ready)
1252 {
1253 ui->clipBoard.origin = std::make_pair (row, start);
1254 ui->clipBoard.extends = std::make_pair (0, ui->pattern.padGetSize (row, start) - 1);
1255 ui->clipBoard.ready = false;
1256 ui->drawPad (row, step);
1257 }
1258
1259 else
1260 {
1261 int end = step;
1262 while (ui->pattern.padHasSuccessor (row, end)) ++end;
1263 std::pair<int, int> newExtends = std::make_pair (row - ui->clipBoard.origin.first, end - ui->clipBoard.origin.second);
1264 if (newExtends != ui->clipBoard.extends)
1265 {
1266 ui->clipBoard.extends = newExtends;
1267 ui->drawPad ();
1268 }
1269 }
1270 }
1271
1272 else if (edit == EDIT_PASTE)
1273 {
1274 if (!ui->clipBoard.data.empty ())
1275 {
1276
1277 for (int r = 0; r < int (ui->clipBoard.data.size ()); ++r)
1278 {
1279 // Clip merged pads
1280 ui->padClip (row - r, step);
1281 if (ui->clipBoard.data[r].size () > 1) ui->padClip (row - r, step + ui->clipBoard.data[r].size ());
1282
1283 for (int s = 0; s < int (ui->clipBoard.data[r].size ()); ++s)
1284 {
1285 if
1286 (
1287 (row - r >= 0) &&
1288 (row - r < ROWS) &&
1289 (step + s >= 0) &&
1290 (step + s < int (ui->controllerWidgets[NR_OF_STEPS]->getValue ()))
1291 )
1292 {
1293 ui->pattern.setPad (row - r, step + s, ui->clipBoard.data.at(r).at(s));
1294 ui->drawPad (row - r, step + s);
1295 ui->send_pad (row - r, step + s);
1296 }
1297 }
1298 }
1299 }
1300 }
1301 }
1302 }
1303
1304 // Right button: copy pad to properties
1305 else if ((pointerEvent->getButton() == BDevices::RIGHT_BUTTON) &&
1306 ((event->getEventType () == BEvents::BUTTON_PRESS_EVENT) ||
1307 (event->getEventType () == BEvents::POINTER_DRAG_EVENT)))
1308 {
1309 ui->controllerWidgets[SELECTION_CH]->setValue (int (pd.ch) & 0x0F);
1310 ui->controllerWidgets[SELECTION_GATE_RAND]->setValue (pd.randGate);
1311 ui->controllerWidgets[SELECTION_NOTE]->setValue (pd.pitchNote);
1312 ui->controllerWidgets[SELECTION_OCTAVE]->setValue (pd.pitchOctave);
1313 ui->controllerWidgets[SELECTION_VELOCITY]->setValue (pd.velocity);
1314 ui->controllerWidgets[SELECTION_DURATION]->setValue (LIMIT (pd.duration, 0.0, 1.0));
1315 ui->controllerWidgets[SELECTION_NOTE_RAND]->setValue (pd.randNote);
1316 ui->controllerWidgets[SELECTION_OCTAVE_RAND]->setValue (pd.randOctave);
1317 ui->controllerWidgets[SELECTION_VELOCITY_RAND]->setValue (pd.randVelocity);
1318 ui->controllerWidgets[SELECTION_DURATION_RAND]->setValue (pd.randDuration);
1319 }
1320 }
1321 }
1322
1323 else if ((event->getEventType () == BEvents::BUTTON_RELEASE_EVENT) && (pointerEvent->getButton() == BDevices::LEFT_BUTTON))
1324 {
1325 // Edit mode
1326 if (ui->controllerWidgets[SELECTION_CH]->getValue() > NR_SEQUENCER_CHS + NR_CTRL_BUTTONS)
1327 {
1328 int edit = ((int)ui->controllerWidgets[SELECTION_CH]->getValue() - NR_SEQUENCER_CHS - NR_CTRL_BUTTONS) * 0x100;
1329
1330 if (edit == EDIT_MERGE)
1331 {
1332 int clipRMin = ui->clipBoard.origin.first;
1333 int clipRMax = ui->clipBoard.origin.first + ui->clipBoard.extends.first;
1334 if (clipRMin > clipRMax) std::swap (clipRMin, clipRMax);
1335 int clipSMin = ui->clipBoard.origin.second;
1336 int clipSMax = ui->clipBoard.origin.second + ui->clipBoard.extends.second;
1337 if (clipSMin > clipSMax) std::swap (clipSMin, clipSMax);
1338
1339
1340 // Split
1341 if (ui->pattern.padGetSize (clipRMin, clipSMin) > 1)
1342 {
1343 // Find start
1344 int start = clipSMin;
1345 while (ui->pattern.padHasAntecessor (clipRMin, start)) --start;
1346
1347 // Find end
1348 int end = clipSMax;
1349 while (ui->pattern.padHasSuccessor (clipRMin, end)) ++end;
1350
1351 // Limit pad duration to 1.0 (= split)
1352 for (int i = start; i <= end; ++i)
1353 {
1354 Pad pd = ui->pattern.getPad (clipRMin, i);
1355 pd.duration = LIMIT (pd.duration, 0.0, 1.0);
1356 ui->pattern.setPad (clipRMin, i, pd);
1357 ui->send_pad (clipRMin, i);
1358 }
1359 }
1360
1361 // Merge
1362 else
1363 {
1364 // Find start
1365 int start = clipSMin;
1366 while (ui->pattern.padHasAntecessor (clipRMin, start)) --start;
1367
1368 // Find end
1369 int end = clipSMax;
1370 while (ui->pattern.padHasSuccessor (clipRMin, end)) ++end;
1371
1372 Pad pd0 = ui->pattern.getPad (clipRMin, start);
1373
1374 // Count number of mergeable pads
1375 int size = 0;
1376 do ++size;
1377 while
1378 (
1379 (start + size - 1 < end) &&
1380 ((int (pd0.ch) & 0x0F) == (int (ui->pattern.getPad (clipRMin, start + size).ch) & 0x0F)) &&
1381 (ui->pattern.getPad (clipRMin, start + size).duration > 0.0)
1382 );
1383
1384 // TODO Set control to first element
1385 int ctrl = 0;
1386 for (int i = 0; i < size; ++i)
1387 {
1388 Pad pdi = ui->pattern.getPad (clipRMin, start + i);
1389 if (int (pdi.ch) & 0xF0)
1390 {
1391 ctrl = int (pdi.ch) & 0xF0;
1392 break;
1393 }
1394 }
1395 pd0.ch = int (pd0.ch) | ctrl;
1396
1397 pd0.duration = ui->pattern.getPad (clipRMin, start + size - 1).duration + size - 1;
1398
1399 for (int i = 0; i < size; ++i)
1400 {
1401 ui->pattern.setPad (clipRMin, start + i, pd0);
1402 ui->send_pad (clipRMin, start + i);
1403 --pd0.duration;
1404 if (i == 0) pd0.ch = int (pd0.ch) & 0x0F;
1405 }
1406 }
1407
1408 ui->clipBoard.ready = true;
1409 ui->drawPad();
1410 ui->pattern.store ();
1411 }
1412
1413 else if ((edit == EDIT_CUT) || (edit == EDIT_COPY) || (edit == EDIT_FLIPX) || (edit == EDIT_FLIPY))
1414 {
1415 int clipRMin = ui->clipBoard.origin.first;
1416 int clipRMax = ui->clipBoard.origin.first + ui->clipBoard.extends.first;
1417 if (clipRMin > clipRMax) std::swap (clipRMin, clipRMax);
1418 int clipSMin = ui->clipBoard.origin.second;
1419 int clipSMax = ui->clipBoard.origin.second + ui->clipBoard.extends.second;
1420 if (clipSMin > clipSMax) std::swap (clipSMin, clipSMax);
1421
1422 // Clip merged Pads
1423 if ((edit == EDIT_CUT) || (edit == EDIT_FLIPX) || (edit == EDIT_FLIPY))
1424 {
1425 for (int r = clipRMin; r < clipRMax; ++r)
1426 {
1427 ui->padClip (r, clipSMin);
1428 if (clipSMin != clipSMax) ui->padClip (r, clipSMax + 1);
1429 }
1430 }
1431
1432 // Copy to clipboard
1433 ui->clipBoard.data.clear ();
1434 for (int r = clipRMax; r >= clipRMin; --r)
1435 {
1436 std::vector<Pad> padRow;
1437 padRow.clear ();
1438 for (int s = clipSMin; s <= clipSMax; ++s)
1439 {
1440 Pad pd = ui->pattern.getPad (r, s);
1441
1442 // Clip oversized merged pads
1443 if (float(s) + pd.duration > float(clipSMax + 1.0f)) pd.duration = clipSMax + 1 - s;
1444
1445 padRow.push_back (pd);
1446 }
1447 ui->clipBoard.data.push_back (padRow);
1448 }
1449
1450 if (edit == EDIT_FLIPX)
1451 {
1452 for (int dr = 0; dr < int (ui->clipBoard.data.size()); ++dr)
1453 {
1454 for (int ds = 0; ds < int (ui->clipBoard.data[dr].size());)
1455 {
1456 Pad pd = ui->clipBoard.data.at (dr).at (ds);
1457 int pdsize = (pd.duration <= 0.0f ? 1.0 : int (ceil (pd.duration)));
1458
1459 for (int dds = 0; dds < pdsize; ++dds)
1460 {
1461 Pad pds = ui->clipBoard.data.at (dr).at (ds + dds);
1462 ui->pattern.setPad (clipRMax - dr, clipSMax + 1 + dds - ds - pdsize, pds);
1463 ui->send_pad (clipRMax - dr, clipSMax + 1 + dds - ds - pdsize);
1464 }
1465
1466 ds += pdsize;
1467 }
1468 }
1469 ui->pattern.store ();
1470 }
1471
1472 else if (edit == EDIT_FLIPY)
1473 {
1474 for (int dr = 0; dr < int (ui->clipBoard.data.size()); ++dr)
1475 {
1476 for (int ds = 0; ds < int (ui->clipBoard.data[dr].size()); ++ds)
1477 {
1478 Pad pd = ui->clipBoard.data.at (dr).at (ds);
1479 ui->pattern.setPad (clipRMin + dr, clipSMin + ds, pd);
1480 ui->send_pad (clipRMin + dr, clipSMin + ds);
1481 }
1482 }
1483 ui->pattern.store ();
1484 }
1485
1486 // Cut: Clear pads
1487 else if (edit == EDIT_CUT)
1488 {
1489 for (int r = clipRMax; r >= clipRMin; --r)
1490 {
1491 for (int s = clipSMin; s <= clipSMax; ++s)
1492 {
1493 ui->pattern.setPad (r, s, Pad ());
1494 ui->send_pad (r, s);
1495 }
1496 }
1497 ui->pattern.store ();
1498 }
1499
1500 ui->clipBoard.ready = true;
1501 ui->drawPad ();
1502 }
1503 }
1504
1505 // On BUTTON_RELEASE_EVENT and temporary delete mode: switch back
1506 else if (ui->tempTool)
1507 {
1508 ui->tempTool = false;
1509 ui->controllerWidgets[SELECTION_CH]->setValue (ui->tempToolCh);
1510 }
1511
1512 else ui->pattern.store ();
1513 }
1514 }
1515 }
1516
padsScrolledCallback(BEvents::Event * event)1517 void BSEQuencer_GUI::padsScrolledCallback (BEvents::Event* event)
1518 {
1519 if ((event) && (event->getWidget ()) && (event->getWidget()->getMainWindow()) &&
1520 ((event->getEventType () == BEvents::WHEEL_SCROLL_EVENT)))
1521 {
1522 BWidgets::DrawingSurface* widget = (BWidgets::DrawingSurface*) event->getWidget ();
1523 BSEQuencer_GUI* ui = (BSEQuencer_GUI*) widget->getMainWindow();
1524 BEvents::WheelEvent* wheelEvent = (BEvents::WheelEvent*) event;
1525
1526 // Get size of drawing area
1527 const double width = ui->padSurface.getEffectiveWidth ();
1528 const double height = ui->padSurface.getEffectiveHeight ();
1529
1530 int row = (ROWS - 1) - ((int) ((wheelEvent->getPosition ().y - widget->getYOffset()) / (height / ROWS)));
1531 int step = (wheelEvent->getPosition ().x - widget->getXOffset()) / (width / ui->controllerWidgets[NR_OF_STEPS]->getValue ());
1532
1533 if ((row >= 0) && (row < ROWS) && (step >= 0) && (step < ((int)ui->controllerWidgets[NR_OF_STEPS]->getValue ())))
1534 {
1535 while (ui->pattern.padHasAntecessor (row, step)) --step;
1536 Pad pd = ui->pattern.getPad (row, step);
1537 if (int (pd.ch) & 0x0F)
1538 {
1539 // SHIFT: Change duration
1540 if (ui->modifier == MODIFIER_DURATION)
1541 {
1542 float d = pd.duration * (1.0f + 0.01f * wheelEvent->getDelta().y);
1543 if ((d >= 0.0f) && (d <= int (ui->controllerWidgets[NR_OF_STEPS]->getValue() - step)))
1544 {
1545
1546 // Delete last step of pad shinked
1547 if (int (ceil(d)) < int (ceil (pd.duration)))
1548 {
1549 Pad pds = ui->pattern.getPad (row, step + int (pd.duration));
1550 pds.ch = int (pds.ch) & 0xF0;
1551 pds.duration = 0.0f;
1552 ui->pattern.setPad (row, step + int (pd.duration), pds);
1553 ui->drawPad (row, step + int (pd.duration));
1554 ui->send_pad (row, step + int (pd.duration));
1555 }
1556
1557 // Change duration
1558 do
1559 {
1560 pd.duration = d;
1561 ui->pattern.setPad (row, step, pd);
1562 ui->drawPad (row, step);
1563 ui->send_pad (row, step);
1564 pd.ch = int (pd.ch) & 0x0F;
1565 ++step;
1566 --d;
1567 } while (d > 0.0f);
1568 }
1569 }
1570
1571 // CTRL: Change pitch octave
1572 else if (ui->modifier == MODIFIER_OCTAVE)
1573 {
1574 int o = pd.pitchOctave + wheelEvent->getDelta().y;
1575 o = LIMIT (o, -8, 8);
1576 do
1577 {
1578 Pad pds = ui->pattern.getPad (row, step);
1579 pds.pitchOctave = o;
1580 ui->pattern.setPad (row, step, pds);
1581 ui->drawPad (row, step);
1582 ui->send_pad (row, step);
1583 ++step;
1584 } while (ui->pattern.padHasSuccessor (row, step - 1));
1585 }
1586
1587 // Otherwise: Change velocity
1588 else
1589 {
1590 float v = pd.velocity * (1.0f + 0.01f * wheelEvent->getDelta().y);
1591 v = LIMIT (v, 0.0f, 2.0f);
1592 do
1593 {
1594 Pad pds = ui->pattern.getPad (row, step);
1595 pds.velocity = v;
1596 ui->pattern.setPad (row, step, pds);
1597 ui->drawPad (row, step);
1598 ui->send_pad (row, step);
1599 ++step;
1600 } while (ui->pattern.padHasSuccessor (row, step - 1));
1601 }
1602
1603 ui->wheelScrolled = true;
1604 }
1605 }
1606 }
1607 }
1608
padsFocusedCallback(BEvents::Event * event)1609 void BSEQuencer_GUI::padsFocusedCallback (BEvents::Event* event)
1610 {
1611 if ((event) && (event->getWidget ()) && (event->getWidget()->getMainWindow()))
1612 {
1613 BWidgets::DrawingSurface* widget = (BWidgets::DrawingSurface*) event->getWidget ();
1614 BSEQuencer_GUI* ui = (BSEQuencer_GUI*) widget->getMainWindow();
1615 BEvents::FocusEvent* focusEvent = (BEvents::FocusEvent*) event;
1616
1617 // Get size of drawing area
1618 const double width = ui->padSurface.getEffectiveWidth ();
1619 const double height = ui->padSurface.getEffectiveHeight ();
1620
1621 int row = (ROWS - 1) - ((int) ((focusEvent->getPosition ().y - widget->getYOffset()) / (height / ROWS)));
1622 int step = (focusEvent->getPosition ().x - widget->getXOffset()) / (width / ui->controllerWidgets[NR_OF_STEPS]->getValue ());
1623 while (ui->pattern.padHasAntecessor (row, step)) --step;
1624
1625 if ((row >= 0) && (row < ROWS) && (step >= 0) && (step < ((int)ui->controllerWidgets[NR_OF_STEPS]->getValue ())))
1626 {
1627 Pad pd = ui->pattern.getPad (row, step);
1628 double dm = fmod (pd.duration, 1.0);
1629 if (dm == 0.0) dm = 1.0;
1630 double rd = LIMIT (pd.randDuration, - dm, 0.0);
1631 double pdr = pd.duration * rd / dm;
1632 ui->padSurface.focusText.setText
1633 (
1634 BSEQUENCER_LABEL_CHANNEL ": " + std::to_string (int (pd.ch) & 0x0f) + "\n" +
1635 BSEQUENCER_LABEL_GATE ": " + BUtilities::to_string (pd.randGate, "%1.2f") + "\n" +
1636 BSEQUENCER_LABEL_NOTE ": " + std::to_string (int (pd.pitchNote)) + " ± " + std::to_string (int (abs (pd.randNote))) + "\n" +
1637 BSEQUENCER_LABEL_OCTAVE ": " + std::to_string (int (pd.pitchOctave)) + " ± " + std::to_string (int (abs (pd.randOctave))) + "\n" +
1638 BSEQUENCER_LABEL_VELOCITY ": " + BUtilities::to_string (pd.velocity, "%1.2f") + " ± " + BUtilities::to_string (abs (pd.randVelocity), "%1.2f") + "\n" +
1639 BSEQUENCER_LABEL_DURATION ": " + BUtilities::to_string (pd.duration, "%1.2f") + " (" + BUtilities::to_string (pdr, "%1.2f") + ")");
1640 ui->scaleFocus ();
1641 }
1642 }
1643
1644 if (event->getEventType () == BEvents::FOCUS_IN_EVENT) focusInCallback (event);
1645 else if (event->getEventType () == BEvents::FOCUS_OUT_EVENT) focusOutCallback (event);
1646 }
1647
drawCaption()1648 void BSEQuencer_GUI::drawCaption ()
1649 {
1650 cairo_surface_t* surface = captionSurface.getDrawingSurface();
1651 cairo_surface_clear (surface);
1652 const double width = captionSurface.getEffectiveWidth ();
1653 const double height = captionSurface.getEffectiveHeight ();
1654 cairo_t* cr = cairo_create (surface);
1655 BColors::Color textcolor = *txColors. getColor(BColors::ACTIVE);
1656 cairo_set_source_rgba (cr, CAIRO_RGBA (textcolor));
1657 cairo_select_font_face (cr, ctLabelFont.getFontFamily ().c_str (), ctLabelFont.getFontSlant (), ctLabelFont.getFontWeight ());
1658
1659 int scaleNr = controllers[SCALE];
1660 BScale scale
1661 (
1662 ((int)(controllers[ROOT] + controllers[SIGNATURE] + 12)) % 12,
1663 (SignatureIndex) controllers[SIGNATURE],
1664 scaleMaps[scaleNr].scaleNotes
1665 );
1666 std::string label = "";
1667
1668 for (int i = 0; i < ROWS; ++i)
1669 {
1670 BColors::Color color = BColors::invisible;
1671 if (noteBits & (1 << i)) {color = ink; color.applyBrightness (0.75);}
1672 drawButton (surface, 0, (ROWS - i - 1) * height / ROWS + 1, width, height / ROWS - 2, color, NO_CTRL);
1673
1674 ScaleMap* map = &(scaleMaps[scaleNr]);
1675
1676 if (map->altSymbols[i] != "") label = map->altSymbols[i];
1677 else
1678 {
1679 int element = map->elements[i];
1680 label = scale.getSymbol (element);
1681 int note = scale.getMIDInote (element);
1682 if (note != ENOTE)
1683 {
1684 if (note >= 12) label += " +" + std::to_string ((int) (note / 12));
1685 }
1686 else label = "ERR";
1687 }
1688
1689 cairo_text_extents_t ext;
1690
1691 double fontsize = ctLabelFont.getFontSize () * sqrt (2);
1692 do
1693 {
1694 fontsize = fontsize / sqrt (2);
1695 cairo_set_font_size (cr, fontsize);
1696 cairo_text_extents (cr, label.c_str(), &ext);
1697 } while ((ext.width > width) && (fontsize >= ctLabelFont.getFontSize () * 0.5));
1698
1699 cairo_move_to (cr, width / 2 - ext.width / 2, (15.5 - i) * height / 16 + ext.height / 2);
1700 cairo_show_text (cr, label.c_str());
1701 }
1702
1703 cairo_destroy (cr);
1704 captionSurface.update ();
1705 }
1706
drawPad()1707 void BSEQuencer_GUI::drawPad ()
1708 {
1709 cairo_surface_t* surface = padSurface.getDrawingSurface();
1710 cairo_t* cr = cairo_create (surface);
1711 for (int row = 0; row < ROWS; ++row)
1712 {
1713 for (int step = 0; step < ((int)controllerWidgets[NR_OF_STEPS]->getValue ()); ++step)
1714 {
1715 drawPad (cr, row, step);
1716 while (pattern.padHasSuccessor (row, step)) ++step;
1717 }
1718 }
1719 cairo_destroy (cr);
1720 padSurface.update();
1721 }
1722
drawPad(const int row,const int step)1723 void BSEQuencer_GUI::drawPad (const int row, const int step)
1724 {
1725 cairo_surface_t* surface = padSurface.getDrawingSurface();
1726 cairo_t* cr = cairo_create (surface);
1727 drawPad (cr, row, step);
1728 cairo_destroy (cr);
1729 padSurface.update();
1730 }
1731
drawPad(cairo_t * cr,const int row,const int step)1732 void BSEQuencer_GUI::drawPad (cairo_t* cr, const int row, const int step)
1733 {
1734 int start = step;
1735 while (pattern.padHasAntecessor (row, start)) --start;
1736
1737 Pad pd = pattern.getPad (row, start);
1738
1739 if ((!cr) || (cairo_status (cr) != CAIRO_STATUS_SUCCESS) || (row < 0) || (row >= ROWS) || (start < 0) ||
1740 (start >= (int (controllerWidgets[NR_OF_STEPS]->getValue ())))) return;
1741
1742 // Get size of drawing area
1743 const double width = padSurface.getEffectiveWidth ();
1744 const double height = padSurface.getEffectiveHeight ();
1745 const double w = width / controllerWidgets[NR_OF_STEPS]->getValue ();
1746 const double h = height / ROWS;
1747 const double x = start * w;
1748 const double y = (ROWS - row - 1) * h;
1749 const double xr = round (x);
1750 const double yr = round (y);
1751 int ps = pattern.padGetSize (row, start);
1752 if (start + ps > int (controllerWidgets[NR_OF_STEPS]->getValue ())) ps = int (controllerWidgets[NR_OF_STEPS]->getValue ()) - start;
1753 const double wr = round (x + w * ps) - xr;
1754 const double hr = round (y + h) - yr;
1755
1756
1757 // Odd or even?
1758 BColors::Color bg = ((int (start / controllerWidgets[STEPS_PER]->getValue ())) % 2) ? oddPadBgColor : evenPadBgColor;
1759
1760 // Highlight selection
1761 int clipRMin = clipBoard.origin.first;
1762 int clipRMax = clipBoard.origin.first + clipBoard.extends.first;
1763 if (clipRMin > clipRMax) std::swap (clipRMin, clipRMax);
1764 int clipSMin = clipBoard.origin.second;
1765 int clipSMax = clipBoard.origin.second + clipBoard.extends.second;
1766 if (clipSMin > clipSMax) std::swap (clipSMin, clipSMax);
1767
1768 // Draw backgroung
1769 int i = 0;
1770 do
1771 {
1772 if ((start + i >= 0) && (start + i < int (controllerWidgets[NR_OF_STEPS]->getValue ())))
1773 {
1774 BColors::Color bgi = bg;
1775 double xi = round (x + i * w);
1776 double wi = round (x + (i + 1) * w) - xi;
1777 if ((!clipBoard.ready) && (row >= clipRMin) && (row <= clipRMax) && (start + i >= clipSMin) && (start + i <= clipSMax)) bgi.applyBrightness (1.5);
1778 cairo_set_source_rgba (cr, CAIRO_RGBA (bgi));
1779 cairo_set_line_width (cr, 0.0);
1780 cairo_rectangle (cr, xi, yr, wi, hr);
1781 cairo_fill (cr);
1782 }
1783 ++i;
1784 } while (pattern.padHasSuccessor (row, start + i - 1));
1785
1786 // Draw pad
1787 int ch = padGetChannel (row, start);
1788 int ctrl = padGetControl (row, start);
1789 double vel = (pd.velocity <= 1 ? pd.velocity - 1 : (pd.velocity - 1) * 0.5);
1790 int oct = pd.pitchOctave;
1791
1792 if ((ch >= 0) && (ch <= NR_SEQUENCER_CHS) && (ctrl >= 0) && (ctrl < NR_CTRL_BUTTONS))
1793 {
1794 BColors::Color color = chButtonStyles[ch].color;
1795 if (ch > 0) color.applyBrightness(vel);
1796 int i = 0;
1797 do
1798 {
1799 if (cursorBits[start + i] & (1 << row))
1800 {
1801 color.setAlpha (1.0);
1802 color.applyBrightness (0.75);
1803 break;
1804 }
1805 ++i;
1806 }
1807 while (pattern.padHasSuccessor (row, start + i - 1));
1808
1809 int symbol = ctrlButtonStyles[ctrl].symbol;
1810
1811 drawButton (cr, xr + 1, yr + 1, wr - 2, hr - 2, color, symbol, (pd.duration == 0 ? 1 : pd.duration / ceil (pd.duration)));
1812
1813 // Displays pitch octave
1814 if ((pd.duration > 0.0) && (pd.velocity > 0.0))
1815 {
1816 cairo_surface_t* surface = padSurface.getDrawingSurface();
1817 cairo_t* cr = cairo_create (surface);
1818 double h = 0.75 * lfLabelFont.getFontSize ();
1819
1820 if (pd.pitchNote > 0)
1821 {
1822 cairo_move_to (cr, xr + 1.0 + 3.0 * sz, yr + 1.0 + 2.0 * sz + h);
1823 cairo_line_to (cr, xr + 1.0 + 3.0 * sz, yr + 1.0 + 2.0 * sz + (1.0 - pd.pitchNote / 16.0) * h);
1824 cairo_line_to (cr, xr + 1.0 + 2.0 * sz, yr + 1.0 + 3.0 * sz + (1.0 - pd.pitchNote / 16.0) * h);
1825 cairo_move_to (cr, xr + 1.0 + 3.0 * sz, yr + 1.0 + 2.0 * sz + (1.0 - pd.pitchNote / 16.0) * h);
1826 cairo_line_to (cr, xr + 1.0 + 4.0 * sz, yr + 1.0 + 3.0 * sz + (1.0 - pd.pitchNote / 16.0) * h);
1827 cairo_set_line_width (cr, 1.0);
1828 if ((ch == 4) && (pd.velocity > 0.5)) cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::black));
1829 else cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::white));
1830 cairo_stroke (cr);
1831 }
1832
1833 if (pd.pitchNote < 0)
1834 {
1835 cairo_move_to (cr, xr + 1.0 + 3.0 * sz, yr + 1.0 + 2.0 * sz);
1836 cairo_line_to (cr, xr + 1.0 + 3.0 * sz, yr + 1.0 + 2.0 * sz + (-pd.pitchNote / 16.0) * h);
1837 cairo_line_to (cr, xr + 1.0 + 2.0 * sz, yr + 1.0 + 1.0 * sz + (-pd.pitchNote / 16.0) * h);
1838 cairo_move_to (cr, xr + 1.0 + 3.0 * sz, yr + 1.0 + 2.0 * sz + (-pd.pitchNote / 16.0) * h);
1839 cairo_line_to (cr, xr + 1.0 + 4.0 * sz, yr + 1.0 + 1.0 * sz + (-pd.pitchNote / 16.0) * h);
1840 cairo_set_line_width (cr, 1.0);
1841 if ((ch == 4) && (pd.velocity > 0.5)) cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::black));
1842 else cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::white));
1843 cairo_stroke (cr);
1844 }
1845
1846 if (oct != 0)
1847 {
1848 std::string valstr = (oct <= 0 ? "" : "+") + std::to_string (oct);
1849 if ((ch == 4) && (pd.velocity > 0.5)) cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::black));
1850 else cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::white));
1851 cairo_select_font_face (cr, lfLabelFont.getFontFamily ().c_str (), lfLabelFont.getFontSlant (), lfLabelFont.getFontWeight ());
1852 cairo_set_font_size (cr, h);
1853 cairo_move_to (cr, xr + 1.0 + 6.0 * sz, yr + 1.0 + 2.0 * sz + h);
1854 cairo_show_text (cr, valstr.c_str ());
1855 }
1856
1857 cairo_destroy (cr);
1858 }
1859 }
1860 }
1861
padIsSelected(const int row,const int step)1862 bool BSEQuencer_GUI::padIsSelected (const int row, const int step)
1863 {
1864 int s = step;
1865 while (pattern.padHasAntecessor (row, step)) --s;
1866
1867 int clipRMin = clipBoard.origin.first;
1868 int clipRMax = clipBoard.origin.first + clipBoard.extends.first;
1869 if (clipRMin > clipRMax) std::swap (clipRMin, clipRMax);
1870 int clipSMin = clipBoard.origin.second;
1871 int clipSMax = clipBoard.origin.second + clipBoard.extends.second;
1872 if (clipSMin > clipSMax) std::swap (clipSMin, clipSMax);
1873 if (clipBoard.ready || (row < clipRMin) || (row > clipRMax)) return false;
1874
1875 do
1876 {
1877 if ((s >= clipSMin) && (s <= clipSMax)) return true;
1878 ++s;
1879 } while (pattern.padHasSuccessor (row, s - 1));
1880
1881 return false;
1882 }
1883
padGetChannel(const int row,const int step)1884 int BSEQuencer_GUI::padGetChannel (const int row, const int step)
1885 {
1886 int start = step;
1887 while (pattern.padHasAntecessor (row, start)) --start;
1888 return int (pattern.getPad (row, start).ch) & 0x0F;
1889 }
1890
padGetControl(const int row,const int step)1891 int BSEQuencer_GUI::padGetControl (const int row, const int step)
1892 {
1893 int start = step;
1894 while (pattern.padHasAntecessor (row, start)) --start;
1895 return (int (pattern.getPad (row, start).ch) & 0xF0) / 0x10;
1896 }
1897
padClip(const int row,const int step)1898 void BSEQuencer_GUI::padClip (const int row, const int step)
1899 {
1900 if (pattern.padHasAntecessor (row, step))
1901 {
1902 int start = step - 1;
1903 while (pattern.padHasAntecessor (row, start)) --start;
1904 for (int s = start; s < step; ++s)
1905 {
1906 Pad pd = pattern.getPad (row, s);
1907 pd.duration = step - s;
1908 pattern.setPad (row, s, pd);
1909 send_pad (row, s);
1910 }
1911 drawPad (row, start);
1912 drawPad (row, step);
1913 }
1914 }
1915
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)1916 static LV2UI_Handle instantiate (const LV2UI_Descriptor *descriptor,
1917 const char *plugin_uri,
1918 const char *bundle_path,
1919 LV2UI_Write_Function write_function,
1920 LV2UI_Controller controller,
1921 LV2UI_Widget *widget,
1922 const LV2_Feature *const *features)
1923 {
1924 PuglNativeView parentWindow = 0;
1925 LV2UI_Resize* resize = NULL;
1926
1927 if (strcmp(plugin_uri, BSEQUENCER_URI) != 0)
1928 {
1929 std::cerr << "BSEQuencer.lv2#GUI: GUI does not support plugin with URI " << plugin_uri << std::endl;
1930 return NULL;
1931 }
1932
1933 for (int i = 0; features[i]; ++i)
1934 {
1935 if (!strcmp(features[i]->URI, LV2_UI__parent)) parentWindow = (PuglNativeView) features[i]->data;
1936 else if (!strcmp(features[i]->URI, LV2_UI__resize)) resize = (LV2UI_Resize*)features[i]->data;
1937 }
1938 if (parentWindow == 0) std::cerr << "BSEQuencer.lv2#GUI: No parent window.\n";
1939
1940 // New instance
1941 BSEQuencer_GUI* ui;
1942 try {ui = new BSEQuencer_GUI (bundle_path, features, parentWindow);}
1943 catch (std::exception& exc)
1944 {
1945 std::cerr << "BSEQuencer.lv2#GUI: Instantiation failed. " << exc.what () << std::endl;
1946 return NULL;
1947 }
1948
1949 ui->controller = controller;
1950 ui->write_function = write_function;
1951
1952 // Reduce min GUI size for small displays
1953 double sz = 1.0;
1954 int screenWidth = getScreenWidth ();
1955 int screenHeight = getScreenHeight ();
1956 if ((screenWidth < 870) || (screenHeight < 580)) sz = 0.5;
1957 else if ((screenWidth < 1290) || (screenHeight < 860)) sz = 0.66;
1958
1959 /*
1960 std::cerr << "B.SEQuencer_GUI.lv2 screen size " << screenWidth << " x " << screenHeight <<
1961 ". Set GUI size to " << 1250 * sz << " x " << 820 * sz << ".\n";
1962 */
1963
1964 if (resize) resize->ui_resize(resize->handle, 1250 * sz, 820 * sz);
1965
1966 *widget = (LV2UI_Widget) puglGetNativeWindow (ui->getPuglView ());
1967
1968 ui->send_ui_on();
1969
1970 return (LV2UI_Handle) ui;
1971 }
1972
cleanup(LV2UI_Handle ui)1973 static void cleanup(LV2UI_Handle ui)
1974 {
1975 BSEQuencer_GUI* self = (BSEQuencer_GUI*) ui;
1976 if (self) delete self;
1977 }
1978
port_event(LV2UI_Handle ui,uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)1979 static void port_event(LV2UI_Handle ui, uint32_t port_index, uint32_t buffer_size,
1980 uint32_t format, const void* buffer)
1981 {
1982 BSEQuencer_GUI* self = (BSEQuencer_GUI*) ui;
1983 if (self) self->port_event(port_index, buffer_size, format, buffer);
1984 }
1985
call_idle(LV2UI_Handle ui)1986 static int call_idle (LV2UI_Handle ui)
1987 {
1988 BSEQuencer_GUI* self = (BSEQuencer_GUI*) ui;
1989 if (self) self->handleEvents ();
1990 return 0;
1991 }
1992
call_resize(LV2UI_Handle ui,int width,int height)1993 static int call_resize (LV2UI_Handle ui, int width, int height)
1994 {
1995 BSEQuencer_GUI* self = (BSEQuencer_GUI*) ui;
1996 if (!self) return 0;
1997
1998 BEvents::ExposeEvent* ev = new BEvents::ExposeEvent (self, self, BEvents::CONFIGURE_REQUEST_EVENT, self->getPosition().x, self->getPosition().y, width, height);
1999 self->addEventToQueue (ev);
2000 return 0;
2001 }
2002
2003 static const LV2UI_Idle_Interface idle = {call_idle};
2004 static const LV2UI_Resize resize = {nullptr, call_resize};
2005
extension_data(const char * uri)2006 static const void* extension_data(const char* uri)
2007 {
2008 if (!strcmp(uri, LV2_UI__idleInterface)) return &idle;
2009 else if(!strcmp(uri, LV2_UI__resize)) return &resize;
2010 else return NULL;
2011 }
2012
2013 static const LV2UI_Descriptor guiDescriptor = {
2014 BSEQUENCER_GUI_URI,
2015 instantiate,
2016 cleanup,
2017 port_event,
2018 extension_data
2019 };
2020
2021 // LV2 Symbol Export
lv2ui_descriptor(uint32_t index)2022 LV2_SYMBOL_EXPORT const LV2UI_Descriptor *lv2ui_descriptor(uint32_t index)
2023 {
2024 switch (index) {
2025 case 0: return &guiDescriptor;
2026 default:return NULL;
2027 }
2028 }
2029
2030 /* End of LV2 specific declarations
2031 *
2032 * *****************************************************************************
2033 *
2034 *
2035 */
2036