andrewm@0
|
1 /*
|
andrewm@0
|
2 TouchKeys: multi-touch musical keyboard control software
|
andrewm@0
|
3 Copyright (c) 2013 Andrew McPherson
|
andrewm@0
|
4
|
andrewm@0
|
5 This program is free software: you can redistribute it and/or modify
|
andrewm@0
|
6 it under the terms of the GNU General Public License as published by
|
andrewm@0
|
7 the Free Software Foundation, either version 3 of the License, or
|
andrewm@0
|
8 (at your option) any later version.
|
andrewm@0
|
9
|
andrewm@0
|
10 This program is distributed in the hope that it will be useful,
|
andrewm@0
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
andrewm@0
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
andrewm@0
|
13 GNU General Public License for more details.
|
andrewm@0
|
14
|
andrewm@0
|
15 You should have received a copy of the GNU General Public License
|
andrewm@0
|
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
andrewm@0
|
17
|
andrewm@0
|
18 =====================================================================
|
andrewm@0
|
19
|
andrewm@0
|
20 PianoKeyboard.h: main class that keeps track of each key (and pedal)
|
andrewm@0
|
21 on the keyboard, while also providing hooks for mapping and scheduling
|
andrewm@0
|
22 of events. One shared instance of this class is used widely throughout
|
andrewm@0
|
23 the program.
|
andrewm@0
|
24 */
|
andrewm@0
|
25
|
andrewm@0
|
26 #ifndef KEYCONTROL_PIANOKEYBOARD_H
|
andrewm@0
|
27 #define KEYCONTROL_PIANOKEYBOARD_H
|
andrewm@0
|
28
|
andrewm@0
|
29 #include <iostream>
|
andrewm@0
|
30 #include <fstream>
|
andrewm@0
|
31 #include <map>
|
andrewm@0
|
32 #include "../JuceLibraryCode/JuceHeader.h"
|
andrewm@0
|
33 #include "../Utility/Types.h"
|
andrewm@0
|
34 #include "../Utility/Node.h"
|
andrewm@0
|
35 #include "PianoKey.h"
|
andrewm@0
|
36 #include "PianoPedal.h"
|
andrewm@0
|
37 #include "../Display/KeyboardDisplay.h"
|
andrewm@0
|
38 #include "../Display/KeyPositionGraphDisplay.h"
|
andrewm@0
|
39 #include "Osc.h"
|
andrewm@0
|
40 #include "../Utility/Scheduler.h"
|
andrewm@0
|
41
|
andrewm@0
|
42
|
andrewm@0
|
43 #define NUM_KEYS 88
|
andrewm@0
|
44 #define NUM_PEDALS 3
|
andrewm@0
|
45
|
andrewm@0
|
46 enum { // Index of each pedal in the buffers
|
andrewm@0
|
47 kPedalDamper = 0,
|
andrewm@0
|
48 kPedalSostenuto = 1,
|
andrewm@0
|
49 kPedalUnaCorda = 2,
|
andrewm@0
|
50 kNumPedals
|
andrewm@0
|
51 };
|
andrewm@0
|
52
|
andrewm@0
|
53 const int kDefaultKeyHistoryLength = 8192;
|
andrewm@0
|
54 const int kDefaultPedalHistoryLength = 1024;
|
andrewm@0
|
55
|
andrewm@0
|
56 class TouchkeyDevice;
|
andrewm@0
|
57 class Mapping;
|
andrewm@0
|
58 class MidiOutputController;
|
andrewm@0
|
59 class MappingFactory;
|
andrewm@0
|
60 class MidiKeyboardSegment;
|
andrewm@0
|
61 class MappingScheduler;
|
andrewm@0
|
62
|
andrewm@0
|
63 /*
|
andrewm@0
|
64 * PianoKeyboard
|
andrewm@0
|
65 *
|
andrewm@0
|
66 * Base class that implements all the functionality needed to measure and process
|
andrewm@0
|
67 * real-time piano key motion. This class is abstract in that it doesn't define a particular
|
andrewm@0
|
68 * source for the key motion data. The data source depends on the hardware used: PianoBar,
|
andrewm@0
|
69 * PnoScan II, CEUS, etc.
|
andrewm@0
|
70 *
|
andrewm@0
|
71 * PianoKeyboard is a source of OSC messages (generated by various key actions). Hence,
|
andrewm@0
|
72 * objects can register with it to implement custom behavior for different actions.
|
andrewm@0
|
73 */
|
andrewm@0
|
74
|
andrewm@0
|
75 class PianoKeyboard : public OscMessageSource {
|
andrewm@0
|
76 public:
|
andrewm@0
|
77 ofstream testLog_;
|
andrewm@0
|
78
|
andrewm@0
|
79 // ***** Constructors *****
|
andrewm@0
|
80
|
andrewm@0
|
81 PianoKeyboard();
|
andrewm@0
|
82
|
andrewm@0
|
83 // ***** Destructor *****
|
andrewm@0
|
84
|
andrewm@0
|
85 ~PianoKeyboard();
|
andrewm@0
|
86
|
andrewm@0
|
87 // ***** Control Methods *****
|
andrewm@0
|
88 //
|
andrewm@0
|
89 // These methods start and stop the device and all associated processing. The implementation
|
andrewm@0
|
90 // is specific to the hardware used. Methods return true on success.
|
andrewm@0
|
91
|
andrewm@0
|
92 //virtual bool start() = 0;
|
andrewm@0
|
93 //virtual bool stop() = 0;
|
andrewm@0
|
94
|
andrewm@0
|
95 bool isInitialized() { return isInitialized_; }
|
andrewm@0
|
96 bool isRunning() { return isRunning_; }
|
andrewm@0
|
97
|
andrewm@0
|
98 void reset();
|
andrewm@0
|
99
|
andrewm@0
|
100 std::pair<int, int> keyboardGUIRange() { return std::pair<int, int>(lowestMidiNote_, highestMidiNote_); }
|
andrewm@0
|
101 void setKeyboardGUIRange(int lowest, int highest);
|
andrewm@0
|
102
|
andrewm@0
|
103 int numberOfPedals() { return numberOfPedals_; }
|
andrewm@0
|
104 void setNumberOfPedals(int number);
|
andrewm@0
|
105
|
andrewm@0
|
106 // ***** Communication Links and Methods *****
|
andrewm@0
|
107
|
andrewm@0
|
108 // Set/query the output controller
|
andrewm@0
|
109 MidiOutputController* midiOutputController() { return midiOutputController_; }
|
andrewm@0
|
110 void setMidiOutputController(MidiOutputController* ct) { midiOutputController_ = ct; }
|
andrewm@0
|
111
|
andrewm@0
|
112 // Set reference to GUI displays
|
andrewm@0
|
113 KeyboardDisplay* gui() { return gui_; }
|
andrewm@0
|
114 void setGUI(KeyboardDisplay* gui);
|
andrewm@0
|
115 KeyPositionGraphDisplay *graphGUI() { return graphGui_; }
|
andrewm@0
|
116 void setGraphGUI(KeyPositionGraphDisplay* newGui) { graphGui_ = newGui; }
|
andrewm@0
|
117
|
andrewm@0
|
118 // OSC transmitter handles the mechanics of sending messages to one or more targets
|
andrewm@0
|
119 void setOscTransmitter(OscTransmitter* trans) { oscTransmitter_ = trans; }
|
andrewm@0
|
120
|
andrewm@0
|
121 // TouchkeyDevice handles communication with the touch-sensor/piano-scanner hardware
|
andrewm@0
|
122 void setTouchkeyDevice(TouchkeyDevice* device) { touchkeyDevice_ = device; }
|
andrewm@0
|
123
|
andrewm@0
|
124 // Send a named message by OSC (and potentially by MIDI or other means if suitable listeners
|
andrewm@0
|
125 // are enabled)
|
andrewm@0
|
126 void sendMessage(const char * path, const char * type, ...);
|
andrewm@0
|
127
|
andrewm@0
|
128 // ***** Scheduling Methods *****
|
andrewm@0
|
129
|
andrewm@0
|
130 // Add or remove events from the scheduler queue
|
andrewm@0
|
131 void scheduleEvent(void *who, Scheduler::action func, timestamp_type timestamp) {
|
andrewm@0
|
132 futureEventScheduler_.schedule(who, func, timestamp);
|
andrewm@0
|
133 }
|
andrewm@0
|
134 void unscheduleEvent(void *who) {
|
andrewm@0
|
135 futureEventScheduler_.unschedule(who);
|
andrewm@0
|
136 }
|
andrewm@0
|
137 void unscheduleEvent(void *who, timestamp_type timestamp) {
|
andrewm@0
|
138 futureEventScheduler_.unschedule(who, timestamp);
|
andrewm@0
|
139 }
|
andrewm@0
|
140
|
andrewm@0
|
141 // Return the current timestamp associated with the scheduler
|
andrewm@0
|
142 timestamp_type schedulerCurrentTimestamp() { return futureEventScheduler_.currentTimestamp(); }
|
andrewm@0
|
143
|
andrewm@0
|
144 // ***** Individual Key/Pedal Methods *****
|
andrewm@0
|
145
|
andrewm@0
|
146 // Access to individual keys and pedals
|
andrewm@0
|
147 PianoKey* key(int note) {
|
andrewm@0
|
148 //if(note < lowestMidiNote_ || note > highestMidiNote_)
|
andrewm@0
|
149 // return 0;
|
andrewm@0
|
150 //return keys_[note - lowestMidiNote_];
|
andrewm@0
|
151 if(note < 0 || note > 127)
|
andrewm@0
|
152 return 0;
|
andrewm@0
|
153 return keys_[note];
|
andrewm@0
|
154 }
|
andrewm@0
|
155 PianoPedal* pedal(int pedal) {
|
andrewm@0
|
156 if(pedal < 0 || pedal >= numberOfPedals_)
|
andrewm@0
|
157 return 0;
|
andrewm@0
|
158 return pedals_[pedal];
|
andrewm@0
|
159 }
|
andrewm@0
|
160
|
andrewm@0
|
161 // Keys and pedals are enabled by default. If one has been disabled, reenable it so it reads data
|
andrewm@0
|
162 // and triggers notes, as normal.
|
andrewm@0
|
163 void enableKey(int key);
|
andrewm@0
|
164 void enablePedal(int pedal);
|
andrewm@0
|
165
|
andrewm@0
|
166 // Disable a key or pedal from causing any activity to occur.
|
andrewm@0
|
167 void disableKey(int key);
|
andrewm@0
|
168 void disablePedal(int pedal);
|
andrewm@0
|
169
|
andrewm@0
|
170 // Leave a key enabled, but terminate any activity it has initiated and return it to the idle state.
|
andrewm@0
|
171 // If the key is active because of a hardware problem, this may be a short-term solution at best, requiring
|
andrewm@0
|
172 // the key to be disabled until the problem can be properly resolved.
|
andrewm@0
|
173 void forceKeyIdle(int key);
|
andrewm@0
|
174
|
andrewm@0
|
175 // Set the color of an RGB LED for the given key, if relevant hardware is present
|
andrewm@0
|
176 void setKeyLEDColorRGB(const int note, const float red, const float green, const float blue);
|
andrewm@0
|
177 void setKeyLEDColorHSV(const int note, const float hue, const float saturation, const float value);
|
andrewm@0
|
178 void setAllKeyLEDsOff();
|
andrewm@0
|
179
|
andrewm@0
|
180 // ***** Mapping Methods *****
|
andrewm@0
|
181 // Mappings are identified by the MIDI note they affect and by
|
andrewm@0
|
182 // their owner object.
|
andrewm@0
|
183 void addMapping(int noteNumber, Mapping* mapping); // Add a new mapping to the container
|
andrewm@0
|
184 void removeMapping(int noteNumber); // Remove a mapping from the container
|
andrewm@0
|
185 Mapping* mapping(int noteNumber); // Look up a mapping with the given note and owner
|
andrewm@0
|
186 std::vector<int> activeMappings(); // Return a list of all active note mappings
|
andrewm@0
|
187 void clearMappings(); // Remove all mappings
|
andrewm@0
|
188
|
andrewm@0
|
189 // Managing the mapping factories
|
andrewm@0
|
190 MappingFactory *mappingFactory(MidiKeyboardSegment* segment) {
|
andrewm@0
|
191 ScopedReadLock sl(mappingFactoriesMutex_);
|
andrewm@0
|
192 if(mappingFactories_.count(segment) == 0)
|
andrewm@0
|
193 return 0;
|
andrewm@0
|
194 return mappingFactories_[segment];
|
andrewm@0
|
195 }
|
andrewm@0
|
196 void setMappingFactory(MidiKeyboardSegment* segment, MappingFactory *factory) {
|
andrewm@0
|
197 ScopedWriteLock sl(mappingFactoriesMutex_);
|
andrewm@0
|
198 mappingFactories_[segment] = factory;
|
andrewm@0
|
199 }
|
andrewm@0
|
200 void removeMappingFactory(MidiKeyboardSegment* segment) {
|
andrewm@0
|
201 ScopedWriteLock sl(mappingFactoriesMutex_);
|
andrewm@0
|
202 if(mappingFactories_.count(segment) > 0)
|
andrewm@0
|
203 mappingFactories_.erase(segment);
|
andrewm@0
|
204 }
|
andrewm@0
|
205
|
andrewm@0
|
206 // Passing data to all mapping factories; these methods are not specific to a particular
|
andrewm@0
|
207 // MIDI input segment so we need to check with each factory whether it wants this data.
|
andrewm@0
|
208 void tellAllMappingFactoriesTouchBegan(int noteNumber, bool midiNoteIsOn, bool keyMotionActive,
|
andrewm@0
|
209 Node<KeyTouchFrame>* touchBuffer,
|
andrewm@0
|
210 Node<key_position>* positionBuffer,
|
andrewm@0
|
211 KeyPositionTracker* positionTracker);
|
andrewm@0
|
212 void tellAllMappingFactoriesTouchEnded(int noteNumber, bool midiNoteIsOn, bool keyMotionActive,
|
andrewm@0
|
213 Node<KeyTouchFrame>* touchBuffer,
|
andrewm@0
|
214 Node<key_position>* positionBuffer,
|
andrewm@0
|
215 KeyPositionTracker* positionTracker);
|
andrewm@0
|
216 void tellAllMappingFactoriesKeyMotionActive(int noteNumber, bool midiNoteIsOn, bool touchIsOn,
|
andrewm@0
|
217 Node<KeyTouchFrame>* touchBuffer,
|
andrewm@0
|
218 Node<key_position>* positionBuffer,
|
andrewm@0
|
219 KeyPositionTracker* positionTracker);
|
andrewm@0
|
220 void tellAllMappingFactoriesKeyMotionIdle(int noteNumber, bool midiNoteIsOn, bool touchIsOn,
|
andrewm@0
|
221 Node<KeyTouchFrame>* touchBuffer,
|
andrewm@0
|
222 Node<key_position>* positionBuffer,
|
andrewm@0
|
223 KeyPositionTracker* positionTracker);
|
andrewm@0
|
224
|
andrewm@0
|
225 MappingScheduler& mappingScheduler() { return *mappingScheduler_; }
|
andrewm@0
|
226
|
andrewm@0
|
227 // ***** Member Variables *****
|
andrewm@0
|
228 public:
|
andrewm@0
|
229 // This mutex is grabbed by any thread which is supplying performance
|
andrewm@0
|
230 // data (MIDI or touch). By synchronizing access once centrally, we
|
andrewm@0
|
231 // can avoid many other lock scenarios in individual objects. The object
|
andrewm@0
|
232 // is declared public so it can be used in ScopedLocks.
|
andrewm@0
|
233 CriticalSection performanceDataMutex_;
|
andrewm@0
|
234
|
andrewm@0
|
235 private:
|
andrewm@0
|
236 // Individual key and pedal data structures
|
andrewm@0
|
237 std::vector<PianoKey*> keys_;
|
andrewm@0
|
238 std::vector<PianoPedal*> pedals_;
|
andrewm@0
|
239
|
andrewm@0
|
240 // Reference to GUI display (if present)
|
andrewm@0
|
241 KeyboardDisplay* gui_;
|
andrewm@0
|
242 KeyPositionGraphDisplay *graphGui_;
|
andrewm@0
|
243
|
andrewm@0
|
244 // Reference to the MIDI output controller
|
andrewm@0
|
245 MidiOutputController* midiOutputController_;
|
andrewm@0
|
246
|
andrewm@0
|
247 // Reference to message transmitter class
|
andrewm@0
|
248 OscTransmitter* oscTransmitter_;
|
andrewm@0
|
249
|
andrewm@0
|
250 // Reference to TouchKey hardware controller class
|
andrewm@0
|
251 TouchkeyDevice* touchkeyDevice_;
|
andrewm@0
|
252
|
andrewm@0
|
253 // Keyboard range, expressed in MIDI note numbers
|
andrewm@0
|
254 int lowestMidiNote_, highestMidiNote_;
|
andrewm@0
|
255 int numberOfPedals_;
|
andrewm@0
|
256
|
andrewm@0
|
257 bool isInitialized_;
|
andrewm@0
|
258 bool isRunning_;
|
andrewm@0
|
259 bool isCalibrated_;
|
andrewm@0
|
260 bool calibrationInProgress_;
|
andrewm@0
|
261
|
andrewm@0
|
262 // Mapping objects associated with particular messages
|
andrewm@0
|
263 // When a sendMessage() command is received, it checks the message
|
andrewm@0
|
264 // against this set of possible listeners to see if it matches the
|
andrewm@0
|
265 // path. If so, the handler function is called.
|
andrewm@0
|
266 std::multimap<std::string, OscHandler*> messageListeners_;
|
andrewm@0
|
267
|
andrewm@0
|
268 // This object can be used to schedule events to be executed at future timestamps,
|
andrewm@0
|
269 // for example to handle timeouts. This will often be called from within a particular
|
andrewm@0
|
270 // key, but we should maintain one central repository for these events.
|
andrewm@0
|
271 Scheduler futureEventScheduler_;
|
andrewm@0
|
272
|
andrewm@0
|
273 // Data related to mappings for active notes
|
andrewm@0
|
274 std::map<int, Mapping*> mappings_; // Mappings from key motion to sound
|
andrewm@0
|
275
|
andrewm@0
|
276 // Collection of mapping factories organised by segment of the keyboard. Different
|
andrewm@0
|
277 // segments may have different mappings
|
andrewm@0
|
278 std::map<MidiKeyboardSegment*, MappingFactory*> mappingFactories_;
|
andrewm@0
|
279 ReadWriteLock mappingFactoriesMutex_;
|
andrewm@0
|
280
|
andrewm@0
|
281 // Scheduler specifically used for coordinating mappings
|
andrewm@0
|
282 MappingScheduler *mappingScheduler_;
|
andrewm@0
|
283
|
andrewm@0
|
284 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PianoKeyboard)
|
andrewm@0
|
285 };
|
andrewm@0
|
286
|
andrewm@0
|
287 #endif /* KEYCONTROL_PIANOKEYBOARD_H */ |