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 "[ExternalController]"
15 
16 #include "ExternalController.h"
17 
18 #include "misc/ConfigGroups.h"
19 #include "misc/Debug.h"
20 #include "MappedEvent.h"
21 #include "document/RosegardenDocument.h"
22 #include "gui/application/RosegardenMainWindow.h"
23 #include "sequencer/RosegardenSequencer.h"
24 #include "gui/seqmanager/SequenceManager.h"
25 #include "misc/Strings.h"
26 
27 #include <QSettings>
28 
29 namespace Rosegarden
30 {
31 
32 
33 ExternalController &
self()34 ExternalController::self()
35 {
36     // Guaranteed in C++11 to be lazy initialized and thread-safe.
37     // See ISO/IEC 14882:2011 6.7(4).
38     static ExternalController instance;
39 
40     // ??? To avoid the static destruction order fiasco, we might want to
41     //     switch to keeping and returning a shared_ptr.  That would
42     //     allow callers to hold on to a shared_ptr until they are done.
43     //     For now, hopefully no one else with static lifetime tries to
44     //     access this after it is destroyed.
45     //
46     //     Note that std::shared_ptr is thread-safe, unlike QSharedPointer.
47     //     This means that we can safely return a shared_ptr to any thread
48     //     that asks for one.
49 
50     return instance;
51 }
52 
ExternalController()53 ExternalController::ExternalController() :
54     activeWindow(Main),
55     m_instrumentId(NoInstrument),
56     m_playing(false),
57     m_recording(false)
58 {
59     QSettings settings;
60     settings.beginGroup(GeneralOptionsConfigGroup);
61     m_controllerType = static_cast<ControllerType>(
62             settings.value("controller_type", CT_RosegardenNative).toInt());
63 }
64 
connectRMW(RosegardenMainWindow * rmw)65 void ExternalController::connectRMW(RosegardenMainWindow *rmw)
66 {
67     // If it seems like this isn't connecting, make sure the ExternalController
68     // instance was created by the UI thread.  Signals/Slots cannot be
69     // connected across threads.
70 
71     connect(rmw, &RosegardenMainWindow::documentLoaded,
72             this, &ExternalController::slotDocumentLoaded);
73 
74     // Might as well hook up for CCs as well.
75     connect(Instrument::getStaticSignals().data(),
76                 &InstrumentStaticSignals::controlChange,
77             this,
78             &ExternalController::slotControlChange);
79 
80     SequenceManager *sequenceManager = rmw->getSequenceManager();
81 
82     // And transport changes.
83     connect(sequenceManager, &SequenceManager::signalPlaying,
84             this, &ExternalController::slotPlaying);
85     connect(sequenceManager, &SequenceManager::signalRecording,
86             this, &ExternalController::slotRecording);
87 }
88 
setType(ControllerType controllerType)89 void ExternalController::setType(ControllerType controllerType)
90 {
91     m_controllerType = controllerType;
92 
93     // Write to .conf so this becomes the default.
94     QSettings settings;
95     settings.beginGroup(GeneralOptionsConfigGroup);
96     settings.setValue("controller_type", static_cast<int>(m_controllerType));
97 
98     if (m_controllerType == CT_KorgNanoKontrol2)
99         korgNanoKontrol2.init();
100 }
101 
isEnabled()102 bool ExternalController::isEnabled()
103 {
104     static bool cacheValid = false;
105     static bool enabled = false;
106 
107     if (cacheValid)
108         return enabled;
109 
110     // Cache is not valid.  Prime it.
111 
112     QSettings settings;
113     settings.beginGroup(GeneralOptionsConfigGroup);
114     // We default to disabling the external controller port because some
115     // software synths are very aggressive about making connections and
116     // can end up connecting to the external controller port.  When that
117     // happens, typically only channel 1 can be heard since
118     // RosegardenMainWindow will send volume 0 to all other channels.
119     // External controller can be re-enabled via the preferences.  When
120     // enabled, be sure to look closely at the connections being made
121     // and make sure external controller is only being connected to an
122     // appropriate control surface.
123     enabled = settings.value("external_controller", false).toBool();
124 
125     cacheValid = true;
126 
127     return enabled;
128 }
129 
processEvent(const MappedEvent * event)130 void ExternalController::processEvent(const MappedEvent *event)
131 {
132     switch (m_controllerType) {
133     case CT_RosegardenNative:
134         processRGNative(event);
135         break;
136     case CT_KorgNanoKontrol2:
137         korgNanoKontrol2.processEvent(event);
138         break;
139     }
140 }
141 
processRGNative(const MappedEvent * event)142 void ExternalController::processRGNative(const MappedEvent *event)
143 {
144     if (event->getType() == MappedEvent::MidiController) {
145 
146         // If switch window CC (CONTROLLER_SWITCH_WINDOW)
147         if (event->getData1() == 81) {
148 
149             const int value = event->getData2();
150 
151             if (value < 42) {  // Main Window
152 
153                 RosegardenMainWindow::self()->openWindow(Main);
154 
155                 return;
156 
157             } else if (value < 84) {  // Audio Mixer
158 
159                 RosegardenMainWindow::self()->openWindow(AudioMixer);
160 
161                 return;
162 
163             } else {  // MIDI Mixer
164 
165                 RosegardenMainWindow::self()->openWindow(MidiMixer);
166 
167                 return;
168 
169             }
170         }
171     }
172 
173     // Delegate to the currently active window.
174     switch (activeWindow)
175     {
176     case Main:
177         emit externalControllerRMVW(event);
178         break;
179     case MidiMixer:
180         emit externalControllerMMW(event);
181         break;
182     case AudioMixer:
183         emit externalControllerAMW2(event);
184         break;
185     }
186 }
187 
send(MidiByte channel,MidiByte controlNumber,MidiByte value)188 void ExternalController::send(
189         MidiByte channel, MidiByte controlNumber, MidiByte value)
190 {
191     // Not enabled?  Bail.
192     if (!isEnabled())
193         return;
194 
195     MappedEvent event(NoInstrument,  // instrumentId is ignored
196                       MappedEvent::MidiController,
197                       controlNumber,
198                       MidiByte(value));
199     event.setRecordedChannel(channel);
200     event.setRecordedDevice(Device::EXTERNAL_CONTROLLER);
201 
202     RosegardenSequencer::getInstance()->processMappedEvent(event);
203 }
204 
sendAllCCs(const Instrument * instrument,MidiByte channel)205 void ExternalController::sendAllCCs(
206         const Instrument *instrument, MidiByte channel)
207 {
208     if (channel == MidiMaxValue)
209         channel = instrument->getNaturalChannel();
210 
211     send(channel,
212          MIDI_CONTROLLER_VOLUME,
213          instrument->getVolumeCC());
214 
215     send(channel,
216          MIDI_CONTROLLER_PAN,
217          instrument->getPanCC());
218 
219     // For MIDI instruments...
220     if (instrument->getType() == Instrument::Midi) {
221 
222         // Also send all the other CCs in case the surface can handle them.
223 
224         StaticControllers staticControllers =
225                 instrument->getStaticControllers();
226 
227         // For each Control Change...
228         for (const ControllerValuePair &pair : staticControllers) {
229 
230             const MidiByte controlNumber = pair.first;
231 
232             // Volume and Pan were already covered above.  Skip.
233             if (controlNumber == MIDI_CONTROLLER_VOLUME)
234                 continue;
235             if (controlNumber == MIDI_CONTROLLER_PAN)
236                 continue;
237 
238             const MidiByte controlValue = pair.second;
239 
240             send(channel, controlNumber, controlValue);
241 
242         }
243 
244     }
245 }
246 
sendSysExHex(const QString & hexString)247 void ExternalController::sendSysExHex(const QString &hexString)
248 {
249     // Not enabled?  Bail.
250     if (!isEnabled())
251         return;
252 
253     // Translate the hex string to bytes.
254 
255     std::string rawString;
256     // For each hex byte (two characters)...
257     for (int i = 0; i < hexString.size(); i += 2) {
258         rawString.push_back(static_cast<char>(
259                 hexString.mid(i, 2).toInt(nullptr, 16)));
260     }
261 
262     // Send it out.
263 
264     sendSysExRaw(rawString);
265 }
266 
267 void
sendSysExRaw(const std::string & rawString)268 ExternalController::sendSysExRaw(const std::string &rawString)
269 {
270     // Not enabled?  Bail.
271     if (!isEnabled())
272         return;
273 
274     // Assemble the SysEx MappedEvent.
275 
276     MappedEvent event(NoInstrument,  // instrumentId is ignored
277                       MappedEvent::MidiSystemMessage,
278                       MIDI_SYSTEM_EXCLUSIVE);
279     event.setRecordedDevice(Device::EXTERNAL_CONTROLLER);
280     event.addDataString(rawString);
281 
282     // Send it out.
283 
284     RosegardenSequencer::getInstance()->processMappedEvent(event);
285 }
286 
getSysEx(std::string &)287 bool ExternalController::getSysEx(std::string & /*rawString*/)
288 {
289 #if 0
290     // This really didn't work.  We need to do some restructuring of
291     // AlsaDriver before we can successfully have conversations with
292     // MIDI equipment over the external controller port.
293 
294     // ??? Need to do a blocking read from the external controller port
295     //     with a 500msec timeout.  How???
296 
297     // Copy sysex data from m_sysexEvent to rawString.
298     rawString = DataBlockRepository::getDataBlockForEvent(&sysexEvent);
299 
300 #endif
301 
302     return true;
303 }
304 
305 void
slotDocumentLoaded(RosegardenDocument * doc)306 ExternalController::slotDocumentLoaded(RosegardenDocument *doc)
307 {
308     // If this is never happening, see the comments in connectRMW() above.
309 
310     if (!doc)
311         return;
312 
313     // Connect to the new document for change notifications.
314     connect(doc, &RosegardenDocument::documentModified,
315             this, &ExternalController::slotDocumentModified);
316 
317     // Force an update to make sure we are in sync with the new document.
318     m_instrumentId = NoInstrument;
319     slotDocumentModified(true);
320 }
321 
322 void
slotDocumentModified(bool)323 ExternalController::slotDocumentModified(bool)
324 {
325     // If this is never happening, see the comments in connectRMW() above.
326 
327     if (m_controllerType == CT_RosegardenNative) {
328 
329         // We only handle updates for RosegardenMainWindow.
330         if (activeWindow != Main)
331             return;
332 
333         RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
334 
335         // Get the selected Track's Instrument.
336         InstrumentId instrumentId =
337                 doc->getComposition().getSelectedInstrumentId();
338 
339         if (instrumentId == NoInstrument)
340             return;
341 
342         // No change to the Track/Instrument?  Bail.
343         if (instrumentId == m_instrumentId)
344             return;
345 
346         m_instrumentId = instrumentId;
347 
348         Instrument *instrument = doc->getStudio().getInstrumentById(instrumentId);
349 
350         if (!instrument)
351             return;
352 
353         // Send out the CCs for the current Track on channel 0.
354         sendAllCCs(instrument, 0);
355 
356         return;
357     }
358 
359     if (m_controllerType == CT_KorgNanoKontrol2) {
360         korgNanoKontrol2.documentModified();
361         return;
362     }
363 }
364 
365 void
slotControlChange(Instrument * instrument,int cc)366 ExternalController::slotControlChange(Instrument *instrument, int cc)
367 {
368     if (m_controllerType == CT_RosegardenNative) {
369         // We only handle updates for RosegardenMainWindow.
370         if (activeWindow != Main)
371             return;
372 
373         // Not our Instrument?  Bail.
374         if (instrument->getId() != m_instrumentId)
375             return;
376 
377         // Fixed channels only.
378         if (!instrument->hasFixedChannel())
379             return;
380 
381         if (cc == MIDI_CONTROLLER_VOLUME) {
382             send(0, MIDI_CONTROLLER_VOLUME, instrument->getVolumeCC());
383             return;
384         }
385 
386         if (cc == MIDI_CONTROLLER_PAN) {
387             send(0, MIDI_CONTROLLER_PAN, instrument->getPanCC());
388             return;
389         }
390 
391         // All other controllers can use Instrument::getControllerValue().
392 
393         send(0, cc, instrument->getControllerValue(cc));
394     }
395 }
396 
397 void
slotPlaying(bool checked)398 ExternalController::slotPlaying(bool checked)
399 {
400     m_playing = checked;
401 
402     if (m_controllerType == CT_KorgNanoKontrol2) {
403         if (m_playing  &&  !m_recording)
404             korgNanoKontrol2.playing();
405         else if (!m_playing  &&  !m_recording)
406             korgNanoKontrol2.stopped();
407         return;
408     }
409 }
410 
411 void
slotRecording(bool checked)412 ExternalController::slotRecording(bool checked)
413 {
414     m_recording = checked;
415 
416     if (m_controllerType == CT_KorgNanoKontrol2) {
417         if (m_recording)
418             korgNanoKontrol2.recording();
419         else if (!m_playing  &&  !m_recording)
420             korgNanoKontrol2.stopped();
421         return;
422     }
423 }
424 
425 
426 }
427