view Source/Mappings/PitchBend/TouchkeyPitchBendMappingFactory.cpp @ 56:b4a2d2ae43cf tip

merge
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Fri, 23 Nov 2018 15:48:14 +0000
parents 90ce403d0dc5
children
line wrap: on
line source
/*
  TouchKeys: multi-touch musical keyboard control software
  Copyright (c) 2013 Andrew McPherson

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
  =====================================================================

  TouchkeyPitchBendMappingFactory.cpp: factory for the pitch-bend mapping,
  which handles changing pitch based on relative finger motion.
*/

#include "TouchkeyPitchBendMappingFactory.h"
#include "TouchkeyPitchBendMappingShortEditor.h"

// Default constructor, containing a reference to the PianoKeyboard class.

TouchkeyPitchBendMappingFactory::TouchkeyPitchBendMappingFactory(PianoKeyboard &keyboard, MidiKeyboardSegment& segment) :
TouchkeyBaseMappingFactory<TouchkeyPitchBendMapping>(keyboard, segment),
bendRangeSemitones_(TouchkeyPitchBendMapping::kDefaultBendRangeSemitones),
bendThresholdSemitones_(TouchkeyPitchBendMapping::kDefaultBendThresholdSemitones),
bendThresholdKeyLength_(TouchkeyPitchBendMapping::kDefaultBendThresholdKeyLength),
bendMode_(TouchkeyPitchBendMapping::kDefaultPitchBendMode),
fixedModeMinEnableDistance_(TouchkeyPitchBendMapping::kDefaultFixedModeEnableDistance),
fixedModeBufferDistance_(TouchkeyPitchBendMapping::kDefaultFixedModeBufferDistance),
bendIgnoresTwoFingers_(TouchkeyPitchBendMapping::kDefaultIgnoresTwoFingers),
bendIgnoresThreeFingers_(TouchkeyPitchBendMapping::kDefaultIgnoresThreeFingers)
{
    // Set up the MIDI converter to use pitch wheel
    // setName("/touchkeys/pitchbend");
    setBendParameters();
}

// ***** Destructor *****

TouchkeyPitchBendMappingFactory::~TouchkeyPitchBendMappingFactory() {
    
}

// ***** Accessors / Modifiers *****

void TouchkeyPitchBendMappingFactory::setName(const string& name) {
    TouchkeyBaseMappingFactory<TouchkeyPitchBendMapping>::setName(name);
    setBendParameters();
}


// ***** Bend Methods *****

void TouchkeyPitchBendMappingFactory::setBendRange(float rangeSemitones, bool updateCurrent) {
    /*if(updateCurrent) {
     // Send new range to all active mappings
     // TODO: mutex protect
     std::map<int, mapping_pair>::iterator it = mappings_.begin();
     while(it != mappings_.end()) {
     // Tell this mapping to update its range
     TouchkeyPitchBendMapping *mapping = it->second.second;
     mapping->setRange(rangeSemitones);
     it++;
     }
     }*/
    
    bendRangeSemitones_ = rangeSemitones;
}

void TouchkeyPitchBendMappingFactory::setBendThresholdSemitones(float thresholdSemitones) {
    bendThresholdSemitones_ = thresholdSemitones;    
}

void TouchkeyPitchBendMappingFactory::setBendThresholdKeyLength(float thresholdKeyLength) {
    bendThresholdKeyLength_ = thresholdKeyLength;    
}

void TouchkeyPitchBendMappingFactory::setBendThresholds(float thresholdSemitones, float thresholdKeyLength, bool updateCurrent) {
    /*if(updateCurrent) {
     // Send new range to all active mappings
     // TODO: mutex protect
     std::map<int, mapping_pair>::iterator it = mappings_.begin();
     while(it != mappings_.end()) {
     // Tell this mapping to update its range
     TouchkeyPitchBendMapping *mapping = it->second.second;
     mapping->setThresholds(thresholdSemitones, thresholdKeyLength);
     it++;
     }
     }*/
    
    bendThresholdSemitones_ = thresholdSemitones;
    bendThresholdKeyLength_ = thresholdKeyLength;
}

// Set the mode to bend a fixed amount up and down the key, regardless of where
// the touch starts. minimumDistanceToEnable sets a floor below which the bend isn't
// possible (for starting very close to an edge) and bufferAtEnd sets the amount
// of key length beyond which no further bend takes place.
void TouchkeyPitchBendMappingFactory::setBendFixedEndpoints(float minimumDistanceToEnable, float bufferAtEnd) {
    bendMode_ = TouchkeyPitchBendMapping::kPitchBendModeFixedEndpoints;
    fixedModeMinEnableDistance_ = minimumDistanceToEnable;
    fixedModeBufferDistance_ = bufferAtEnd;
}

// Set the mode to bend an amount proportional to distance, which means
// that the total range of bend will depend on where the finger started.
void TouchkeyPitchBendMappingFactory::setBendVariableEndpoints() {
    bendMode_ = TouchkeyPitchBendMapping::kPitchBendModeVariableEndpoints;
}

void TouchkeyPitchBendMappingFactory::setBendIgnoresMultipleFingers(bool ignoresTwo, bool ignoresThree) {
    // TODO: update current
    bendIgnoresTwoFingers_ = ignoresTwo;
    bendIgnoresThreeFingers_ = ignoresThree;
}

#ifndef TOUCHKEYS_NO_GUI
// ***** GUI Support *****
MappingEditorComponent* TouchkeyPitchBendMappingFactory::createBasicEditor() {
    return new TouchkeyPitchBendMappingShortEditor(*this);
}
#endif

// ****** OSC Control Support ******
OscMessage* TouchkeyPitchBendMappingFactory::oscControlMethod(const char *path, const char *types,
                                                            int numValues, lo_arg **values, void *data) {
    if(!strcmp(path, "/set-bend-range")) {
        // Change the range of the pitch bend in semitones
        if(numValues > 0) {
            if(types[0] == 'f') {
                setBendRange(values[0]->f);
                return OscTransmitter::createSuccessMessage();
            }
        }
    }
    else if(!strcmp(path, "/set-bend-threshold")) {
        // Change the threshold to activate the pitch bend [in semitones]
        if(numValues > 0) {
            if(types[0] == 'f') {
                setBendThresholdSemitones(values[0]->f);
                return OscTransmitter::createSuccessMessage();
            }
        }
    }
    else if(!strcmp(path, "/set-bend-fixed-endpoints")) {
        // Enable fixed endpoints on the pitch bend
        if(numValues > 0) {
            if(types[0] == 'f') {
                float fixedEndpointBufferAtEnd = 0;
                if(numValues >= 2) {
                    if(types[1] == 'f') {
                        fixedEndpointBufferAtEnd = values[1]->f;
                    }
                }
                setBendFixedEndpoints(values[0]->f, fixedEndpointBufferAtEnd);
                
                return OscTransmitter::createSuccessMessage();
            }
        }
    }
    else if(!strcmp(path, "/set-bend-variable-endpoints")) {
        // Enable variable endpoints on the pitch bend
        setBendVariableEndpoints();
        return OscTransmitter::createSuccessMessage();
    }
    else if(!strcmp(path, "/set-bend-ignores-multiple-fingers")) {
        // Change whether the bend ignores two or three fingers
        if(numValues >= 2) {
            if(types[0] == 'i' && types[1] == 'i') {
                setBendIgnoresMultipleFingers(values[0]->i, values[1]->i);
                return OscTransmitter::createSuccessMessage();
            }
        }
    }
    
    // If no match, check the base class
    return TouchkeyBaseMappingFactory<TouchkeyPitchBendMapping>::oscControlMethod(path, types, numValues, values, data);
}

// ****** Preset Save/Load ******
XmlElement* TouchkeyPitchBendMappingFactory::getPreset() {
    PropertySet properties;
    
    storeCommonProperties(properties);
    
    properties.setValue("bendRangeSemitones", bendRangeSemitones_);
    properties.setValue("bendThresholdSemitones", bendThresholdSemitones_);
    properties.setValue("bendThresholdKeyLength", bendThresholdKeyLength_);
    properties.setValue("bendMode", bendMode_);
    properties.setValue("fixedModeMinEnableDistance", fixedModeMinEnableDistance_);
    properties.setValue("fixedModeBufferDistance", fixedModeBufferDistance_);
    properties.setValue("bendIgnoresTwoFingers", bendIgnoresTwoFingers_);
    properties.setValue("bendIgnoresThreeFingers", bendIgnoresThreeFingers_);
    
    XmlElement* preset = properties.createXml("MappingFactory");
    preset->setAttribute("type", "PitchBend");
    
    return preset;
}

bool TouchkeyPitchBendMappingFactory::loadPreset(XmlElement const* preset) {
    if(preset == 0)
        return false;
    
    PropertySet properties;
    properties.restoreFromXml(*preset);
    
    if(!loadCommonProperties(properties))
        return false;
    if(!properties.containsKey("bendRangeSemitones") ||
       !properties.containsKey("bendThresholdSemitones") ||
       !properties.containsKey("bendThresholdKeyLength") ||
       !properties.containsKey("bendMode") ||
       !properties.containsKey("fixedModeMinEnableDistance") ||
       !properties.containsKey("fixedModeBufferDistance") ||
       !properties.containsKey("bendIgnoresTwoFingers") ||
       !properties.containsKey("bendIgnoresThreeFingers"))
        return false;
    
    bendRangeSemitones_ = properties.getDoubleValue("bendRangeSemitones");
    bendThresholdSemitones_ = properties.getDoubleValue("bendThresholdSemitones");
    bendThresholdKeyLength_ = properties.getDoubleValue("bendThresholdKeyLength");
    bendMode_ = properties.getIntValue("bendMode");
    fixedModeMinEnableDistance_ = properties.getDoubleValue("fixedModeMinEnableDistance");
    fixedModeBufferDistance_ = properties.getDoubleValue("fixedModeBufferDistance");
    bendIgnoresTwoFingers_ = properties.getBoolValue("bendIgnoresTwoFingers");
    bendIgnoresThreeFingers_ = properties.getBoolValue("bendIgnoresThreeFingers");
    
    // Update MIDI information
    setBendParameters();
    
    return true;
}

// ***** Private Methods *****

// Set the initial parameters for a new mapping
void TouchkeyPitchBendMappingFactory::initializeMappingParameters(int noteNumber, TouchkeyPitchBendMapping *mapping) {
    mapping->setRange(bendRangeSemitones_);
    mapping->setThresholds(bendThresholdSemitones_, bendThresholdKeyLength_);
    if(bendMode_ == TouchkeyPitchBendMapping::kPitchBendModeFixedEndpoints) {
        mapping->setFixedEndpoints(fixedModeMinEnableDistance_, fixedModeBufferDistance_);
    }
    else
        mapping->setVariableEndpoints();
    mapping->setIgnoresMultipleFingers(bendIgnoresTwoFingers_, bendIgnoresThreeFingers_);
}

void TouchkeyPitchBendMappingFactory::setBendParameters() {
    // Range of 0 indicates special case of using global pitch wheel range
    setMidiParameters(MidiKeyboardSegment::kControlPitchWheel, 0.0, 0.0, 0.0);
    
    if(midiConverter_ != 0) {
        midiConverter_->listenToIncomingControl(MidiKeyboardSegment::kControlPitchWheel);
    }
}