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 TouchkeyKeyDivisionMappingFactory.cpp: factory for the split-key mapping
|
andrewm@0
|
21 which triggers different actions or pitches depending on where the key
|
andrewm@0
|
22 was struck.
|
andrewm@0
|
23 */
|
andrewm@0
|
24
|
andrewm@0
|
25 #include "TouchkeyKeyDivisionMappingFactory.h"
|
andrewm@51
|
26 #include "TouchkeyKeyDivisionMappingShortEditor.h"
|
andrewm@44
|
27 #include "../../Display/KeyboardDisplay.h"
|
andrewm@0
|
28
|
andrewm@51
|
29 /* Yarman-24c microtonal tuning */
|
andrewm@51
|
30 const float TouchkeyKeyDivisionMappingFactory::kTuningsYarman24c[24] = {
|
andrewm@0
|
31 0, (1124.744 - 1200.0), 83.059, 143.623, 203.9, 191.771, 292.413, 348.343,
|
andrewm@0
|
32 383.54, 362.503, 498.04, 415.305, 581.382, 634.184, 695.885, 648.682,
|
andrewm@0
|
33 788.736, 853.063, 905.87, 887.656, 996.1, 1043.623, 1085.49, 1071.942,
|
andrewm@0
|
34 };
|
andrewm@0
|
35
|
andrewm@51
|
36 const int TouchkeyKeyDivisionMappingFactory::kMaxSegmentsPerKey = 3;
|
andrewm@0
|
37
|
andrewm@0
|
38 /* As arranged:
|
andrewm@0
|
39 *
|
andrewm@0
|
40 * B| Db/ Dd Eb/ Ed E| Gb/ Gd Ab/ Ad Bb/ Bd
|
andrewm@0
|
41 * C C# D D# E F F# G G# A A# B
|
andrewm@0
|
42 */
|
andrewm@0
|
43
|
andrewm@0
|
44 TouchkeyKeyDivisionMappingFactory::TouchkeyKeyDivisionMappingFactory(PianoKeyboard &keyboard, MidiKeyboardSegment& segment)
|
andrewm@0
|
45 : TouchkeyBaseMappingFactory<TouchkeyKeyDivisionMapping>(keyboard, segment),
|
andrewm@51
|
46 tuningPreset_(-1), tunings_(0),
|
andrewm@0
|
47 numSegmentsPerKey_(TouchkeyKeyDivisionMapping::kDefaultNumberOfSegments),
|
andrewm@0
|
48 timeout_(TouchkeyKeyDivisionMapping::kDefaultDetectionTimeout),
|
andrewm@0
|
49 detectionParameter_(TouchkeyKeyDivisionMapping::kDefaultDetectionParameter),
|
andrewm@0
|
50 retriggerable_(false),
|
andrewm@0
|
51 retriggerNumFrames_(TouchkeyKeyDivisionMapping::kDefaultRetriggerNumFrames),
|
andrewm@0
|
52 retriggerKeepsVelocity_(true),
|
andrewm@0
|
53 referenceNote_(0), globalOffsetCents_(0)
|
andrewm@0
|
54 {
|
andrewm@0
|
55 //setName("/touchkeys/segmentpitch");
|
andrewm@0
|
56 setBendParameters();
|
andrewm@51
|
57 setTuningPreset(kTuningPreset24TET);
|
andrewm@44
|
58
|
andrewm@44
|
59 KeyboardDisplay *display = keyboard_.gui();
|
andrewm@44
|
60 if(display != 0) {
|
andrewm@44
|
61 display->addKeyDivision(this, segment.noteRange().first, segment.noteRange().second, numSegmentsPerKey_);
|
andrewm@44
|
62 }
|
andrewm@44
|
63 }
|
andrewm@44
|
64
|
andrewm@44
|
65 TouchkeyKeyDivisionMappingFactory::~TouchkeyKeyDivisionMappingFactory() {
|
andrewm@44
|
66 // Remove the divisions from the keys, if this mapping has added them
|
andrewm@44
|
67 KeyboardDisplay *display = keyboard_.gui();
|
andrewm@44
|
68 if(display != 0)
|
andrewm@44
|
69 display->removeKeyDivision(this);
|
andrewm@51
|
70 if(tunings_ != 0) {
|
andrewm@51
|
71 delete tunings_;
|
andrewm@51
|
72 tunings_ = 0;
|
andrewm@51
|
73 }
|
andrewm@0
|
74 }
|
andrewm@0
|
75
|
andrewm@0
|
76 void TouchkeyKeyDivisionMappingFactory::setName(const string& name) {
|
andrewm@0
|
77 TouchkeyBaseMappingFactory<TouchkeyKeyDivisionMapping>::setName(name);
|
andrewm@0
|
78 setBendParameters();
|
andrewm@0
|
79 }
|
andrewm@0
|
80
|
andrewm@51
|
81 void TouchkeyKeyDivisionMappingFactory::setTuningPreset(int preset) {
|
andrewm@51
|
82 if(preset < 0 || preset >= kTuningPresetMaxValue)
|
andrewm@51
|
83 return;
|
andrewm@51
|
84
|
andrewm@51
|
85 ScopedLock sl(tuningMutex_);
|
andrewm@51
|
86
|
andrewm@51
|
87 tuningPreset_ = preset;
|
andrewm@51
|
88 if(tunings_ != 0) {
|
andrewm@51
|
89 delete tunings_;
|
andrewm@51
|
90 tunings_ = 0;
|
andrewm@51
|
91 }
|
andrewm@51
|
92
|
andrewm@51
|
93 if(tuningPreset_ == kTuningPreset19TET) {
|
andrewm@51
|
94 numSegmentsPerKey_ = 2;
|
andrewm@51
|
95 tunings_ = new float[24];
|
andrewm@51
|
96 for(int i = 0; i < 24; i++) {
|
andrewm@51
|
97 // Start with fraction of an octave, round to the nearest 19th of an octave
|
andrewm@51
|
98 float original = (float)i / 24.0;
|
andrewm@51
|
99 float rounded = floorf(original * 19.0 + 0.5);
|
andrewm@51
|
100
|
andrewm@51
|
101 // Now convert the 19-tone index back to a fractional number of semitones
|
andrewm@51
|
102 tunings_[i] = rounded * 1200.0 / 19.0;
|
andrewm@51
|
103 }
|
andrewm@51
|
104 }
|
andrewm@51
|
105 else if(tuningPreset_ == kTuningPreset24TET) {
|
andrewm@51
|
106 numSegmentsPerKey_ = 2;
|
andrewm@51
|
107 tunings_ = new float[24];
|
andrewm@51
|
108 for(int i = 0; i < 24; i++) {
|
andrewm@51
|
109 tunings_[i] = (float)i * 50.0;
|
andrewm@51
|
110 }
|
andrewm@51
|
111 }
|
andrewm@51
|
112 else if(tuningPreset_ == kTuningPreset31TET) {
|
andrewm@51
|
113 numSegmentsPerKey_ = 3;
|
andrewm@51
|
114 tunings_ = new float[36];
|
andrewm@51
|
115 for(int i = 0; i < 36; i++) {
|
andrewm@51
|
116 // Start with fraction of an octave, round to the nearest 31st of an octave
|
andrewm@51
|
117 float original = (float)i / 36.0;
|
andrewm@51
|
118 float rounded = floorf(original * 31.0 + 0.5);
|
andrewm@51
|
119
|
andrewm@51
|
120 // Now convert the 31-tone index back to a fractional number of semitones
|
andrewm@51
|
121 tunings_[i] = rounded * 1200.0 / 31.0;
|
andrewm@51
|
122 }
|
andrewm@51
|
123 }
|
andrewm@51
|
124 else if(tuningPreset_ == kTuningPreset36TET) {
|
andrewm@51
|
125 numSegmentsPerKey_ = 3;
|
andrewm@51
|
126 tunings_ = new float[36];
|
andrewm@51
|
127 for(int i = 0; i < 24; i++) {
|
andrewm@51
|
128 tunings_[i] = (float)i * 100.0 / 3.0;
|
andrewm@51
|
129 }
|
andrewm@51
|
130 }
|
andrewm@51
|
131 else if(tuningPreset_ == kTuningPresetYarman24c) {
|
andrewm@51
|
132 numSegmentsPerKey_ = 2;
|
andrewm@51
|
133 tunings_ = new float[24];
|
andrewm@51
|
134 for(int i = 0; i < 24; i++)
|
andrewm@51
|
135 tunings_[i] = kTuningsYarman24c[i];
|
andrewm@51
|
136 }
|
andrewm@51
|
137
|
andrewm@51
|
138 KeyboardDisplay *display = keyboard_.gui();
|
andrewm@51
|
139 if(display != 0) {
|
andrewm@51
|
140 display->removeKeyDivision(this);
|
andrewm@51
|
141 display->addKeyDivision(this, keyboardSegment_.noteRange().first, keyboardSegment_.noteRange().second, numSegmentsPerKey_);
|
andrewm@51
|
142 }
|
andrewm@51
|
143 }
|
andrewm@51
|
144
|
andrewm@51
|
145 #ifndef TOUCHKEYS_NO_GUI
|
andrewm@51
|
146 // ***** GUI Support *****
|
andrewm@51
|
147 MappingEditorComponent* TouchkeyKeyDivisionMappingFactory::createBasicEditor() {
|
andrewm@51
|
148 return new TouchkeyKeyDivisionMappingShortEditor(*this);
|
andrewm@51
|
149 }
|
andrewm@51
|
150 #endif
|
andrewm@36
|
151
|
andrewm@36
|
152 // ****** Preset Save/Load ******
|
andrewm@36
|
153 XmlElement* TouchkeyKeyDivisionMappingFactory::getPreset() {
|
andrewm@36
|
154 PropertySet properties;
|
andrewm@36
|
155
|
andrewm@36
|
156 storeCommonProperties(properties);
|
andrewm@36
|
157
|
andrewm@36
|
158 // No properties for now
|
andrewm@36
|
159
|
andrewm@36
|
160 XmlElement* preset = properties.createXml("MappingFactory");
|
andrewm@36
|
161 preset->setAttribute("type", "KeyDivision");
|
andrewm@36
|
162
|
andrewm@36
|
163 return preset;
|
andrewm@36
|
164 }
|
andrewm@36
|
165
|
andrewm@36
|
166 bool TouchkeyKeyDivisionMappingFactory::loadPreset(XmlElement const* preset) {
|
andrewm@36
|
167 if(preset == 0)
|
andrewm@36
|
168 return false;
|
andrewm@36
|
169
|
andrewm@36
|
170 PropertySet properties;
|
andrewm@36
|
171 properties.restoreFromXml(*preset);
|
andrewm@36
|
172
|
andrewm@36
|
173 if(!loadCommonProperties(properties))
|
andrewm@36
|
174 return false;
|
andrewm@36
|
175
|
andrewm@36
|
176 // Nothing specific to do for now
|
andrewm@36
|
177
|
andrewm@36
|
178 return true;
|
andrewm@36
|
179 }
|
andrewm@36
|
180
|
andrewm@36
|
181 // ***** Private Methods *****
|
andrewm@36
|
182
|
andrewm@0
|
183 // Set the initial parameters for a new mapping
|
andrewm@0
|
184 void TouchkeyKeyDivisionMappingFactory::initializeMappingParameters(int noteNumber, TouchkeyKeyDivisionMapping *mapping) {
|
andrewm@51
|
185 ScopedLock sl(tuningMutex_);
|
andrewm@51
|
186
|
andrewm@51
|
187 // Convert absolute tunings into pitch bends in semitones
|
andrewm@51
|
188 float tunings[kMaxSegmentsPerKey];
|
andrewm@51
|
189
|
andrewm@0
|
190 int index = (noteNumber + 12 - referenceNote_) % 12;
|
andrewm@0
|
191 float standardTuning = (float)index * 100.0;
|
andrewm@0
|
192
|
andrewm@51
|
193 for(int i = 0; i < numSegmentsPerKey_; i++) {
|
andrewm@51
|
194 tunings[i] = (tunings_[index*numSegmentsPerKey_ + i] - standardTuning + globalOffsetCents_) * .01;
|
andrewm@51
|
195 }
|
andrewm@51
|
196 mapping->setSegmentPitchBends(tunings, numSegmentsPerKey_);
|
andrewm@0
|
197 mapping->setNumberOfSegments(numSegmentsPerKey_);
|
andrewm@0
|
198 mapping->setTimeout(timeout_);
|
andrewm@0
|
199 mapping->setDetectionParameter(detectionParameter_);
|
andrewm@0
|
200 mapping->setRetriggerable(retriggerable_, retriggerNumFrames_, retriggerKeepsVelocity_);
|
andrewm@0
|
201 }
|
andrewm@0
|
202
|
andrewm@0
|
203 void TouchkeyKeyDivisionMappingFactory::setBendParameters() {
|
andrewm@7
|
204 // Range of 0 indicates special case of using global pitch wheel range
|
andrewm@7
|
205 setMidiParameters(MidiKeyboardSegment::kControlPitchWheel, 0.0, 0.0, 0.0);
|
andrewm@0
|
206
|
andrewm@0
|
207 if(midiConverter_ != 0) {
|
andrewm@0
|
208 midiConverter_->listenToIncomingControl(MidiKeyboardSegment::kControlPitchWheel);
|
andrewm@0
|
209 }
|
andrewm@0
|
210 } |