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 TouchkeyBaseMappingFactory.h: base factory class specifically for
|
andrewm@0
|
21 TouchKeys mappings. It provides a collection of useful methods for
|
andrewm@0
|
22 creating and destroying individual mappings on touch/MIDI onset and
|
andrewm@0
|
23 release, as well as parameter adjustment code and OSC to MIDI conversion.
|
andrewm@0
|
24 This is a template class that must be created with a specific Mapping
|
andrewm@0
|
25 subclass.
|
andrewm@0
|
26 */
|
andrewm@0
|
27
|
andrewm@0
|
28 #ifndef __TouchKeys__TouchkeyBaseMappingFactory__
|
andrewm@0
|
29 #define __TouchKeys__TouchkeyBaseMappingFactory__
|
andrewm@0
|
30
|
andrewm@0
|
31 #include <iostream>
|
andrewm@0
|
32 #include <map>
|
andrewm@0
|
33 #include <sstream>
|
andrewm@0
|
34 #include "MappingFactory.h"
|
andrewm@0
|
35 #include "../TouchKeys/OscMidiConverter.h"
|
andrewm@0
|
36 #include "../TouchKeys/MidiOutputController.h"
|
andrewm@0
|
37 #include "../TouchKeys/MidiKeyboardSegment.h"
|
andrewm@0
|
38 #include "MappingScheduler.h"
|
andrewm@0
|
39
|
andrewm@0
|
40 #undef DEBUG_TOUCHKEY_BASE_MAPPING_FACTORY
|
andrewm@0
|
41
|
andrewm@0
|
42 // Base class for mapping factories that meet the following criteria:
|
andrewm@0
|
43 // * MIDI and TouchKeys data (no continuous angle)
|
andrewm@0
|
44 // * Mappings begin when either or touch or MIDI starts and end when both finish
|
andrewm@0
|
45 // * Each mapping object affects a single note
|
andrewm@0
|
46
|
andrewm@0
|
47 template <class MappingType>
|
andrewm@0
|
48 class TouchkeyBaseMappingFactory : public MappingFactory {
|
andrewm@0
|
49
|
andrewm@0
|
50 public:
|
andrewm@0
|
51 // ***** Constructor *****
|
andrewm@0
|
52
|
andrewm@0
|
53 // Default constructor, containing a reference to the PianoKeyboard class.
|
andrewm@0
|
54 TouchkeyBaseMappingFactory(PianoKeyboard &keyboard, MidiKeyboardSegment& segment) :
|
andrewm@0
|
55 MappingFactory(keyboard), keyboardSegment_(segment), midiConverter_(0),
|
andrewm@49
|
56 controlName_(""), shortControlName_(""),
|
andrewm@0
|
57 inputRangeMin_(0.0), inputRangeMax_(1.0), inputRangeCenter_(0.0),
|
andrewm@0
|
58 outOfRangeBehavior_(OscMidiConverter::kOutOfRangeClip),
|
andrewm@41
|
59 use14BitControl_(false),
|
andrewm@0
|
60 midiControllerNumber_(-1), bypassed_(false), activeNotes_(0x0FFF) {}
|
andrewm@0
|
61
|
andrewm@0
|
62 // ***** Destructor *****
|
andrewm@0
|
63
|
andrewm@0
|
64 virtual ~TouchkeyBaseMappingFactory() {
|
andrewm@0
|
65 removeAllMappings();
|
andrewm@0
|
66 if(midiConverter_ != 0 && controlName_ != "")
|
andrewm@0
|
67 midiConverter_->removeControl(controlName_.c_str());
|
andrewm@0
|
68 if(midiControllerNumber_ >= 0) {
|
andrewm@0
|
69 keyboardSegment_.releaseOscMidiConverter(midiControllerNumber_);
|
andrewm@0
|
70 }
|
andrewm@0
|
71 }
|
andrewm@0
|
72
|
andrewm@0
|
73 // ***** Accessors / Modifiers *****
|
andrewm@0
|
74
|
andrewm@0
|
75 // Return the keyboard segment associated with this factory
|
andrewm@0
|
76 MidiKeyboardSegment& segment() { return keyboardSegment_; }
|
andrewm@0
|
77
|
andrewm@0
|
78 // Look up a mapping with the given note number
|
andrewm@0
|
79 virtual MappingType* mapping(int noteNumber) {
|
andrewm@0
|
80 ScopedLock sl(mappingsMutex_);
|
andrewm@0
|
81 if(mappings_.count(noteNumber) == 0)
|
andrewm@0
|
82 return 0;
|
andrewm@0
|
83 return mappings_[noteNumber];
|
andrewm@0
|
84 }
|
andrewm@0
|
85
|
andrewm@0
|
86 // Return a list of all active notes
|
andrewm@0
|
87 virtual std::vector<int> activeMappings() {
|
andrewm@0
|
88 ScopedLock sl(mappingsMutex_);
|
andrewm@0
|
89 std::vector<int> keys;
|
andrewm@0
|
90 typename std::map<int, MappingType*>::iterator it = mappings_.begin();
|
andrewm@0
|
91 while(it != mappings_.end()) {
|
andrewm@0
|
92 int nextKey = (it++)->first;
|
andrewm@0
|
93 keys.push_back(nextKey);
|
andrewm@0
|
94 }
|
andrewm@0
|
95 return keys;
|
andrewm@0
|
96 }
|
andrewm@0
|
97
|
andrewm@0
|
98 // Remove all active mappings
|
andrewm@0
|
99 virtual void removeAllMappings() {
|
andrewm@0
|
100 ScopedLock sl(mappingsMutex_);
|
andrewm@0
|
101 typename std::map<int, MappingType*>::iterator it = mappings_.begin();
|
andrewm@0
|
102
|
andrewm@0
|
103 while(it != mappings_.end()) {
|
andrewm@0
|
104 // Delete everybody in the container
|
andrewm@0
|
105 MappingType *mapping = it->second;
|
andrewm@0
|
106 #ifdef NEW_MAPPING_SCHEDULER
|
andrewm@0
|
107 mapping->disengage(true);
|
andrewm@0
|
108 //keyboard_.mappingScheduler().unscheduleAndDelete(mapping);
|
andrewm@0
|
109 #else
|
andrewm@0
|
110 mapping->disengage();
|
andrewm@0
|
111 delete mapping;
|
andrewm@0
|
112 #endif
|
andrewm@0
|
113 it++;
|
andrewm@0
|
114 }
|
andrewm@0
|
115
|
andrewm@0
|
116 // Now clear the container
|
andrewm@0
|
117 mappings_.clear();
|
andrewm@0
|
118 }
|
andrewm@0
|
119
|
andrewm@0
|
120 // Callback from mapping to say it's finished
|
andrewm@0
|
121 virtual void mappingFinished(int noteNumber) {
|
andrewm@0
|
122 ScopedLock sl(mappingsMutex_);
|
andrewm@0
|
123 removeMapping(noteNumber);
|
andrewm@0
|
124 }
|
andrewm@0
|
125
|
andrewm@0
|
126 // Suspend messages from a particular note
|
andrewm@0
|
127 virtual void suspendMapping(int noteNumber) {
|
andrewm@0
|
128 ScopedLock sl(mappingsMutex_);
|
andrewm@0
|
129 if(mappings_.count(noteNumber) == 0)
|
andrewm@0
|
130 return;
|
andrewm@0
|
131 mappings_[noteNumber]->suspend();
|
andrewm@0
|
132 }
|
andrewm@0
|
133
|
andrewm@0
|
134 // Suspend messages from all notes
|
andrewm@0
|
135 virtual void suspendAllMappings() {
|
andrewm@0
|
136 ScopedLock sl(mappingsMutex_);
|
andrewm@0
|
137 typename std::map<int, MappingType*>::iterator it = mappings_.begin();
|
andrewm@0
|
138
|
andrewm@0
|
139 while(it != mappings_.end()) {
|
andrewm@0
|
140 //std::cout << "suspending mapping on note " << it->first << std::endl;
|
andrewm@0
|
141 it->second->suspend();
|
andrewm@0
|
142 it++;
|
andrewm@0
|
143 }
|
andrewm@0
|
144 }
|
andrewm@0
|
145
|
andrewm@0
|
146 // Resume messages from a particular note
|
andrewm@0
|
147 virtual void resumeMapping(int noteNumber, bool resend) {
|
andrewm@0
|
148 ScopedLock sl(mappingsMutex_);
|
andrewm@0
|
149 if(mappings_.count(noteNumber) == 0)
|
andrewm@0
|
150 return;
|
andrewm@0
|
151 //std::cout << "resuming mapping on note " << noteNumber << std::endl;
|
andrewm@0
|
152 mappings_[noteNumber]->resume(resend);
|
andrewm@0
|
153 }
|
andrewm@0
|
154
|
andrewm@0
|
155 // Resume messages on all notes
|
andrewm@0
|
156 virtual void resumeAllMappings(bool resend) {
|
andrewm@0
|
157 ScopedLock sl(mappingsMutex_);
|
andrewm@0
|
158 typename std::map<int, MappingType*>::iterator it = mappings_.begin();
|
andrewm@0
|
159
|
andrewm@0
|
160 while(it != mappings_.end()) {
|
andrewm@0
|
161 it->second->resume(resend);
|
andrewm@0
|
162 it++;
|
andrewm@0
|
163 }
|
andrewm@0
|
164 }
|
andrewm@0
|
165
|
andrewm@0
|
166 // Whether this mapping is bypassed
|
andrewm@0
|
167 virtual int bypassed() {
|
andrewm@0
|
168 return bypassed_ ? kBypassOn : kBypassOff;
|
andrewm@0
|
169 }
|
andrewm@0
|
170
|
andrewm@0
|
171 // Set whether the mapping is bypassed or not
|
andrewm@0
|
172 virtual void setBypassed(bool bypass) {
|
andrewm@0
|
173 bypassed_ = bypass;
|
andrewm@0
|
174 }
|
andrewm@0
|
175
|
andrewm@0
|
176 // ***** Class-Specific Methods *****
|
andrewm@0
|
177
|
andrewm@0
|
178 virtual void setMidiParameters(int controller, float inputMinValue, float inputMaxValue, float inputCenterValue,
|
andrewm@0
|
179 int outputDefaultValue = -1, int outputMinValue = -1, int outputMaxValue = -1,
|
andrewm@0
|
180 int outputCenterValue = -1, bool use14BitControl = false,
|
andrewm@0
|
181 int outOfRangeBehavior = OscMidiConverter::kOutOfRangeClip) {
|
andrewm@0
|
182 if(controller < 0)
|
andrewm@0
|
183 return;
|
andrewm@0
|
184
|
andrewm@0
|
185 inputRangeMin_ = inputMinValue;
|
andrewm@0
|
186 inputRangeMax_ = inputMaxValue;
|
andrewm@0
|
187 inputRangeCenter_ = inputCenterValue;
|
andrewm@0
|
188 outOfRangeBehavior_ = outOfRangeBehavior;
|
andrewm@41
|
189 use14BitControl_ = use14BitControl;
|
andrewm@0
|
190
|
andrewm@0
|
191 // Remove listener on previous name (if any)
|
andrewm@0
|
192 //midiConverter_.removeAllControls();
|
andrewm@0
|
193 if(midiControllerNumber_ >= 0 && controller != midiControllerNumber_) {
|
andrewm@0
|
194 keyboardSegment_.releaseOscMidiConverter(midiControllerNumber_);
|
andrewm@0
|
195 midiConverter_ = keyboardSegment_.acquireOscMidiConverter(controller);
|
andrewm@0
|
196 }
|
andrewm@0
|
197 else if(midiControllerNumber_ < 0 || midiConverter_ == 0) {
|
andrewm@0
|
198 midiConverter_ = keyboardSegment_.acquireOscMidiConverter(controller);
|
andrewm@0
|
199 }
|
andrewm@0
|
200 midiControllerNumber_ = controller;
|
andrewm@0
|
201
|
andrewm@0
|
202 midiConverter_->setMidiMessageType(outputDefaultValue, outputMinValue, outputMaxValue, outputCenterValue, use14BitControl);
|
andrewm@0
|
203
|
andrewm@0
|
204 // Add listener for new name
|
andrewm@0
|
205 if(controlName_ != "")
|
andrewm@0
|
206 midiConverter_->addControl(controlName_.c_str(), 1, inputRangeMin_, inputRangeMax_, inputRangeCenter_, outOfRangeBehavior_);
|
andrewm@0
|
207 }
|
andrewm@0
|
208
|
andrewm@0
|
209 virtual string const getName() { return controlName_; }
|
andrewm@49
|
210 virtual string const getShortName() { return shortControlName_; }
|
andrewm@0
|
211
|
andrewm@0
|
212 virtual void setName(const string& name) {
|
andrewm@0
|
213 if(name == "")
|
andrewm@0
|
214 return;
|
andrewm@49
|
215 shortControlName_ = name;
|
andrewm@49
|
216
|
andrewm@0
|
217 std::stringstream ss;
|
andrewm@0
|
218
|
andrewm@0
|
219 // Remove listener on previous name (if any)
|
andrewm@0
|
220 if(midiConverter_ != 0 && controlName_ != "")
|
andrewm@0
|
221 midiConverter_->removeControl(controlName_.c_str());
|
andrewm@0
|
222
|
andrewm@0
|
223 ss << "/touchkeys/mapping/segment" << (int)keyboardSegment_.outputPort() << "/" << name;
|
andrewm@0
|
224 controlName_ = ss.str();
|
andrewm@0
|
225
|
andrewm@0
|
226 // Add listener for new name
|
andrewm@0
|
227 if(midiConverter_ != 0)
|
andrewm@0
|
228 midiConverter_->addControl(controlName_.c_str(), 1, inputRangeMin_, inputRangeMax_, inputRangeCenter_, outOfRangeBehavior_);
|
andrewm@0
|
229 }
|
andrewm@0
|
230
|
andrewm@0
|
231 // Set which keys should have this mapping enable
|
andrewm@0
|
232 virtual void setActiveNotes(unsigned int notes) {
|
andrewm@0
|
233 activeNotes_ = notes;
|
andrewm@0
|
234 }
|
andrewm@0
|
235
|
andrewm@33
|
236 // ****** Preset Save/Load ******
|
andrewm@33
|
237
|
andrewm@33
|
238 // These generate XML settings files and reload settings from them
|
andrewm@33
|
239
|
andrewm@33
|
240 virtual XmlElement* getPreset() {
|
andrewm@34
|
241 PropertySet properties;
|
andrewm@34
|
242 storeCommonProperties(properties);
|
andrewm@34
|
243
|
andrewm@34
|
244 XmlElement* presetElement = properties.createXml("MappingFactory");
|
andrewm@33
|
245 presetElement->setAttribute("type", "Unknown");
|
andrewm@33
|
246 return presetElement;
|
andrewm@33
|
247 }
|
andrewm@33
|
248
|
andrewm@34
|
249 virtual bool loadPreset(XmlElement const* preset) {
|
andrewm@34
|
250 if(preset == 0)
|
andrewm@34
|
251 return false;
|
andrewm@34
|
252
|
andrewm@34
|
253 PropertySet properties;
|
andrewm@34
|
254 properties.restoreFromXml(*preset);
|
andrewm@34
|
255
|
andrewm@34
|
256 if(!loadCommonProperties(properties))
|
andrewm@34
|
257 return false;
|
andrewm@34
|
258 return true;
|
andrewm@34
|
259 }
|
andrewm@33
|
260
|
andrewm@0
|
261 // ***** State Updaters *****
|
andrewm@0
|
262
|
andrewm@0
|
263 // These are called by PianoKey whenever certain events occur that might
|
andrewm@0
|
264 // merit the start and stop of a mapping. What is done with them depends on
|
andrewm@0
|
265 // the particular factory subclass.
|
andrewm@0
|
266
|
andrewm@0
|
267 // Touch becomes active on a key where it wasn't previously
|
andrewm@0
|
268 virtual void touchBegan(int noteNumber, bool midiNoteIsOn, bool keyMotionActive,
|
andrewm@0
|
269 Node<KeyTouchFrame>* touchBuffer,
|
andrewm@0
|
270 Node<key_position>* positionBuffer,
|
andrewm@0
|
271 KeyPositionTracker* positionTracker) {
|
andrewm@0
|
272 ScopedLock sl(mappingsMutex_);
|
andrewm@0
|
273 // Add a new mapping if one doesn't exist already
|
andrewm@0
|
274 if(mappings_.count(noteNumber) == 0) {
|
andrewm@0
|
275 #ifdef DEBUG_TOUCHKEY_BASE_MAPPING_FACTORY
|
andrewm@0
|
276 std::cout << "Note " << noteNumber << ": adding mapping (touch)\n";
|
andrewm@0
|
277 #endif
|
andrewm@0
|
278 int moduloNoteNumber = noteNumber % 12;
|
andrewm@0
|
279 if((activeNotes_ & (1 << moduloNoteNumber)) && !bypassed_)
|
andrewm@0
|
280 addMapping(noteNumber, touchBuffer, positionBuffer, positionTracker);
|
andrewm@0
|
281 }
|
andrewm@0
|
282 }
|
andrewm@0
|
283
|
andrewm@0
|
284 // Touch ends on a key where it wasn't previously
|
andrewm@0
|
285 virtual void touchEnded(int noteNumber, bool midiNoteIsOn, bool keyMotionActive,
|
andrewm@0
|
286 Node<KeyTouchFrame>* touchBuffer,
|
andrewm@0
|
287 Node<key_position>* positionBuffer,
|
andrewm@0
|
288 KeyPositionTracker* positionTracker) {
|
andrewm@0
|
289 ScopedLock sl(mappingsMutex_);
|
andrewm@0
|
290 // If a mapping exists but the MIDI note is off, remove the mapping
|
andrewm@0
|
291 if(mappings_.count(noteNumber) != 0 && !midiNoteIsOn) {
|
andrewm@0
|
292 #ifdef DEBUG_TOUCHKEY_BASE_MAPPING_FACTORY
|
andrewm@0
|
293 std::cout << "Note " << noteNumber << ": removing mapping (touch)\n";
|
andrewm@0
|
294 #endif
|
andrewm@0
|
295 if(mappings_[noteNumber]->requestFinish())
|
andrewm@0
|
296 removeMapping(noteNumber);
|
andrewm@0
|
297 }
|
andrewm@0
|
298 }
|
andrewm@0
|
299
|
andrewm@0
|
300 // MIDI note on for a key
|
andrewm@0
|
301 virtual void midiNoteOn(int noteNumber, bool touchIsOn, bool keyMotionActive,
|
andrewm@0
|
302 Node<KeyTouchFrame>* touchBuffer,
|
andrewm@0
|
303 Node<key_position>* positionBuffer,
|
andrewm@0
|
304 KeyPositionTracker* positionTracker) {
|
andrewm@0
|
305 ScopedLock sl(mappingsMutex_);
|
andrewm@0
|
306 // Add a new mapping if one doesn't exist already
|
andrewm@0
|
307 if(mappings_.count(noteNumber) == 0) {
|
andrewm@0
|
308 #ifdef DEBUG_TOUCHKEY_BASE_MAPPING_FACTORY
|
andrewm@0
|
309 std::cout << "Note " << noteNumber << ": adding mapping (MIDI)\n";
|
andrewm@0
|
310 #endif
|
andrewm@0
|
311 int moduloNoteNumber = noteNumber % 12;
|
andrewm@0
|
312 if((activeNotes_ & (1 << moduloNoteNumber)) && !bypassed_)
|
andrewm@0
|
313 addMapping(noteNumber, touchBuffer, positionBuffer, positionTracker);
|
andrewm@0
|
314 }
|
andrewm@0
|
315 }
|
andrewm@0
|
316
|
andrewm@0
|
317 // MIDI note off for a key
|
andrewm@0
|
318 virtual void midiNoteOff(int noteNumber, bool touchIsOn, bool keyMotionActive,
|
andrewm@0
|
319 Node<KeyTouchFrame>* touchBuffer,
|
andrewm@0
|
320 Node<key_position>* positionBuffer,
|
andrewm@0
|
321 KeyPositionTracker* positionTracker) {
|
andrewm@0
|
322 ScopedLock sl(mappingsMutex_);
|
andrewm@0
|
323 // If a mapping exists but the touch is off, remove the mapping
|
andrewm@0
|
324 if(mappings_.count(noteNumber) != 0 && !touchIsOn) {
|
andrewm@0
|
325 #ifdef DEBUG_TOUCHKEY_BASE_MAPPING_FACTORY
|
andrewm@0
|
326 std::cout << "Note " << noteNumber << ": removing mapping (MIDI)\n";
|
andrewm@0
|
327 #endif
|
andrewm@0
|
328 if(mappings_[noteNumber]->requestFinish())
|
andrewm@0
|
329 removeMapping(noteNumber);
|
andrewm@0
|
330 }
|
andrewm@0
|
331 }
|
andrewm@0
|
332
|
andrewm@0
|
333 // Subclasses of this one won't care about these two methods:
|
andrewm@0
|
334
|
andrewm@0
|
335 // Key goes active from continuous key position
|
andrewm@0
|
336 virtual void keyMotionActive(int noteNumber, bool midiNoteIsOn, bool touchIsOn,
|
andrewm@0
|
337 Node<KeyTouchFrame>* touchBuffer,
|
andrewm@0
|
338 Node<key_position>* positionBuffer,
|
andrewm@0
|
339 KeyPositionTracker* positionTracker) {}
|
andrewm@0
|
340 // Key goes idle from continuous key position
|
andrewm@0
|
341 virtual void keyMotionIdle(int noteNumber, bool midiNoteIsOn, bool touchIsOn,
|
andrewm@0
|
342 Node<KeyTouchFrame>* touchBuffer,
|
andrewm@0
|
343 Node<key_position>* positionBuffer,
|
andrewm@0
|
344 KeyPositionTracker* positionTracker) {}
|
andrewm@0
|
345
|
andrewm@0
|
346 // But we do use this one to send out default values:
|
andrewm@0
|
347 virtual void noteWillBegin(int noteNumber, int midiChannel, int midiVelocity) {
|
andrewm@0
|
348 if(midiConverter_ == 0)
|
andrewm@0
|
349 return;
|
andrewm@0
|
350 midiConverter_->clearLastValues(midiChannel, true);
|
andrewm@0
|
351 //midiConverter_->sendDefaultValue(midiChannel);
|
andrewm@0
|
352 }
|
andrewm@49
|
353
|
andrewm@49
|
354 // ****** OSC Control ******
|
andrewm@49
|
355 // As an alternative to GUI control, the mapping factories can receive OSC messages
|
andrewm@49
|
356 // from the keyboard segment to which they are attached.
|
andrewm@49
|
357 virtual OscMessage* oscControlMethod(const char *path, const char *types,
|
andrewm@49
|
358 int numValues, lo_arg **values, void *data) {
|
andrewm@49
|
359 if(!strcmp(path, "/set-bypass")) {
|
andrewm@49
|
360 // Enable/disable suspend mapping
|
andrewm@49
|
361 if(numValues > 0) {
|
andrewm@49
|
362 if(types[0] == 'i') {
|
andrewm@49
|
363 if(values[0]->i != 0)
|
andrewm@49
|
364 setBypassed(true);
|
andrewm@49
|
365 else
|
andrewm@49
|
366 setBypassed(false);
|
andrewm@49
|
367 return OscTransmitter::createSuccessMessage();
|
andrewm@49
|
368 }
|
andrewm@49
|
369 }
|
andrewm@49
|
370 }
|
andrewm@49
|
371 else if(!strcmp(path, "/set-active-notes")) {
|
andrewm@49
|
372 // Set which notes it applies to
|
andrewm@49
|
373 // Bitmask: lower 12 bits of the number for pitch classes 0-11
|
andrewm@49
|
374 if(numValues > 0) {
|
andrewm@49
|
375 if(types[0] == 'i') {
|
andrewm@49
|
376 setActiveNotes((values[0]->i) & 0x0FFF);
|
andrewm@49
|
377
|
andrewm@49
|
378 return OscTransmitter::createSuccessMessage();
|
andrewm@49
|
379 }
|
andrewm@49
|
380 }
|
andrewm@49
|
381 }
|
andrewm@49
|
382
|
andrewm@49
|
383 return 0;
|
andrewm@49
|
384 }
|
andrewm@0
|
385
|
andrewm@0
|
386
|
andrewm@0
|
387 protected:
|
andrewm@0
|
388 // ***** Protected Methods *****
|
andrewm@0
|
389
|
andrewm@0
|
390 // This method should be set by the subclass to initialize the parameters of
|
andrewm@0
|
391 // a new mapping.
|
andrewm@0
|
392 virtual void initializeMappingParameters(int noteNumber, MappingType *mapping) {}
|
andrewm@0
|
393
|
andrewm@34
|
394 // This method adds the common mapping properties to the given PropertySet
|
andrewm@34
|
395 void storeCommonProperties(PropertySet& properties) {
|
andrewm@34
|
396 properties.setValue("controlName", String(controlName_));
|
andrewm@34
|
397 properties.setValue("inputRangeMin", inputRangeMin_);
|
andrewm@34
|
398 properties.setValue("inputRangeMax", inputRangeMax_);
|
andrewm@34
|
399 properties.setValue("inputRangeCenter", inputRangeCenter_);
|
andrewm@34
|
400 properties.setValue("outOfRangeBehavior", outOfRangeBehavior_);
|
andrewm@34
|
401 properties.setValue("midiControllerNumber", midiControllerNumber_);
|
andrewm@34
|
402 properties.setValue("bypassed", bypassed_);
|
andrewm@34
|
403 properties.setValue("activeNotes", (int)activeNotes_);
|
andrewm@34
|
404 }
|
andrewm@34
|
405
|
andrewm@34
|
406 // This method loads the common mapping properties from the given PropertySet
|
andrewm@34
|
407 bool loadCommonProperties(PropertySet const& properties) {
|
andrewm@34
|
408 if(!properties.containsKey("controlName") ||
|
andrewm@34
|
409 !properties.containsKey("inputRangeMin") ||
|
andrewm@34
|
410 !properties.containsKey("inputRangeMax") ||
|
andrewm@34
|
411 !properties.containsKey("inputRangeCenter") ||
|
andrewm@34
|
412 !properties.containsKey("outOfRangeBehavior") ||
|
andrewm@34
|
413 !properties.containsKey("midiControllerNumber") ||
|
andrewm@34
|
414 !properties.containsKey("bypassed") ||
|
andrewm@34
|
415 !properties.containsKey("activeNotes")) {
|
andrewm@34
|
416 return false;
|
andrewm@34
|
417 }
|
andrewm@34
|
418
|
andrewm@34
|
419 // Setting the MIDI controller number needs to be done with
|
andrewm@34
|
420 // the setMidiParameters() method which will update midiControllerNumber_
|
andrewm@34
|
421 int tempMidiController = 1;
|
andrewm@34
|
422
|
andrewm@34
|
423 controlName_ = properties.getValue("controlName").toUTF8();
|
andrewm@34
|
424 inputRangeMin_ = properties.getDoubleValue("inputRangeMin");
|
andrewm@34
|
425 inputRangeMax_ = properties.getDoubleValue("inputRangeMax");
|
andrewm@34
|
426 inputRangeCenter_ = properties.getDoubleValue("inputRangeCenter");
|
andrewm@34
|
427 outOfRangeBehavior_ = properties.getIntValue("outOfRangeBehavior");
|
andrewm@34
|
428 tempMidiController = properties.getIntValue("midiControllerNumber");
|
andrewm@34
|
429 bypassed_ = properties.getBoolValue("bypassed");
|
andrewm@34
|
430 activeNotes_ = properties.getIntValue("activeNotes");
|
andrewm@34
|
431
|
andrewm@34
|
432 setMidiParameters(tempMidiController, inputRangeMin_, inputRangeMax_, inputRangeCenter_);
|
andrewm@34
|
433
|
andrewm@34
|
434 return true;
|
andrewm@34
|
435 }
|
andrewm@34
|
436
|
andrewm@0
|
437 private:
|
andrewm@0
|
438 // ***** Private Methods *****
|
andrewm@0
|
439
|
andrewm@0
|
440 // Add a new mapping
|
andrewm@0
|
441 void addMapping(int noteNumber,
|
andrewm@0
|
442 Node<KeyTouchFrame>* touchBuffer,
|
andrewm@0
|
443 Node<key_position>* positionBuffer,
|
andrewm@0
|
444 KeyPositionTracker* positionTracker) {
|
andrewm@0
|
445 // TODO: mutex
|
andrewm@0
|
446 removeMapping(noteNumber); // Free any mapping that's already present on this note
|
andrewm@0
|
447
|
andrewm@0
|
448 MappingType *mapping = new MappingType(keyboard_, this, noteNumber, touchBuffer,
|
andrewm@0
|
449 positionBuffer, positionTracker);
|
andrewm@0
|
450
|
andrewm@0
|
451 // Set parameters
|
andrewm@0
|
452 mapping->setName(controlName_);
|
andrewm@0
|
453 initializeMappingParameters(noteNumber, mapping);
|
andrewm@0
|
454
|
andrewm@0
|
455 // Save the mapping
|
andrewm@0
|
456 mappings_[noteNumber] = mapping;
|
andrewm@0
|
457
|
andrewm@0
|
458 // Finally, engage the new mapping
|
andrewm@0
|
459 mapping->engage();
|
andrewm@0
|
460 }
|
andrewm@0
|
461
|
andrewm@0
|
462 void removeMapping(int noteNumber) {
|
andrewm@0
|
463 // TODO: mutex
|
andrewm@0
|
464 if(mappings_.count(noteNumber) == 0)
|
andrewm@0
|
465 return;
|
andrewm@0
|
466 MappingType* mapping = mappings_[noteNumber];
|
andrewm@0
|
467 #ifdef NEW_MAPPING_SCHEDULER
|
andrewm@0
|
468 mapping->disengage(true);
|
andrewm@0
|
469 //keyboard_.mappingScheduler().unscheduleAndDelete(mapping);
|
andrewm@0
|
470 #else
|
andrewm@0
|
471 mapping->disengage();
|
andrewm@0
|
472 delete mapping;
|
andrewm@0
|
473 #endif
|
andrewm@0
|
474 mappings_.erase(noteNumber);
|
andrewm@0
|
475 }
|
andrewm@0
|
476
|
andrewm@0
|
477 protected:
|
andrewm@0
|
478 // State variables
|
andrewm@0
|
479 MidiKeyboardSegment& keyboardSegment_; // Segment of the keyboard that this mapping addresses
|
andrewm@0
|
480 OscMidiConverter *midiConverter_; // Object to convert OSC messages to MIDI
|
andrewm@0
|
481 std::map<int, MappingType*> mappings_; // Collection of active mappings
|
andrewm@0
|
482 CriticalSection mappingsMutex_; // Mutex protecting mappings from changes
|
andrewm@0
|
483
|
andrewm@49
|
484 std::string controlName_; // Name of the mapping in long..
|
andrewm@49
|
485 std::string shortControlName_; // ... and short forms
|
andrewm@0
|
486 float inputRangeMin_, inputRangeMax_; // Input ranges
|
andrewm@0
|
487 float inputRangeCenter_;
|
andrewm@0
|
488 int outOfRangeBehavior_; // What happens to out of range inputs
|
andrewm@41
|
489 bool use14BitControl_; // Whether to use a 14-bit control
|
andrewm@0
|
490
|
andrewm@0
|
491 int midiControllerNumber_; // Which controller to use
|
andrewm@0
|
492 bool bypassed_; // Whether the mapping has been bypassed by UI
|
andrewm@0
|
493 unsigned int activeNotes_; // Indication of which notes out of the 12 to use
|
andrewm@0
|
494 };
|
andrewm@0
|
495
|
andrewm@0
|
496 #endif /* defined(__TouchKeys__TouchkeyBaseMappingFactory__) */ |