1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 /*
3   Rosegarden
4   A sequencer and musical notation editor.
5   Copyright 2020 the Rosegarden development team.
6 
7   This program is free software; you can redistribute it and/or
8   modify it under the terms of the GNU General Public License as
9   published by the Free Software Foundation; either version 2 of the
10   License, or (at your option) any later version.  See the file
11   COPYING included with this distribution for more information.
12 */
13 
14 #define RG_MODULE_STRING "[KorgNanoKontrol2]"
15 
16 #include "KorgNanoKontrol2.h"
17 
18 #include "base/AudioLevel.h"
19 #include "base/Composition.h"
20 #include "misc/Debug.h"
21 #include "base/Instrument.h"
22 #include "MappedEvent.h"
23 #include "base/QEvents.h"
24 #include "document/RosegardenDocument.h"
25 #include "gui/application/RosegardenMainWindow.h"
26 #include "base/Studio.h"
27 #include "base/Track.h"
28 
29 #include <QCoreApplication>
30 #include <QEvent>
31 
32 #include <vector>
33 
34 namespace Rosegarden
35 {
36 
37 
KorgNanoKontrol2()38 KorgNanoKontrol2::KorgNanoKontrol2() :
39     m_page(0),
40     m_firstRefresh(true),
41     m_play(false),
42     m_record(false),
43     m_stop(false),
44     m_rewind(false),
45     m_fastForward(false),
46     m_cycle(false)
47 {
48 }
49 
init()50 void KorgNanoKontrol2::init()
51 {
52     // Configure the device.
53 
54 #if 0
55 
56     // Confirm expected device.
57 
58     // Send Inquiry Message Request.
59     ExternalController::sendSysExHex("7E7F0601");
60 
61     std::string buffer;
62 
63     // Get Device Inquiry Reply.
64     // We need a synchronous getSysEx() with 500msec(?) timeout.
65     // It also needs to stitch together multiple packets into one until EOX.
66     ExternalController::self()->getSysEx(buffer);
67 
68     QByteArray byteArray(buffer.c_str());
69     RG_DEBUG << "Response: " << byteArray.toHex();
70 
71     // Special handling for this since the hardware version can vary.
72     bool success = checkDeviceInquiryReply(buffer);
73     if (!success) {
74         RG_WARNING << "init(): Did not receive expected Device Inquiry Reply";
75         return;
76     }
77 
78     // Get a dump of the scene.
79     sendSysExRaw(currentSceneDataDumpRequest);
80     getSysEx(buffer);
81 
82     // Compare with Rosegarden scene, bail if same.
83     if (compareSysEx(buffer, rosegardenScene))
84         return;
85 
86     // If the user appears to have customized the scene, ask if it is
87     // ok to clobber it.
88     if (!compareSysEx(buffer, defaultScene))
89     {
90         // Ask user if it is ok to reconfigure the device.
91         QMessageBox::StandardButton reply = QMessageBox::warning(
92                 0,
93                 tr("Rosegarden"),
94                 tr("The connected Korg nanoKONTROL2 is not configured optimally for Rosegarden.  Reconfiguring it will lose any custom settings you've made with the nanoKONTROL2 editor.  Reconfigure?"),
95                 QMessageBox::Yes | QMessageBox::No,
96                 QMessageBox::Yes);
97 
98         // If not, return.
99         if (reply == QMessageBox::No)
100             return;
101     }
102 
103     // Send "Current Scene Data Dump" with Rosegarden scene.
104     sendSysExRaw(rosegardenScene);
105 
106     // Confirm ACK
107     getSysEx(buffer);
108     success = compareSysEx(buffer, dataLoadCompleted);
109     if (!success) {
110         RG_WARNING << "init(): Did not receive expected Data Load Completed ACK";
111         return;
112     }
113 
114     // Send Scene Write Request.
115     sendSysExRaw(sceneWriteRequest);
116 
117     // Confirm Write Completed.
118     getSysEx(buffer);
119     success = compareSysEx(buffer, writeCompleted);
120     if (!success) {
121         RG_WARNING << "init(): Did not receive expected Write Completed";
122         return;
123     }
124 
125     // ??? Do an LED test?  testLEDs() and ask the user to confirm.
126 
127     initLEDs();
128 
129 #endif
130 
131 }
132 
documentModified()133 void KorgNanoKontrol2::documentModified()
134 {
135     refreshLEDs();
136 }
137 
stopped()138 void KorgNanoKontrol2::stopped()
139 {
140     setPlayRecordStopLEDs(false, false, true);
141 }
142 
playing()143 void KorgNanoKontrol2::playing()
144 {
145     setPlayRecordStopLEDs(true, false, false);
146 }
147 
recording()148 void KorgNanoKontrol2::recording()
149 {
150     // ??? This will appear to not work.  See setPlayRecordStopLEDs()
151     //     for an explanation.
152     setPlayRecordStopLEDs(false, true, false);
153 }
154 
processEvent(const MappedEvent * event)155 void KorgNanoKontrol2::processEvent(const MappedEvent *event)
156 {
157     // Not a CC?  Bail.
158     if (event->getType() != MappedEvent::MidiController)
159         return;
160 
161     const MidiByte controlNumber = event->getData1();
162     const MidiByte value = event->getData2();
163 
164     // Record
165     // Handle this first for "speed".
166     if (controlNumber == 45  &&  value == 127) {
167         RosegardenMainWindow::self()->slotRecord();
168         return;
169     }
170 
171     // Play
172     // Handle this second for "speed".
173     if (controlNumber == 41  &&  value == 127) {
174         RosegardenMainWindow::self()->slotPlay();
175         return;
176     }
177 
178     // Volume Faders
179     if (controlNumber <= 7) {
180         processFader(controlNumber, value);
181         return;
182     }
183 
184     // Pan Knobs
185     if (16 <= controlNumber  &&  controlNumber <= 23) {
186         processKnob(controlNumber, value);
187         return;
188     }
189 
190     // Track Left
191     if (controlNumber == 58  &&  value == 127) {
192         if (m_page == 0) {
193             // refresh LEDs regardless in case the user needs to get
194             // them back in sync.
195             refreshLEDs();
196 
197             return;
198         }
199 
200         --m_page;
201 
202         refreshLEDs();
203 
204         // ??? Would be nice to have some feedback in the UI.  E.g. a
205         //     range indicator on the tracks.
206         //TrackButtons *trackButtons = RosegardenMainWindow::self()->
207         //        getView()->getTrackEditor()->getTrackButtons();
208         //trackButtons->setSurfaceRange(m_page * 8, 8);
209         //trackButtons->slotUpdateTracks();
210 
211         return;
212     }
213 
214     // Track Right
215     if (controlNumber == 59  &&  value == 127) {
216         RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
217         Composition &comp = doc->getComposition();
218 
219         if ((m_page + 1) * 8 >= comp.getTracks().size()) {
220             // refresh LEDs regardless in case the user needs to get
221             // them back in sync.
222             refreshLEDs();
223 
224             return;
225         }
226 
227         ++m_page;
228 
229         refreshLEDs();
230 
231         return;
232     }
233 
234     // Stop
235     if (controlNumber == 42  &&  value == 127) {
236         // We cannot call this in the middle of processing incoming MIDI.
237         // The system is not ready for recording to stop.
238         //RosegardenMainWindow::self()->slotStop();
239 
240         // Instead, we queue up a request for
241         // RosegardenMainWindow::customEvent().
242         QEvent *event = new QEvent(Stop);
243         QCoreApplication::postEvent(
244                 RosegardenMainWindow::self(), event);
245 
246         return;
247     }
248 
249     // Rewind
250     if (controlNumber == 43) {
251         // Note: We tried doing this locally, but it crosses threads.
252         //       Using the event queue is thread-safe.
253 
254         QEvent *event = new ButtonEvent(Rewind, (value == 127));
255         QCoreApplication::postEvent(
256                 RosegardenMainWindow::self(), event);
257         return;
258     }
259 
260     // Fast-forward
261     if (controlNumber == 44) {
262         // Note: We tried doing this locally, but it crosses threads.
263         //       Using the event queue is thread-safe.
264 
265         QEvent *event = new ButtonEvent(FastForward, (value == 127));
266         QCoreApplication::postEvent(
267                 RosegardenMainWindow::self(), event);
268         return;
269     }
270 
271     // Cycle (Loop)
272     if (controlNumber == 46  &&  value == 127) {
273         RosegardenMainWindow::self()->toggleLoop();
274         return;
275     }
276 
277     // "S" solo buttons
278     if (32 <= controlNumber  &&  controlNumber <= 39  &&  value == 127) {
279         processSolo(controlNumber);
280         return;
281     }
282 
283     // "M" mute buttons
284     if (48 <= controlNumber  &&  controlNumber <= 55  &&  value == 127) {
285         processMute(controlNumber);
286         return;
287     }
288 
289     // "R" record buttons
290     if (64 <= controlNumber  &&  controlNumber <= 71  &&  value == 127) {
291         processRecord(controlNumber);
292         return;
293     }
294 
295     // Marker Set
296     if (controlNumber == 60  &&  value == 127) {
297         QEvent *event = new QEvent(AddMarker);
298         QCoreApplication::postEvent(
299                 RosegardenMainWindow::self(), event);
300         return;
301     }
302 
303     // Marker Left
304     if (controlNumber == 61  &&  value == 127) {
305         QEvent *event = new QEvent(PreviousMarker);
306         QCoreApplication::postEvent(
307                 RosegardenMainWindow::self(), event);
308         return;
309     }
310 
311     // Marker Right
312     if (controlNumber == 62  &&  value == 127) {
313         QEvent *event = new QEvent(NextMarker);
314         QCoreApplication::postEvent(
315                 RosegardenMainWindow::self(), event);
316         return;
317     }
318 
319 }
320 
processFader(MidiByte controlNumber,MidiByte value)321 void KorgNanoKontrol2::processFader(MidiByte controlNumber, MidiByte value)
322 {
323     const int trackNumber = controlNumber + m_page*8;
324 
325     RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
326     Composition &comp = doc->getComposition();
327 
328     const Track *track = comp.getTrackByPosition(trackNumber);
329     if (!track)
330         return;
331 
332     Studio &studio = doc->getStudio();
333     Instrument *instrument = studio.getInstrumentById(track->getInstrument());
334     if (!instrument)
335         return;
336 
337     if (instrument->getType() == Instrument::Midi) {
338         // If the Instrument has volume...
339         if (instrument->hasController(MIDI_CONTROLLER_VOLUME)) {
340             // Adjust the Instrument and update everyone.
341             instrument->setControllerValue(MIDI_CONTROLLER_VOLUME, value);
342             Instrument::emitControlChange(instrument, MIDI_CONTROLLER_VOLUME);
343             doc->setModified();
344         }
345 
346         return;
347     }
348 
349     // We have an audio instrument or a softsynth...
350 
351     const float dB = AudioLevel::fader_to_dB(
352             value, 127, AudioLevel::ShortFader);
353 
354     instrument->setLevel(dB);
355     Instrument::emitControlChange(instrument, MIDI_CONTROLLER_VOLUME);
356     doc->setModified();
357 }
358 
processKnob(MidiByte controlNumber,MidiByte value)359 void KorgNanoKontrol2::processKnob(MidiByte controlNumber, MidiByte value)
360 {
361     const int trackNumber = controlNumber - 16 + m_page*8;
362 
363     RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
364     Composition &comp = doc->getComposition();
365 
366     const Track *track = comp.getTrackByPosition(trackNumber);
367     if (!track)
368         return;
369 
370     Studio &studio = doc->getStudio();
371     Instrument *instrument = studio.getInstrumentById(track->getInstrument());
372     if (!instrument)
373         return;
374 
375     if (instrument->getType() == Instrument::Midi) {
376         // If the Instrument has volume...
377         if (instrument->hasController(MIDI_CONTROLLER_PAN)) {
378             // Adjust the Instrument and update everyone.
379             instrument->setControllerValue(MIDI_CONTROLLER_PAN, value);
380             Instrument::emitControlChange(instrument, MIDI_CONTROLLER_PAN);
381             doc->setModified();
382         }
383 
384         return;
385     }
386 
387     // We have an audio instrument or a softsynth...
388 
389     instrument->setControllerValue(
390             MIDI_CONTROLLER_PAN,
391             AudioLevel::AudioPanI(value));
392     Instrument::emitControlChange(instrument, MIDI_CONTROLLER_PAN);
393     doc->setModified();
394 }
395 
processSolo(MidiByte controlNumber)396 void KorgNanoKontrol2::processSolo(MidiByte controlNumber)
397 {
398     const int trackNumber = controlNumber - 32 + m_page*8;
399 
400     RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
401     Composition &comp = doc->getComposition();
402 
403     Track *track = comp.getTrackByPosition(trackNumber);
404     if (!track)
405         return;
406 
407     // Toggle solo
408     track->setSolo(!track->isSolo());
409     comp.notifyTrackChanged(track);
410 
411     doc->slotDocumentModified();
412 }
413 
processMute(MidiByte controlNumber)414 void KorgNanoKontrol2::processMute(MidiByte controlNumber)
415 {
416     const int trackNumber = controlNumber - 48 + m_page*8;
417 
418     RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
419     Composition &comp = doc->getComposition();
420 
421     Track *track = comp.getTrackByPosition(trackNumber);
422     if (!track)
423         return;
424 
425     // Toggle mute
426     track->setMuted(!track->isMuted());
427     comp.notifyTrackChanged(track);
428 
429     doc->slotDocumentModified();
430 }
431 
processRecord(MidiByte controlNumber)432 void KorgNanoKontrol2::processRecord(MidiByte controlNumber)
433 {
434     const int trackNumber = controlNumber - 64 + m_page*8;
435 
436     RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
437     Composition &comp = doc->getComposition();
438 
439     Track *track = comp.getTrackByPosition(trackNumber);
440     if (!track)
441         return;
442 
443     // Toggle
444     bool state = !comp.isTrackRecording(track->getId());
445 
446     // Update the Track
447     comp.setTrackRecording(track->getId(), state);
448     comp.notifyTrackChanged(track);
449 
450     doc->checkAudioPath(track);
451 
452     doc->slotDocumentModified();
453 }
454 
testLEDs(bool on)455 void KorgNanoKontrol2::testLEDs(bool on)
456 {
457     const MidiByte value = on ? 127 : 0;
458 
459     for (int i = 0; i < 8; ++i) {
460         // Record
461         ExternalController::send(
462                 0, // channel
463                 64+i, // controlNumber
464                 value);  // value
465         // Mute
466         ExternalController::send(
467                 0, // channel
468                 48+i, // controlNumber
469                 value);  // value
470         // Solo
471         ExternalController::send(
472                 0, // channel
473                 32+i, // controlNumber
474                 value);  // value
475     }
476 
477     // Play
478     ExternalController::send(
479             0, // channel
480             41, // controlNumber
481             value);  // value
482     // Stop
483     ExternalController::send(
484             0, // channel
485             42, // controlNumber
486             value);  // value
487     // Rewind
488     ExternalController::send(
489             0, // channel
490             43, // controlNumber
491             value);  // value
492     // Fast forward
493     ExternalController::send(
494             0, // channel
495             44, // controlNumber
496             value);  // value
497     // Record
498     ExternalController::send(
499             0, // channel
500             45, // controlNumber
501             value);  // value
502     // Cycle
503     ExternalController::send(
504             0, // channel
505             46, // controlNumber
506             value);  // value
507 
508     // The following buttons have no LED:
509     // - Track Left (58)
510     // - Track Right (59)
511     // - Marker Set (60)
512     // - Marker Left (61)
513     // - Marker Left (62)
514 }
515 
initLEDs()516 void KorgNanoKontrol2::initLEDs()
517 {
518     // Turn off all the LEDs and update the cache to match.
519 
520     testLEDs(false);
521 
522     for (int i = 0; i < 8; ++i) {
523         m_solo[i] = false;
524         m_mute[i] = true;
525         m_recordArmed[i] = false;
526     }
527 
528     m_play = false;
529 
530     m_stop = true;
531     // Stop
532     ExternalController::send(
533             0, // channel
534             42, // controlNumber
535             127);  // value
536 
537     m_rewind = false;
538     m_fastForward = false;
539     m_record = false;
540     m_cycle = false;
541 }
542 
refreshLEDs()543 void KorgNanoKontrol2::refreshLEDs()
544 {
545 #if 0
546     // Do another init for testing purposes.  We should be able to
547     // connect to aseqdump and watch the messages go back and forth.
548     init();
549 #endif
550 #if 0
551     static bool on = false;
552     on = !on;
553     testLEDs(on);
554 #endif
555 
556     if (m_firstRefresh) {
557         initLEDs();
558         m_firstRefresh = false;
559     }
560 
561     // Note: The LEDs will not work unless they are set to "External" mode.
562     //       This can be adjusted with the nanoKONTROL2 editor for Windows/Mac
563     //       or you can send two sysex messages to the device.  Eventually,
564     //       this class will be able to send the sysex messages.  See
565     //       init().
566 
567     RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
568     Composition &comp = doc->getComposition();
569 
570     // For each Track
571     for (int i = 0; i < 8; ++i) {
572         const int trackNumber = i + m_page*8;
573 
574         Track *track = comp.getTrackByPosition(trackNumber);
575         if (!track)
576             return;
577 
578         // Solo
579         const bool solo = track->isSolo();
580         // If there was a change...
581         if (solo != m_solo[i]) {
582             ExternalController::send(
583                     0, // channel
584                     32+i, // controlNumber
585                     solo ? 127 : 0);  // value
586             // Update the cache.
587             m_solo[i] = solo;
588         }
589 
590         // Mute
591         const bool mute = track->isMuted();
592         // If there was a change...
593         if (mute != m_mute[i]) {
594             ExternalController::send(
595                     0, // channel
596                     48+i, // controlNumber
597                     mute ? 0 : 127);  // value
598             // Update the cache.
599             m_mute[i] = mute;
600         }
601 
602         // Record
603         const bool recordArmed = comp.isTrackRecording(track->getId());
604         // If there was a change...
605         if (recordArmed != m_recordArmed[i]) {
606             ExternalController::send(
607                     0, // channel
608                     64+i, // controlNumber
609                     recordArmed ? 127 : 0);  // value
610             // Update the cache.
611             m_recordArmed[i] = recordArmed;
612         }
613     }
614 
615     // Cycle
616     const bool cycle = doc->getComposition().isLooping();
617     // If there was a change...
618     if (cycle != m_cycle) {
619         ExternalController::send(
620                 0, // channel
621                 46, // controlNumber
622                 cycle ? 127 : 0);  // value
623         // Update the cache.
624         m_cycle = cycle;
625     }
626 
627 }
628 
setPlayRecordStopLEDs(bool play,bool record,bool stop)629 void KorgNanoKontrol2::setPlayRecordStopLEDs(bool play, bool record, bool stop)
630 {
631     // Note: Tried SequenceManager::getTransportStatus(), but there is no
632     //       way to subscribe for changes to that.
633 
634     if (stop != m_stop) {
635         ExternalController::send(
636                 0, // channel
637                 42, // controlNumber
638                 stop ? 127 : 0);  // value
639         m_stop = stop;
640     }
641 
642     if (play != m_play) {
643         ExternalController::send(
644                 0, // channel
645                 41, // controlNumber
646                 play ? 127 : 0);  // value
647         m_play = play;
648     }
649 
650     if (record != m_record) {
651         // ??? This isn't working.  From watching this with aseqdump, you can
652         //     see that when record begins, the CC's that are sent out the
653         //     external controller port get stuck and are not released until
654         //     we stop recording.  So, when we are in record, sending CCs to
655         //     the external controller port does not work.  Yet another
656         //     argument for separating the external controller port from the
657         //     rest of AlsaDriver.
658         ExternalController::send(
659                 0, // channel
660                 45, // controlNumber
661                 record ? 127 : 0);  // value
662         m_record = record;
663     }
664 
665 }
666 
667 
668 }
669