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