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: TouchkeyControlMappingFactory.cpp: factory for the TouchKeys control andrewm@0: mapping, which converts an arbitrary touch parameter into a MIDI or andrewm@0: OSC control message. andrewm@0: */ andrewm@0: andrewm@0: #include "TouchkeyControlMappingFactory.h" andrewm@0: #include "TouchkeyControlMappingShortEditor.h" andrewm@41: #include "TouchkeyControlMappingExtendedEditor.h" andrewm@0: andrewm@0: const int TouchkeyControlMappingFactory::kDefaultController = 1; andrewm@0: const float TouchkeyControlMappingFactory::kDefaultOutputRangeMin = 0.0; andrewm@0: const float TouchkeyControlMappingFactory::kDefaultOutputRangeMax = 127.0; andrewm@0: const float TouchkeyControlMappingFactory::kDefaultOutputDefault = 0.0; andrewm@0: andrewm@0: TouchkeyControlMappingFactory::TouchkeyControlMappingFactory(PianoKeyboard &keyboard, MidiKeyboardSegment& segment) : andrewm@0: TouchkeyBaseMappingFactory(keyboard, segment), andrewm@0: inputParameter_(TouchkeyControlMapping::kInputParameterYPosition), andrewm@0: inputType_(TouchkeyControlMapping::kTypeAbsolute), andrewm@0: outputRangeMin_(kDefaultOutputRangeMin), outputRangeMax_(kDefaultOutputRangeMax), andrewm@0: outputDefault_(kDefaultOutputDefault), threshold_(0.0), andrewm@0: ignoresTwoFingers_(TouchkeyControlMapping::kDefaultIgnoresTwoFingers), andrewm@0: ignoresThreeFingers_(TouchkeyControlMapping::kDefaultIgnoresThreeFingers), andrewm@0: direction_(TouchkeyControlMapping::kDefaultDirection) andrewm@0: { andrewm@0: setController(kDefaultController); andrewm@0: } andrewm@0: andrewm@0: // ***** Destructor ***** andrewm@0: andrewm@0: TouchkeyControlMappingFactory::~TouchkeyControlMappingFactory() { andrewm@0: andrewm@0: } andrewm@0: andrewm@0: // ***** Accessors / Modifiers ***** andrewm@0: andrewm@41: int TouchkeyControlMappingFactory::getDirection() { andrewm@41: // Get the direction of motion. This is always positive for andrewm@41: if(inputType_ == TouchkeyControlMapping::kTypeAbsolute) andrewm@41: return TouchkeyControlMapping::kDirectionPositive; andrewm@41: return direction_; andrewm@41: } andrewm@41: andrewm@0: void TouchkeyControlMappingFactory::setInputParameter(int inputParameter) { andrewm@49: if(inputParameter >= 1 && inputParameter < TouchkeyControlMapping::kInputParameterMaxValue) andrewm@49: inputParameter_ = inputParameter; andrewm@0: } andrewm@0: andrewm@0: void TouchkeyControlMappingFactory::setInputType(int inputType) { andrewm@49: if(inputType >= 1 && inputType < TouchkeyControlMapping::kTypeMaxValue) andrewm@49: inputType_ = inputType; andrewm@0: } andrewm@0: andrewm@0: void TouchkeyControlMappingFactory::setController(int controller) { andrewm@52: if(controller < 1 || andrewm@52: controller >= MidiKeyboardSegment::kControlMax) andrewm@49: return; andrewm@49: andrewm@0: // Before changing the controller, check if we were going to or from the pitch wheel. andrewm@0: // If so, we should scale the value to or from a 14-bit value andrewm@0: if(midiControllerNumber_ == MidiKeyboardSegment::kControlPitchWheel && andrewm@0: controller != MidiKeyboardSegment::kControlPitchWheel) { andrewm@0: outputRangeMax_ = outputRangeMax_ / 128.0; andrewm@0: if(outputRangeMax_ > 127.0) andrewm@0: outputRangeMax_ = 127.0; andrewm@0: outputRangeMin_ = outputRangeMin_ / 128.0; andrewm@0: if(outputRangeMin_ > 127.0) andrewm@0: outputRangeMin_ = 127.0; andrewm@0: outputDefault_ = outputDefault_ / 128.0; andrewm@0: if(outputDefault_ > 127.0) andrewm@0: outputDefault_ = 127.0; andrewm@0: } andrewm@0: else if(midiControllerNumber_ != MidiKeyboardSegment::kControlPitchWheel && andrewm@0: controller == MidiKeyboardSegment::kControlPitchWheel) { andrewm@0: if(outputRangeMax_ == 127.0) andrewm@0: outputRangeMax_ = 16383.0; andrewm@0: else andrewm@0: outputRangeMax_ = outputRangeMax_ * 128.0; andrewm@0: if(outputRangeMin_ == 127.0) andrewm@0: outputRangeMin_ = 16383.0; andrewm@0: else andrewm@0: outputRangeMin_ = outputRangeMin_ * 128.0; andrewm@0: if(outputDefault_ == 127.0) andrewm@0: outputDefault_ = 16383.0; andrewm@0: else andrewm@0: outputDefault_ = outputDefault_ * 128.0; andrewm@0: } andrewm@0: andrewm@0: setMidiParameters(controller, inputRangeMin_, inputRangeMax_, inputRangeCenter_, andrewm@41: outputDefault_, outputRangeMin_, outputRangeMax_, andrewm@41: -1, use14BitControl_, outOfRangeBehavior_); andrewm@0: andrewm@0: // Listen to incoming controls from the keyboard too, if this is enabled andrewm@0: // in MidiKeyboardSegment andrewm@0: if(midiConverter_ != 0) { andrewm@0: midiConverter_->listenToIncomingControl(midiControllerNumber_); andrewm@0: } andrewm@0: } andrewm@0: andrewm@0: void TouchkeyControlMappingFactory::setRangeInputMin(float inputMin) { andrewm@0: if(inputMin < -1.0) andrewm@0: inputRangeMin_ = -1.0; andrewm@0: else if(inputMin > 1.0) andrewm@0: inputRangeMin_ = 1.0; andrewm@0: else andrewm@0: inputRangeMin_ = inputMin; andrewm@0: andrewm@0: // Update control andrewm@0: //if(midiConverter_ == 0) andrewm@0: // return; andrewm@0: //midiConverter_->setControlMinValue(controlName_.c_str(), inputRangeMin_); andrewm@0: setMidiParameters(midiControllerNumber_, inputRangeMin_, inputRangeMax_, inputRangeCenter_, andrewm@41: outputDefault_, outputRangeMin_, outputRangeMax_, andrewm@41: -1, use14BitControl_, outOfRangeBehavior_); andrewm@0: } andrewm@0: andrewm@0: void TouchkeyControlMappingFactory::setRangeInputMax(float inputMax) { andrewm@0: if(inputMax < -1.0) andrewm@0: inputRangeMax_ = -1.0; andrewm@0: else if(inputMax > 1.0) andrewm@0: inputRangeMax_ = 1.0; andrewm@0: else andrewm@0: inputRangeMax_ = inputMax; andrewm@0: andrewm@0: // Update control andrewm@0: //if(midiConverter_ == 0) andrewm@0: // return; andrewm@0: //midiConverter_->setControlMaxValue(controlName_.c_str(), inputRangeMax_); andrewm@0: setMidiParameters(midiControllerNumber_, inputRangeMin_, inputRangeMax_, inputRangeCenter_, andrewm@41: outputDefault_, outputRangeMin_, outputRangeMax_, andrewm@41: -1, use14BitControl_, outOfRangeBehavior_); andrewm@0: } andrewm@0: andrewm@0: void TouchkeyControlMappingFactory::setRangeInputCenter(float inputCenter) { andrewm@0: if(inputCenter < -1.0) andrewm@0: inputRangeCenter_ = -1.0; andrewm@0: else if(inputCenter > 1.0) andrewm@0: inputRangeCenter_ = 1.0; andrewm@0: else andrewm@0: inputRangeCenter_ = inputCenter; andrewm@0: andrewm@0: // Update control andrewm@0: //if(midiConverter_ == 0) andrewm@0: // return; andrewm@0: //midiConverter_->setControlCenterValue(controlName_.c_str(), inputRangeCenter_); andrewm@0: setMidiParameters(midiControllerNumber_, inputRangeMin_, inputRangeMax_, inputRangeCenter_, andrewm@41: outputDefault_, outputRangeMin_, outputRangeMax_, andrewm@41: -1, use14BitControl_, outOfRangeBehavior_); andrewm@0: } andrewm@0: andrewm@0: void TouchkeyControlMappingFactory::setRangeOutputMin(float outputMin) { andrewm@0: outputRangeMin_ = outputMin; andrewm@0: andrewm@0: setMidiParameters(midiControllerNumber_, inputRangeMin_, inputRangeMax_, inputRangeCenter_, andrewm@41: outputDefault_, outputRangeMin_, outputRangeMax_, andrewm@41: -1, use14BitControl_, outOfRangeBehavior_); andrewm@0: } andrewm@0: andrewm@0: void TouchkeyControlMappingFactory::setRangeOutputMax(float outputMax) { andrewm@0: outputRangeMax_ = outputMax; andrewm@0: andrewm@0: setMidiParameters(midiControllerNumber_, inputRangeMin_, inputRangeMax_, inputRangeCenter_, andrewm@41: outputDefault_, outputRangeMin_, outputRangeMax_, andrewm@41: -1, use14BitControl_, outOfRangeBehavior_); andrewm@0: } andrewm@0: andrewm@0: void TouchkeyControlMappingFactory::setRangeOutputDefault(float outputDefault) { andrewm@0: outputDefault_ = outputDefault; andrewm@0: andrewm@0: setMidiParameters(midiControllerNumber_, inputRangeMin_, inputRangeMax_, inputRangeCenter_, andrewm@41: outputDefault_, outputRangeMin_, outputRangeMax_, andrewm@41: -1, use14BitControl_, outOfRangeBehavior_); andrewm@0: } andrewm@0: andrewm@0: void TouchkeyControlMappingFactory::setThreshold(float threshold) { andrewm@0: threshold_ = threshold; andrewm@0: } andrewm@0: andrewm@0: void TouchkeyControlMappingFactory::setIgnoresTwoFingers(bool ignoresTwo) { andrewm@0: ignoresTwoFingers_ = ignoresTwo; andrewm@0: } andrewm@0: andrewm@0: void TouchkeyControlMappingFactory::setIgnoresThreeFingers(bool ignoresThree) { andrewm@0: ignoresThreeFingers_ = ignoresThree; andrewm@0: } andrewm@0: andrewm@0: void TouchkeyControlMappingFactory::setDirection(int direction) { andrewm@0: direction_ = direction; andrewm@0: } andrewm@0: andrewm@41: void TouchkeyControlMappingFactory::setOutOfRangeBehavior(int behavior) { andrewm@41: outOfRangeBehavior_ = behavior; andrewm@41: andrewm@41: setMidiParameters(midiControllerNumber_, inputRangeMin_, inputRangeMax_, inputRangeCenter_, andrewm@41: outputDefault_, outputRangeMin_, outputRangeMax_, andrewm@41: -1, use14BitControl_, outOfRangeBehavior_); andrewm@41: } andrewm@41: andrewm@41: void TouchkeyControlMappingFactory::setUses14BitControl(bool use) { andrewm@41: use14BitControl_ = use; andrewm@41: andrewm@41: setMidiParameters(midiControllerNumber_, inputRangeMin_, inputRangeMax_, inputRangeCenter_, andrewm@41: outputDefault_, outputRangeMin_, outputRangeMax_, andrewm@41: -1, use14BitControl_, outOfRangeBehavior_); andrewm@41: } andrewm@41: andrewm@49: #ifndef TOUCHKEYS_NO_GUI andrewm@0: // ***** GUI Support ***** andrewm@0: MappingEditorComponent* TouchkeyControlMappingFactory::createBasicEditor() { andrewm@0: return new TouchkeyControlMappingShortEditor(*this); andrewm@0: } andrewm@0: andrewm@41: MappingEditorComponent* TouchkeyControlMappingFactory::createExtendedEditor() { andrewm@41: return new TouchkeyControlMappingExtendedEditor(*this); andrewm@41: } andrewm@49: #endif andrewm@49: andrewm@49: // ****** OSC Control Support ****** andrewm@49: OscMessage* TouchkeyControlMappingFactory::oscControlMethod(const char *path, const char *types, andrewm@49: int numValues, lo_arg **values, void *data) { andrewm@49: if(!strcmp(path, "/set-input-parameter")) { andrewm@49: // Change the input parameter for the control mapping andrewm@49: if(numValues > 0) { andrewm@49: if(types[0] == 'i') { andrewm@49: setInputParameter(values[0]->i); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: } andrewm@49: } andrewm@49: else if(!strcmp(path, "/set-input-type")) { andrewm@49: // Change the input type (absolute/relative) andrewm@49: if(numValues > 0) { andrewm@49: if(types[0] == 'i') { andrewm@49: setInputType(values[0]->i); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: } andrewm@49: } andrewm@49: else if(!strcmp(path, "/set-input-range-min")) { andrewm@49: // Change the input range andrewm@49: if(numValues > 0) { andrewm@49: if(types[0] == 'f') { andrewm@49: setRangeInputMin(values[0]->f); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: } andrewm@49: } andrewm@49: else if(!strcmp(path, "/set-input-range-max")) { andrewm@49: // Change the input range andrewm@49: if(numValues > 0) { andrewm@49: if(types[0] == 'f') { andrewm@49: setRangeInputMax(values[0]->f); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: } andrewm@49: } andrewm@49: else if(!strcmp(path, "/set-input-range-center")) { andrewm@49: // Change the input range andrewm@49: if(numValues > 0) { andrewm@49: if(types[0] == 'f') { andrewm@49: setRangeInputCenter(values[0]->f); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: } andrewm@49: } andrewm@49: else if(!strcmp(path, "/set-output-range-min")) { andrewm@49: // Change the output range andrewm@49: if(numValues > 0) { andrewm@49: if(types[0] == 'f') { andrewm@49: setRangeOutputMin(values[0]->f); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: } andrewm@49: } andrewm@49: else if(!strcmp(path, "/set-output-range-max")) { andrewm@49: // Change the output range andrewm@49: if(numValues > 0) { andrewm@49: if(types[0] == 'f') { andrewm@49: setRangeOutputMax(values[0]->f); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: } andrewm@49: } andrewm@49: else if(!strcmp(path, "/set-output-default")) { andrewm@49: // Change the output range andrewm@49: if(numValues > 0) { andrewm@49: if(types[0] == 'f') { andrewm@49: setRangeOutputDefault(values[0]->f); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: } andrewm@49: } andrewm@49: else if(!strcmp(path, "/set-out-of-range-behavior")) { andrewm@49: // Change how out-of-range inputs are handled andrewm@49: if(numValues > 0) { andrewm@49: if(types[0] == 'i') { andrewm@49: setOutOfRangeBehavior(values[0]->i); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: } andrewm@49: } andrewm@49: else if(!strcmp(path, "/set-midi-controller")) { andrewm@49: // Set the MIDI output CC, including pitchwheel etc. and 14-bit options andrewm@49: if(numValues > 0) { andrewm@49: if(types[0] == 'i') { andrewm@49: if(numValues >= 2) andrewm@49: if(types[1] == 'i') andrewm@49: setUses14BitControl(values[1]->i != 0); andrewm@49: andrewm@49: setController(values[0]->i); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: } andrewm@49: } andrewm@49: else if(!strcmp(path, "/set-threshold")) { andrewm@49: // Set the threshold for relative activations andrewm@49: if(numValues > 0) { andrewm@49: if(types[0] == 'f') { andrewm@49: setThreshold(values[0]->f); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: } andrewm@49: } andrewm@49: else if(!strcmp(path, "/set-ignores-multiple-fingers")) { andrewm@49: // Change whether two or three finger touches are ignored andrewm@49: if(numValues >= 2) { andrewm@49: if(types[0] == 'i' && types[1] == 'i') { andrewm@49: setIgnoresTwoFingers(values[0]->i); andrewm@49: setIgnoresThreeFingers(values[1]->i); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: } andrewm@49: } andrewm@49: else if(!strcmp(path, "/set-direction")) { andrewm@49: // Set the direction of the mapping (normal/reverse/absolute val) andrewm@49: if(numValues > 0) { andrewm@49: if(types[0] == 'i') { andrewm@49: setDirection(values[0]->i); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: else if(types[0] == 's') { andrewm@49: const char *str = &values[0]->s; andrewm@49: andrewm@49: if(!strncmp(str, "norm", 4)) { andrewm@49: setDirection(TouchkeyControlMapping::kDirectionPositive); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: else if(!strncmp(str, "rev", 3)) { andrewm@49: setDirection(TouchkeyControlMapping::kDirectionNegative); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: if(!strncmp(str, "always", 6) || !strncmp(str, "both", 4)) { andrewm@49: setDirection(TouchkeyControlMapping::kDirectionBoth); andrewm@49: return OscTransmitter::createSuccessMessage(); andrewm@49: } andrewm@49: else andrewm@49: return OscTransmitter::createFailureMessage(); andrewm@49: } andrewm@49: } andrewm@49: } andrewm@49: andrewm@49: // If no match, check the base class andrewm@49: return TouchkeyBaseMappingFactory::oscControlMethod(path, types, numValues, values, data); andrewm@49: } andrewm@41: andrewm@41: andrewm@33: // ****** Preset Save/Load ****** andrewm@33: XmlElement* TouchkeyControlMappingFactory::getPreset() { andrewm@34: PropertySet properties; andrewm@34: andrewm@34: storeCommonProperties(properties); andrewm@34: properties.setValue("inputParameter", inputParameter_); andrewm@34: properties.setValue("inputType", inputType_); andrewm@34: properties.setValue("outputRangeMin", outputRangeMin_); andrewm@34: properties.setValue("outputRangeMax", outputRangeMax_); andrewm@34: properties.setValue("outputDefault", outputDefault_); andrewm@34: properties.setValue("threshold", threshold_); andrewm@34: properties.setValue("ignoresTwoFingers", ignoresTwoFingers_); andrewm@34: properties.setValue("ignoresThreeFingers", ignoresThreeFingers_); andrewm@34: properties.setValue("direction", direction_); andrewm@43: properties.setValue("use14Bit", use14BitControl_); andrewm@34: andrewm@34: XmlElement* preset = properties.createXml("MappingFactory"); andrewm@33: preset->setAttribute("type", "Control"); andrewm@34: andrewm@33: return preset; andrewm@33: } andrewm@33: andrewm@33: bool TouchkeyControlMappingFactory::loadPreset(XmlElement const* preset) { andrewm@34: if(preset == 0) andrewm@34: return false; andrewm@34: andrewm@34: PropertySet properties; andrewm@34: properties.restoreFromXml(*preset); andrewm@34: andrewm@34: if(!loadCommonProperties(properties)) andrewm@34: return false; andrewm@34: if(!properties.containsKey("inputParameter") || andrewm@34: !properties.containsKey("inputType") || andrewm@34: !properties.containsKey("outputRangeMin") || andrewm@34: !properties.containsKey("outputRangeMax") || andrewm@34: !properties.containsKey("outputDefault") || andrewm@34: !properties.containsKey("threshold") || andrewm@34: !properties.containsKey("ignoresTwoFingers") || andrewm@34: !properties.containsKey("ignoresThreeFingers") || andrewm@34: !properties.containsKey("direction")) andrewm@34: return false; andrewm@34: andrewm@34: inputParameter_ = properties.getIntValue("inputParameter"); andrewm@34: inputType_ = properties.getIntValue("inputType"); andrewm@35: outputRangeMin_ = properties.getDoubleValue("outputRangeMin"); andrewm@35: outputRangeMax_ = properties.getDoubleValue("outputRangeMax"); andrewm@35: outputDefault_ = properties.getDoubleValue("outputDefault"); andrewm@35: threshold_ = properties.getDoubleValue("threshold"); andrewm@35: ignoresTwoFingers_ = properties.getBoolValue("ignoresTwoFingers"); andrewm@35: ignoresThreeFingers_ = properties.getBoolValue("ignoresThreeFingers"); andrewm@34: direction_ = properties.getIntValue("direction"); andrewm@34: andrewm@43: // These values added to later versions of the presets so check andrewm@43: // whether they actually exist or not andrewm@43: if(properties.containsKey("use14Bit")) andrewm@43: use14BitControl_ = properties.getBoolValue("use14Bit"); andrewm@43: andrewm@34: // Update MIDI information; this doesn't actually change the controller andrewm@34: // (which is already set) but it adds a listener and updates the ranges andrewm@34: setController(midiControllerNumber_); andrewm@34: andrewm@33: return true; andrewm@33: } andrewm@33: andrewm@0: // ***** Private Methods ***** andrewm@0: andrewm@0: void TouchkeyControlMappingFactory::initializeMappingParameters(int noteNumber, TouchkeyControlMapping *mapping) { andrewm@0: // Set parameters andrewm@0: mapping->setInputParameter(inputParameter_, inputType_); andrewm@0: mapping->setRange(inputRangeMin_, inputRangeMax_, outputRangeMin_, outputRangeMax_, outputDefault_); andrewm@0: mapping->setThreshold(threshold_); andrewm@0: mapping->setIgnoresMultipleFingers(ignoresTwoFingers_, ignoresThreeFingers_); andrewm@0: mapping->setDirection(direction_); andrewm@0: }