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