1 /*!
2 * @file seqwidget_lv2.cpp
3 * @brief Implements the the LV2 GUI for the QMidiArp Seq plugin.
4 *
5 *
6 * Copyright 2009 - 2017 <qmidiarp-devel@lists.sourceforge.net>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 * MA 02110-1301, USA.
22 *
23 */
24
25 #include "seqwidget_lv2.h"
26
27 #include <unistd.h>
28 #include <ctime>
29
30
SeqWidgetLV2(LV2UI_Controller ct,LV2UI_Write_Function write_function,const LV2_Feature * const * host_features)31 SeqWidgetLV2::SeqWidgetLV2 (
32 LV2UI_Controller ct,
33 LV2UI_Write_Function write_function,
34 const LV2_Feature *const *host_features)
35 : SeqWidget(true, true, true)
36 {
37 m_controller = ct;
38 writeFunction = write_function;
39
40 /* Scan host features for URID map */
41
42 LV2_URID_Map *urid_map;
43 for (int i = 0; host_features[i]; ++i) {
44 if (::strcmp(host_features[i]->URI, LV2_URID_URI "#map") == 0) {
45 urid_map = (LV2_URID_Map *) host_features[i]->data;
46 if (urid_map) {
47 (void)urid_map->map(urid_map->handle, LV2_MIDI_EVENT_URI);
48 break;
49 }
50 }
51 }
52 if (!urid_map) {
53 qWarning("Host does not support urid:map.");
54 return;
55 }
56
57 lv2_atom_forge_init(&forge, urid_map);
58
59 /* Map URIS */
60 QMidiArpURIs* const uris = &m_uris;
61 map_uris(urid_map, uris);
62
63
64 transportBox = new QCheckBox(this);
65 QLabel *transportBoxLabel = new QLabel(tr("&Sync with Host"),this);
66 transportBoxLabel->setBuddy(transportBox);
67 transportBox->setToolTip(tr("Sync to Transport from Host"));
68 tempoSpin = new QSpinBox(this);
69 tempoSpin->setRange(10, 400);
70 tempoSpin->setValue(120);
71 tempoSpin->setKeyboardTracking(false);
72 tempoSpin->setToolTip(tr("Tempo of internal clock"));
73 connect(transportBox, SIGNAL(toggled(bool)), this, SLOT(mapBool(bool)));
74 connect(transportBox, SIGNAL(toggled(bool)), tempoSpin, SLOT(setDisabled(bool)));
75 transportBox->setChecked(true);
76
77 inOutBoxWidget->layout()->addWidget(transportBoxLabel);
78 inOutBoxWidget->layout()->addWidget(transportBox);
79 inOutBoxWidget->layout()->addWidget(tempoSpin);
80
81 connect(velocity, SIGNAL(valueChanged(int)), this, SLOT(mapParam(int)));
82 connect(notelength, SIGNAL(valueChanged(int)), this, SLOT(mapParam(int)));
83 connect(resBox, SIGNAL(activated(int)), this, SLOT(mapParam(int)));
84 connect(sizeBox, SIGNAL(activated(int)), this, SLOT(mapParam(int)));
85 connect(transpose, SIGNAL(valueChanged(int)), this, SLOT(mapParam(int)));
86 connect(loopBox, SIGNAL(activated(int)), this, SLOT(mapParam(int)));
87 connect(channelOut, SIGNAL(activated(int)), this, SLOT(mapParam(int)));
88 connect(chIn, SIGNAL(activated(int)), this, SLOT(mapParam(int)));
89 connect(indexIn[0], SIGNAL(valueChanged(int)), this, SLOT(mapParam(int)));
90 connect(indexIn[1], SIGNAL(valueChanged(int)), this, SLOT(mapParam(int)));
91 connect(rangeIn[0], SIGNAL(valueChanged(int)), this, SLOT(mapParam(int)));
92 connect(rangeIn[1], SIGNAL(valueChanged(int)), this, SLOT(mapParam(int)));
93 connect(tempoSpin, SIGNAL(valueChanged(int)), this, SLOT(mapParam(int)));
94 connect(dispSignalMapper, SIGNAL(mapped(int)), this, SLOT(mapParam(int)));
95
96 connect(muteOutAction, SIGNAL(toggled(bool)), this, SLOT(mapBool(bool)));
97 connect(enableNoteIn, SIGNAL(toggled(bool)), this, SLOT(mapBool(bool)));
98 connect(enableVelIn, SIGNAL(toggled(bool)), this, SLOT(mapBool(bool)));
99 connect(enableNoteOff, SIGNAL(toggled(bool)), this, SLOT(mapBool(bool)));
100 connect(enableRestartByKbd, SIGNAL(toggled(bool)), this, SLOT(mapBool(bool)));
101 connect(enableTrigByKbd, SIGNAL(toggled(bool)), this, SLOT(mapBool(bool)));
102 connect(enableTrigLegato, SIGNAL(toggled(bool)), this, SLOT(mapBool(bool)));
103 connect(recordAction, SIGNAL(toggled(bool)), this, SLOT(mapBool(bool)));
104 connect(deferChangesAction, SIGNAL(toggled(bool)), this, SLOT(mapBool(bool)));
105
106 connect(this, SIGNAL(mouseSig(double, double, int, int))
107 , this, SLOT(mapMouse(double, double, int, int)));
108
109 setStyleSheet("QLabel { font: 7pt; } \
110 QComboBox { font: 7pt; max-height: 15px;} \
111 QToolButton { max-height: 20px;} \
112 QSpinBox { font: 7pt; max-height: 20px;} \
113 QCheckBox { font: 7pt; max-height: 20px;} \
114 QGroupBox { font: 7pt; }");
115
116 res = 4;
117 size = 4;
118 mouseXCur = 0.0;
119 mouseYCur = 0.0;
120 sendUIisUp(true);
121 }
122
~SeqWidgetLV2()123 SeqWidgetLV2::~SeqWidgetLV2()
124 {
125 }
126
port_event(uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)127 void SeqWidgetLV2::port_event ( uint32_t port_index,
128 uint32_t buffer_size, uint32_t format, const void *buffer )
129 {
130 const QMidiArpURIs* uris = &m_uris;
131 LV2_Atom* atom = (LV2_Atom*)buffer;
132
133 if (!data.count()) sendUIisUp(true);
134
135 if (format == uris->atom_eventTransfer
136 && atom->type == uris->atom_Object) {
137 receiveWave(atom);
138 }
139 else if (format == 0 && buffer_size == sizeof(float)) {
140
141 float fValue = *(float *) buffer;
142
143 switch (port_index) {
144 case VELOCITY:
145 velocity->setValue(fValue);
146 break;
147 case NOTELENGTH:
148 notelength->setValue(fValue);
149 break;
150 case RESOLUTION:
151 resBox->setCurrentIndex(fValue);
152 break;
153 case SIZE:
154 sizeBox->setCurrentIndex(fValue);
155 break;
156 case TRANSPOSE:
157 transpose->setValue(fValue);
158 break;
159 case CH_OUT:
160 channelOut->setCurrentIndex(fValue);
161 break;
162 case CH_IN:
163 chIn->setCurrentIndex(fValue);
164 break;
165 case INDEX_IN1:
166 indexIn[0]->setValue(fValue);
167 break;
168 case INDEX_IN2:
169 indexIn[1]->setValue(fValue);
170 break;
171 case RANGE_IN1:
172 rangeIn[0]->setValue(fValue);
173 break;
174 case RANGE_IN2:
175 rangeIn[1]->setValue(fValue);
176 break;
177 case CURSOR_POS:
178 cursor->updateNumbers(res, size);
179 cursor->updatePosition(fValue);
180 cursor->update();
181 break;
182 case LOOPMARKER:
183 screen->setLoopMarker((int)fValue);
184 screen->update();
185 break;
186 case LOOPMODE:
187 loopBox->setCurrentIndex(fValue);
188 break;
189 case MUTE:
190 muteOutAction->setChecked((bool)fValue);
191 screen->setMuted(fValue);
192 screen->update();
193 break;
194 case DISPLAY_ZOOM:
195 setDispVert((int)fValue);
196 break;
197 case MOUSEX:
198 case MOUSEY:
199 case MOUSEBUTTON:
200 case MOUSEPRESSED:
201 break;
202 case ENABLE_NOTEIN:
203 enableNoteIn->setChecked((bool)fValue);
204 break;
205 case ENABLE_VELIN:
206 enableVelIn->setChecked((bool)fValue);
207 break;
208 case ENABLE_NOTEOFF:
209 enableNoteOff->setChecked((bool)fValue);
210 break;
211 case ENABLE_RESTARTBYKBD:
212 enableRestartByKbd->setChecked((bool)fValue);
213 break;
214 case ENABLE_TRIGBYKBD:
215 enableTrigByKbd->setChecked((bool)fValue);
216 break;
217 case ENABLE_TRIGLEGATO:
218 enableTrigLegato->setChecked((bool)fValue);
219 break;
220 case RECORD:
221 recordAction->setChecked((bool)fValue);
222 break;
223 case DEFER:
224 deferChangesAction->setChecked((bool)fValue);
225 break;
226 case CURR_RECSTEP:
227 //record step has changed
228 screen->setCurrentRecStep((int)fValue);
229 screen->update();
230 break;
231 case TRANSPORT_MODE:
232 transportBox->setChecked((bool)fValue);
233 break;
234 case TEMPO:
235 tempoSpin->setValue((int)fValue);
236 break;
237 default:
238 break;
239 }
240 }
241 }
242
sendUIisUp(bool on)243 void SeqWidgetLV2::sendUIisUp(bool on)
244 {
245 const QMidiArpURIs* uris = &m_uris;
246 uint8_t obj_buf[64];
247 int state;
248
249 LV2_Atom_Forge_Frame frame;
250 lv2_atom_forge_frame_time(&forge, 0);
251
252 /* prepare forge buffer and initialize atom-sequence */
253 lv2_atom_forge_set_buffer(&forge, obj_buf, 16);
254
255 if (on) state = uris->ui_up; else state=uris->ui_down;
256
257 LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 1, state);
258
259 /* close-off frame */
260 lv2_atom_forge_pop(&forge, &frame);
261 writeFunction(m_controller, MidiIn, lv2_atom_total_size(msg), uris->atom_eventTransfer, msg);
262 }
263
receiveWave(LV2_Atom * atom)264 void SeqWidgetLV2::receiveWave(LV2_Atom* atom)
265 {
266 QMidiArpURIs* const uris = &m_uris;
267 if ( (atom->type != uris->atom_Blank)
268 && (atom->type != uris->atom_Object)) return;
269
270 /* cast the buffer to Atom Object */
271 LV2_Atom_Object* obj = (LV2_Atom_Object*)atom;
272 LV2_Atom *a0 = NULL;
273 lv2_atom_object_get(obj, uris->hex_customwave, &a0, NULL);
274 if (obj->body.otype != uris->hex_customwave) return;
275
276 /* handle wave' data vector */
277 LV2_Atom_Vector* voi = (LV2_Atom_Vector*)LV2_ATOM_BODY(a0);
278 /* check if atom is indeed a vector of the expected type*/
279 if (voi->atom.type != uris->atom_Int) return;
280
281 /* get number of elements in vector
282 * = (raw 8bit data-length - header-length) / sizeof(expected data type:int) */
283 const size_t n_elem = (a0->size - sizeof(LV2_Atom_Vector_Body)) / voi->atom.size;
284 /* typecast, dereference pointer to vector */
285 const int *recdata = (int*) LV2_ATOM_BODY(&voi->atom);
286 res = resBox->currentText().toInt();
287 size = sizeBox->currentText().toInt();
288 for (uint l1 = 0; l1 < n_elem; l1++) {
289 receiveWavePoint(l1, recdata[l1]);
290 }
291 if (n_elem < (uint)data.count()) data.resize(res * size + 1);
292 screen->updateData(data);
293 screen->update();
294 }
295
receiveWavePoint(int index,int value)296 void SeqWidgetLV2::receiveWavePoint(int index, int value)
297 {
298 Sample sample;
299 if (value < 0) {
300 sample.muted = true;
301 value = -value;
302 }
303 else sample.muted = false;
304 sample.value = value;
305 sample.tick = index * TPQN / res;
306 if (index >= data.count()) data.append(sample);
307 else data.replace(index, sample);
308 }
309
mapBool(bool on)310 void SeqWidgetLV2::mapBool(bool on)
311 {
312 float value = (float)on;
313 if (muteOutAction == sender()) {
314 updateParam(MUTE, value);
315 screen->setMuted(value);
316 }
317 else if (enableNoteIn == sender()) updateParam(ENABLE_NOTEIN, value);
318 else if (enableVelIn == sender()) updateParam(ENABLE_VELIN, value);
319 else if (enableNoteOff == sender()) updateParam(ENABLE_NOTEOFF, value);
320 else if (enableRestartByKbd == sender()) updateParam(ENABLE_RESTARTBYKBD, value);
321 else if (enableTrigByKbd == sender()) updateParam(ENABLE_TRIGBYKBD, value);
322 else if (enableTrigLegato == sender()) updateParam(ENABLE_TRIGLEGATO, value);
323 else if (recordAction == sender()) updateParam(RECORD, value);
324 else if (deferChangesAction == sender()) updateParam(DEFER, value);
325 else if (transportBox == sender()) updateParam(TRANSPORT_MODE, value);
326 }
327
mapMouse(double mouseX,double mouseY,int buttons,int pressed)328 void SeqWidgetLV2::mapMouse(double mouseX, double mouseY, int buttons, int pressed)
329 {
330 updateParam(MOUSEX, mouseX);
331 updateParam(MOUSEY, mouseY);
332 updateParam(MOUSEBUTTON, buttons);
333 updateParam(MOUSEPRESSED, pressed); //mouseMoved or pressed
334 updateParam(LOOPMARKER, screen->loopMarker);
335 }
336
mapParam(int value)337 void SeqWidgetLV2::mapParam(int value)
338 {
339 if (velocity == sender()) updateParam(VELOCITY, value);
340 else if (notelength == sender()) updateParam(NOTELENGTH, value);
341 else if (resBox == sender()) updateParam(RESOLUTION, value);
342 else if (sizeBox == sender()) updateParam(SIZE, value);
343 else if (transpose == sender()) updateParam(TRANSPOSE, value);
344 else if (channelOut == sender()) updateParam(CH_OUT, value);
345 else if (chIn == sender()) updateParam(CH_IN, value);
346 else if (indexIn[0] == sender()) updateParam(INDEX_IN1, value);
347 else if (indexIn[1] == sender()) updateParam(INDEX_IN2, value);
348 else if (rangeIn[0] == sender()) updateParam(RANGE_IN1, value);
349 else if (rangeIn[1] == sender()) updateParam(RANGE_IN2, value);
350 else if (loopBox == sender()) updateParam(LOOPMODE, value);
351 else if (tempoSpin == sender()) updateParam(TEMPO, value);
352 else if (dispSignalMapper == sender()) updateParam(DISPLAY_ZOOM, value);
353 }
354
updateParam(int index,float fValue) const355 void SeqWidgetLV2::updateParam(int index, float fValue) const
356 {
357 writeFunction(m_controller, index, sizeof(float), 0, &fValue);
358 }
359
MidiSeqLV2ui_instantiate(const LV2UI_Descriptor *,const char *,const char *,LV2UI_Write_Function write_function,LV2UI_Controller controller,LV2UI_Widget * widget,const LV2_Feature * const * host_features)360 static LV2UI_Handle MidiSeqLV2ui_instantiate (
361 const LV2UI_Descriptor *, const char *, const char *,
362 LV2UI_Write_Function write_function,
363 LV2UI_Controller controller, LV2UI_Widget *widget,
364 const LV2_Feature *const *host_features )
365 {
366 SeqWidgetLV2 *pWidget = new SeqWidgetLV2(
367 controller, write_function, host_features);
368 *widget = pWidget;
369 return pWidget;
370 }
371
MidiSeqLV2ui_cleanup(LV2UI_Handle ui)372 static void MidiSeqLV2ui_cleanup ( LV2UI_Handle ui )
373 {
374 SeqWidgetLV2 *pWidget = static_cast<SeqWidgetLV2 *> (ui);
375 if (pWidget) {
376 pWidget->sendUIisUp(false);
377 delete pWidget;
378 }
379 }
380
MidiSeqLV2ui_port_event(LV2UI_Handle ui,uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)381 static void MidiSeqLV2ui_port_event (
382 LV2UI_Handle ui, uint32_t port_index,
383 uint32_t buffer_size, uint32_t format, const void *buffer )
384 {
385 SeqWidgetLV2 *pWidget = static_cast<SeqWidgetLV2 *> (ui);
386 if (pWidget)
387 pWidget->port_event(port_index, buffer_size, format, buffer);
388 }
389
MidiSeqLV2ui_extension_data(const char *)390 static const void *MidiSeqLV2ui_extension_data ( const char * )
391 {
392 return NULL;
393 }
394
395 static const LV2UI_Descriptor MidiSeqLV2ui_descriptor =
396 {
397 QMIDIARP_SEQ_LV2UI_URI,
398 MidiSeqLV2ui_instantiate,
399 MidiSeqLV2ui_cleanup,
400 MidiSeqLV2ui_port_event,
401 MidiSeqLV2ui_extension_data
402 };
403
lv2ui_descriptor(uint32_t index)404 LV2_SYMBOL_EXPORT const LV2UI_Descriptor *lv2ui_descriptor ( uint32_t index )
405 {
406 return (index == 0 ? &MidiSeqLV2ui_descriptor : NULL);
407 }
408