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