andrewm@0: /* andrewm@0: TouchKeys: multi-touch musical keyboard control software andrewm@0: Copyright (c) 2013 Andrew McPherson andrewm@0: andrewm@0: This program is free software: you can redistribute it and/or modify andrewm@0: it under the terms of the GNU General Public License as published by andrewm@0: the Free Software Foundation, either version 3 of the License, or andrewm@0: (at your option) any later version. andrewm@0: andrewm@0: This program is distributed in the hope that it will be useful, andrewm@0: but WITHOUT ANY WARRANTY; without even the implied warranty of andrewm@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrewm@0: GNU General Public License for more details. andrewm@0: andrewm@0: You should have received a copy of the GNU General Public License andrewm@0: along with this program. If not, see . andrewm@0: andrewm@0: ===================================================================== andrewm@0: andrewm@0: TouchkeyKeyDivisionMappingFactory.cpp: factory for the split-key mapping andrewm@0: which triggers different actions or pitches depending on where the key andrewm@0: was struck. andrewm@0: */ andrewm@0: andrewm@0: #include "TouchkeyKeyDivisionMappingFactory.h" andrewm@51: #include "TouchkeyKeyDivisionMappingShortEditor.h" andrewm@44: #include "../../Display/KeyboardDisplay.h" andrewm@0: andrewm@51: /* Yarman-24c microtonal tuning */ andrewm@51: const float TouchkeyKeyDivisionMappingFactory::kTuningsYarman24c[24] = { andrewm@0: 0, (1124.744 - 1200.0), 83.059, 143.623, 203.9, 191.771, 292.413, 348.343, andrewm@0: 383.54, 362.503, 498.04, 415.305, 581.382, 634.184, 695.885, 648.682, andrewm@0: 788.736, 853.063, 905.87, 887.656, 996.1, 1043.623, 1085.49, 1071.942, andrewm@0: }; andrewm@0: andrewm@51: const int TouchkeyKeyDivisionMappingFactory::kMaxSegmentsPerKey = 3; andrewm@0: andrewm@0: /* As arranged: andrewm@0: * andrewm@0: * B| Db/ Dd Eb/ Ed E| Gb/ Gd Ab/ Ad Bb/ Bd andrewm@0: * C C# D D# E F F# G G# A A# B andrewm@0: */ andrewm@0: andrewm@0: TouchkeyKeyDivisionMappingFactory::TouchkeyKeyDivisionMappingFactory(PianoKeyboard &keyboard, MidiKeyboardSegment& segment) andrewm@0: : TouchkeyBaseMappingFactory(keyboard, segment), andrewm@51: tuningPreset_(-1), tunings_(0), andrewm@0: numSegmentsPerKey_(TouchkeyKeyDivisionMapping::kDefaultNumberOfSegments), andrewm@0: timeout_(TouchkeyKeyDivisionMapping::kDefaultDetectionTimeout), andrewm@0: detectionParameter_(TouchkeyKeyDivisionMapping::kDefaultDetectionParameter), andrewm@0: retriggerable_(false), andrewm@0: retriggerNumFrames_(TouchkeyKeyDivisionMapping::kDefaultRetriggerNumFrames), andrewm@0: retriggerKeepsVelocity_(true), andrewm@0: referenceNote_(0), globalOffsetCents_(0) andrewm@0: { andrewm@0: //setName("/touchkeys/segmentpitch"); andrewm@0: setBendParameters(); andrewm@51: setTuningPreset(kTuningPreset24TET); andrewm@44: andrewm@44: KeyboardDisplay *display = keyboard_.gui(); andrewm@44: if(display != 0) { andrewm@44: display->addKeyDivision(this, segment.noteRange().first, segment.noteRange().second, numSegmentsPerKey_); andrewm@44: } andrewm@44: } andrewm@44: andrewm@44: TouchkeyKeyDivisionMappingFactory::~TouchkeyKeyDivisionMappingFactory() { andrewm@44: // Remove the divisions from the keys, if this mapping has added them andrewm@44: KeyboardDisplay *display = keyboard_.gui(); andrewm@44: if(display != 0) andrewm@44: display->removeKeyDivision(this); andrewm@51: if(tunings_ != 0) { andrewm@51: delete tunings_; andrewm@51: tunings_ = 0; andrewm@51: } andrewm@0: } andrewm@0: andrewm@0: void TouchkeyKeyDivisionMappingFactory::setName(const string& name) { andrewm@0: TouchkeyBaseMappingFactory::setName(name); andrewm@0: setBendParameters(); andrewm@0: } andrewm@0: andrewm@51: void TouchkeyKeyDivisionMappingFactory::setTuningPreset(int preset) { andrewm@51: if(preset < 0 || preset >= kTuningPresetMaxValue) andrewm@51: return; andrewm@51: andrewm@51: ScopedLock sl(tuningMutex_); andrewm@51: andrewm@51: tuningPreset_ = preset; andrewm@51: if(tunings_ != 0) { andrewm@51: delete tunings_; andrewm@51: tunings_ = 0; andrewm@51: } andrewm@51: andrewm@51: if(tuningPreset_ == kTuningPreset19TET) { andrewm@51: numSegmentsPerKey_ = 2; andrewm@51: tunings_ = new float[24]; andrewm@51: for(int i = 0; i < 24; i++) { andrewm@51: // Start with fraction of an octave, round to the nearest 19th of an octave andrewm@51: float original = (float)i / 24.0; andrewm@51: float rounded = floorf(original * 19.0 + 0.5); andrewm@51: andrewm@51: // Now convert the 19-tone index back to a fractional number of semitones andrewm@51: tunings_[i] = rounded * 1200.0 / 19.0; andrewm@51: } andrewm@51: } andrewm@51: else if(tuningPreset_ == kTuningPreset24TET) { andrewm@51: numSegmentsPerKey_ = 2; andrewm@51: tunings_ = new float[24]; andrewm@51: for(int i = 0; i < 24; i++) { andrewm@51: tunings_[i] = (float)i * 50.0; andrewm@51: } andrewm@51: } andrewm@51: else if(tuningPreset_ == kTuningPreset31TET) { andrewm@51: numSegmentsPerKey_ = 3; andrewm@51: tunings_ = new float[36]; andrewm@51: for(int i = 0; i < 36; i++) { andrewm@51: // Start with fraction of an octave, round to the nearest 31st of an octave andrewm@51: float original = (float)i / 36.0; andrewm@51: float rounded = floorf(original * 31.0 + 0.5); andrewm@51: andrewm@51: // Now convert the 31-tone index back to a fractional number of semitones andrewm@51: tunings_[i] = rounded * 1200.0 / 31.0; andrewm@51: } andrewm@51: } andrewm@51: else if(tuningPreset_ == kTuningPreset36TET) { andrewm@51: numSegmentsPerKey_ = 3; andrewm@51: tunings_ = new float[36]; andrewm@51: for(int i = 0; i < 24; i++) { andrewm@51: tunings_[i] = (float)i * 100.0 / 3.0; andrewm@51: } andrewm@51: } andrewm@51: else if(tuningPreset_ == kTuningPresetYarman24c) { andrewm@51: numSegmentsPerKey_ = 2; andrewm@51: tunings_ = new float[24]; andrewm@51: for(int i = 0; i < 24; i++) andrewm@51: tunings_[i] = kTuningsYarman24c[i]; andrewm@51: } andrewm@51: andrewm@51: KeyboardDisplay *display = keyboard_.gui(); andrewm@51: if(display != 0) { andrewm@51: display->removeKeyDivision(this); andrewm@51: display->addKeyDivision(this, keyboardSegment_.noteRange().first, keyboardSegment_.noteRange().second, numSegmentsPerKey_); andrewm@51: } andrewm@51: } andrewm@51: andrewm@51: #ifndef TOUCHKEYS_NO_GUI andrewm@51: // ***** GUI Support ***** andrewm@51: MappingEditorComponent* TouchkeyKeyDivisionMappingFactory::createBasicEditor() { andrewm@51: return new TouchkeyKeyDivisionMappingShortEditor(*this); andrewm@51: } andrewm@51: #endif andrewm@36: andrewm@36: // ****** Preset Save/Load ****** andrewm@36: XmlElement* TouchkeyKeyDivisionMappingFactory::getPreset() { andrewm@36: PropertySet properties; andrewm@36: andrewm@36: storeCommonProperties(properties); andrewm@36: andrewm@36: // No properties for now andrewm@36: andrewm@36: XmlElement* preset = properties.createXml("MappingFactory"); andrewm@36: preset->setAttribute("type", "KeyDivision"); andrewm@36: andrewm@36: return preset; andrewm@36: } andrewm@36: andrewm@36: bool TouchkeyKeyDivisionMappingFactory::loadPreset(XmlElement const* preset) { andrewm@36: if(preset == 0) andrewm@36: return false; andrewm@36: andrewm@36: PropertySet properties; andrewm@36: properties.restoreFromXml(*preset); andrewm@36: andrewm@36: if(!loadCommonProperties(properties)) andrewm@36: return false; andrewm@36: andrewm@36: // Nothing specific to do for now andrewm@36: andrewm@36: return true; andrewm@36: } andrewm@36: andrewm@36: // ***** Private Methods ***** andrewm@36: andrewm@0: // Set the initial parameters for a new mapping andrewm@0: void TouchkeyKeyDivisionMappingFactory::initializeMappingParameters(int noteNumber, TouchkeyKeyDivisionMapping *mapping) { andrewm@51: ScopedLock sl(tuningMutex_); andrewm@51: andrewm@51: // Convert absolute tunings into pitch bends in semitones andrewm@51: float tunings[kMaxSegmentsPerKey]; andrewm@51: andrewm@0: int index = (noteNumber + 12 - referenceNote_) % 12; andrewm@0: float standardTuning = (float)index * 100.0; andrewm@0: andrewm@51: for(int i = 0; i < numSegmentsPerKey_; i++) { andrewm@51: tunings[i] = (tunings_[index*numSegmentsPerKey_ + i] - standardTuning + globalOffsetCents_) * .01; andrewm@51: } andrewm@51: mapping->setSegmentPitchBends(tunings, numSegmentsPerKey_); andrewm@0: mapping->setNumberOfSegments(numSegmentsPerKey_); andrewm@0: mapping->setTimeout(timeout_); andrewm@0: mapping->setDetectionParameter(detectionParameter_); andrewm@0: mapping->setRetriggerable(retriggerable_, retriggerNumFrames_, retriggerKeepsVelocity_); andrewm@0: } andrewm@0: andrewm@0: void TouchkeyKeyDivisionMappingFactory::setBendParameters() { andrewm@7: // Range of 0 indicates special case of using global pitch wheel range andrewm@7: setMidiParameters(MidiKeyboardSegment::kControlPitchWheel, 0.0, 0.0, 0.0); andrewm@0: andrewm@0: if(midiConverter_ != 0) { andrewm@0: midiConverter_->listenToIncomingControl(MidiKeyboardSegment::kControlPitchWheel); andrewm@0: } andrewm@0: }