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 #pragma once
15 
16 #include "base/Instrument.h"
17 #include "KorgNanoKontrol2.h"
18 #include "base/MidiProgram.h"  // For MidiByte
19 
20 #include <QObject>
21 #include <QString>
22 
23 #include <string>
24 
25 
26 namespace Rosegarden
27 {
28 
29 
30 class MappedEvent;
31 class RosegardenDocument;
32 class RosegardenMainWindow;
33 
34 
35 /// Support for the "external controller" port.
36 /**
37  * The external controller port allows MIDI control surfaces to control
38  * the main window, MIDI Mixer window, and Audio Mixer window.
39  *
40  * To use, be sure to enable the external controller port in the preferences:
41  *
42  *   Edit > Preferences... > MIDI > General tab > External controller port
43  *   checkbox.
44  *
45  * After a restart of Rosegarden, an "external controller" port will
46  * appear for MIDI control surfaces to connect to.
47  *
48  * The current window can be selected via CC81.  See processEvent().
49  *
50  * Depending on the current active window, the behavior of the external
51  * controller port will change.  For the main window, the following features
52  * are available via control changes on *channel 1*:
53  *
54  *   CC 82: select Track
55  *   CC  7: Adjust volume on current Track
56  *   CC 10: Adjust pan on current track
57  *   CC  x: Adjust that CC on current track
58  *
59  * See RosegardenMainViewWidget::slotExternalController() for details.
60  *
61  * For the MIDI Mixer window and Audio Mixer window, CCs can be sent on
62  * any of the 16 channels to control those channels on the mixer.  See
63  * MidiMixerWindow::slotExternalController() and
64  * AudioMixerWindow2::slotExternalController() for details.
65  *
66  * CCs are also sent out the external controller port to allow motorized
67  * control surfaces to stay in sync with the current state of the CCs.
68  *
69  */
70 class ExternalController : public QObject
71 {
72     Q_OBJECT
73 
74 public:
75     /// The global instance.
76     static ExternalController &self();
77 
78     static bool isEnabled();
79 
80     enum ControllerType { CT_RosegardenNative, CT_KorgNanoKontrol2 };
81     void setType(ControllerType controllerType);
getType()82     ControllerType getType() const  { return m_controllerType; }
isNative()83     bool isNative() const  { return m_controllerType == CT_RosegardenNative; }
84 
85     /// Call this from RosegardenMainWindow's ctor.
86     /**
87      * This has to be called at the right moment, before the autoload
88      * occurs.  Otherwise the very first RMW::documentLoaded() will not get
89      * in here.
90      */
91     void connectRMW(RosegardenMainWindow *rmw);
92 
93     /// The three windows that currently handle external controller events.
94     enum Window { Main, AudioMixer, MidiMixer };
95     /// The currently active window for external controller events.
96     /**
97      * External controller events are forwarded to the currently active
98      * window.
99      *
100      * Set by the three windows that can handle external controller events.
101      */
102     Window activeWindow;
103 
104     /// Handle MappedEvent's from the external controller port.
105     /**
106      * This routine doesn't handle the events directly.  Instead
107      * it passes the events on to the appropriate handler based
108      * on which control surface has been selected in the preferences.
109      *
110      * See processRGNative() and KorgNanoKontrol2.
111      */
112     void processEvent(const MappedEvent *event);
113 
114     /// Send a control change message out the external controller port.
115     static void send(MidiByte channel, MidiByte controlNumber, MidiByte value);
116     static void sendAllCCs(
117             const Instrument *instrument, MidiByte channel = MidiMaxValue);
118 
119     /// Send SysEx from hex string.  DO NOT include F0/F7.
120     static void sendSysExHex(const QString &hexString);
121     /// Send SysEx from raw string.  DO NOT include F0/F7.
122     static void sendSysExRaw(const std::string &rawString);
123     /// Blocking with timeout.  Returns false if timed out.
124     bool getSysEx(std::string &rawString);
125 
126 signals:
127 
128     /// Connected to RosegardenMainViewWidget::slotExternalController().
129     void externalControllerRMVW(const MappedEvent *event);
130     /// Connected to MidiMixerWindow::slotExternalController().
131     void externalControllerMMW(const MappedEvent *event);
132     /// Connected to AudioMixerWindow::slotExternalController().
133     void externalControllerAMW2(const MappedEvent *event);
134 
135 private slots:
136 
137     /// Connected to RMW::documentLoaded()
138     void slotDocumentLoaded(RosegardenDocument *doc);
139     /// Connected to RD::documentModified()
140     void slotDocumentModified(bool);
141 
142     /// Connected to InstrumentStaticSignals::controlChange().
143     void slotControlChange(Instrument *instrument, int cc);
144 
145     /// Connected to SequenceManager::signalPlaying().
146     void slotPlaying(bool checked);
147     /// Connected to SequenceManager::signalRecording().
148     void slotRecording(bool checked);
149 
150 private:
151 
152     // Access through self() only.
153     ExternalController();
154 
155     ControllerType m_controllerType;
156 
157     /// Cache of the last Instrument we were tracking for RosegardenMainWindow.
158     InstrumentId m_instrumentId;
159 
160     /// Handle Rosegarden's native control surface.
161     /**
162      * This routine handles remote control events received from a
163      * device connected to the "external controller" port.
164      *
165      * This routine handles controller 81 which opens or
166      * brings to the top various windows based on the value.
167      *
168      * All other events are forwarded to the active window.
169      * See m_activeWindow.
170      */
171     void processRGNative(const MappedEvent *event);
172 
173     bool m_playing;
174     bool m_recording;
175 
176     KorgNanoKontrol2 korgNanoKontrol2;
177 
178 };
179 
180 
181 }
182