view Source/Mappings/PitchBend/TouchkeyPitchBendMapping.h @ 56:b4a2d2ae43cf tip

merge
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Fri, 23 Nov 2018 15:48:14 +0000
parents 3580ffe87dc8
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/>.
 
  =====================================================================

  TouchkeyPitchBendMapping.h: per-note mapping for the pitch-bend mapping,
  which handles changing pitch based on relative finger motion.
*/

#ifndef __touchkeys__TouchkeyPitchBendMapping__
#define __touchkeys__TouchkeyPitchBendMapping__



#include <map>
#include <boost/bind.hpp>
#include "../TouchkeyBaseMapping.h"
#include "../../Utility/IIRFilter.h"

// This class handles the detection and mapping of vibrato gestures
// based on Touchkey data. It outputs MIDI or OSC messages that
// can be used to affect the pitch of the active note.

class TouchkeyPitchBendMapping : public TouchkeyBaseMapping {
    friend class TouchkeyVibratoMappingFactory;
    friend class TouchkeyPitchBendMappingFactory;
    

public:
    enum {
        kPitchBendModeVariableEndpoints = 1,
        kPitchBendModeFixedEndpoints
    };
    
private:
    // Useful constants for mapping MRP messages
    
    static const int kDefaultMIDIChannel;
    static const int kDefaultFilterBufferLength;
    
    static const float kDefaultBendRangeSemitones;
    static const float kDefaultBendThresholdSemitones;
    static const float kDefaultBendThresholdKeyLength;
    static const float kDefaultSnapZoneSemitones;
    static const int kDefaultPitchBendMode;
    static const float kDefaultFixedModeEnableDistance;
    static const float kDefaultFixedModeBufferDistance;
    
    static const bool kDefaultIgnoresTwoFingers;
    static const bool kDefaultIgnoresThreeFingers;


public:
	// ***** Constructors *****
	
	// Default constructor, passing the buffer on which to trigger
	TouchkeyPitchBendMapping(PianoKeyboard &keyboard, MappingFactory *factory, int noteNumber, Node<KeyTouchFrame>* touchBuffer,
                           Node<key_position>* positionBuffer, KeyPositionTracker* positionTracker);
	
	// Copy constructor
	//TouchkeyPitchBendMapping(TouchkeyPitchBendMapping const& obj);
    
    // ***** Destructor *****
    
    ~TouchkeyPitchBendMapping();
	
    // ***** Modifiers *****
    
    // Reset the state back initial values
	void reset();
    
    // Resend the current state of all parameters
    void resend();
    
    // Parameters for pitch bend algorithm
    void setRange(float rangeSemitones);
    void setThresholds(float thresholdSemitones, float thresholdKeyLength);
    void setFixedEndpoints(float minimumDistanceToEnable, float bufferAtEnd);
    void setVariableEndpoints();
    
    // Whether we ignore two or three finger touches for calculating pitch bends
    void setIgnoresMultipleFingers(bool ignoresTwo, bool ignoresThree);
    
	// ***** Evaluators *****
    
    // This method receives triggers whenever events occur in the touch data or the
    // continuous key position (state changes only). It alters the behavior and scheduling
    // of the mapping but does not itself send OSC messages
	void triggerReceived(TriggerSource* who, timestamp_type timestamp);
	
    // This method handles the OSC message transmission. It should be run in the Scheduler
    // thread provided by PianoKeyboard.
    timestamp_type performMapping();
    
private:
    // ***** Private Methods *****
    void midiNoteOnReceived(int channel, int velocity);
    void midiNoteOffReceived(int channel);
    
    void resetDetectionState();
    void clearBuffers();
    
    void engageSnapping();
    void disengageSnapping();
    
    void updateCombinedThreshold();
    void sendPitchBendMessage(float pitchBendSemitones, bool force = false);
    
	// ***** Member Variables *****
    
    bool bendIsEngaged_;                        // Whether the pitch bend is currently engaged
    bool snapIsEngaged_;                        // Whether the note is currently being snapped to nearest semitone
    
    float thresholdSemitones_;                  // Minimum motion in semitones before bend engages
    float thresholdKeyLength_;                  // Minimum motion as a function of key length
    float thresholdCombinedMax_;                // Whichever is greater of the above two
    float snapZoneSemitones_;                   // How wide the snap area is around each semitone
    int bendMode_;                              // What mode the bend works in (fixed, variable, etc.)
    float fixedModeMinEnableDistance_;          // Minimum distance to engage in fixed mode
    float fixedModeBufferDistance_;             // Extra distance at end beyond which no bend happens
    bool ignoresTwoFingers_;                    // Whether this mapping supresses all messages when two
    bool ignoresThreeFingers_;                  // or three fingers are present
    
    float onsetLocationX_, onsetLocationY_;     // Where the touch began at MIDI note on
    float lastX_, lastY_;                       // Where the touch was at the last frame we received
    int idOfCurrentTouch_;                      // Which touch ID we're currently following
    timestamp_type lastTimestamp_;              // When the last data point arrived
    Node<float>::size_type lastProcessedIndex_; // Index of the last filtered position sample we've handled
    
    float bendScalerPositive_, bendScalerNegative_; // Translation between position and semitones for upward and downward motions
    
    float currentSnapDestinationSemitones_;     // Where the pitch would snap to if snapping was engaged
    
    float bendRangeSemitones_;                  // Amount of pitch bend in one direction at maximum
    float lastPitchBendSemitones_;              // The last pitch bend value we sent out
    
    Node<float> rawDistance_;                   // Distance from onset location
    CriticalSection distanceAccessMutex_;       // Mutex protecting access to the distance buffer
};


#endif /* defined(__touchkeys__TouchkeyPitchBendMapping__) */