view Source/Mappings/Vibrato/TouchkeyVibratoMapping.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/>.
 
  =====================================================================

  TouchkeyVibratoMapping.h: per-note mapping for the vibrato mapping class,
  which creates vibrato through side-to-side motion of the finger on the
  key surface.
*/

#ifndef __touchkeys__TouchkeyVibratoMapping__
#define __touchkeys__TouchkeyVibratoMapping__


#include <map>
#include <boost/bind.hpp>
#include "../../TouchKeys/KeyTouchFrame.h"
#include "../../TouchKeys/KeyPositionTracker.h"
#include "../../TouchKeys/PianoKeyboard.h"
#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 TouchkeyVibratoMapping : public TouchkeyBaseMapping {
    friend class TouchkeyVibratoMappingFactory;

private:
    // Useful constants for mapping MRP messages
    /*constexpr static const int kDefaultMIDIChannel = 0;
    constexpr static const int kDefaultFilterBufferLength = 30;
    
    constexpr static const float kDefaultVibratoThresholdX = 0.05;
    constexpr static const float kDefaultVibratoRatioX = 0.3;
    constexpr static const float kDefaultVibratoThresholdY = 0.02;
    constexpr static const float kDefaultVibratoRatioY = 0.8;
    constexpr static const timestamp_diff_type kDefaultVibratoTimeout = microseconds_to_timestamp(400000); // 0.4s
    constexpr static const float kDefaultVibratoPrescaler = 2.0;
    constexpr static const float kDefaultVibratoRangeSemitones = 1.25;
    
    constexpr static const timestamp_diff_type kZeroCrossingMinimumTime = microseconds_to_timestamp(50000); // 50ms
    constexpr static const timestamp_diff_type kMinimumOnsetTime = microseconds_to_timestamp(30000); // 30ms
    constexpr static const timestamp_diff_type kMaximumOnsetTime = microseconds_to_timestamp(300000); // 300ms
    constexpr static const timestamp_diff_type kMinimumReleaseTime = microseconds_to_timestamp(30000); // 30ms
    constexpr static const timestamp_diff_type kMaximumReleaseTime = microseconds_to_timestamp(300000); // 300ms*/
    
    static const int kDefaultMIDIChannel;
    static const int kDefaultFilterBufferLength;
    
    static const float kDefaultVibratoThresholdX;
    static const float kDefaultVibratoRatioX;
    static const float kDefaultVibratoThresholdY;
    static const float kDefaultVibratoRatioY;
    static const timestamp_diff_type kDefaultVibratoTimeout;
    static const float kDefaultVibratoPrescaler;
    static const float kDefaultVibratoRangeSemitones;
    
    static const timestamp_diff_type kZeroCrossingMinimumTime;
    static const timestamp_diff_type kMinimumOnsetTime;
    static const timestamp_diff_type kMaximumOnsetTime;
    static const timestamp_diff_type kMinimumReleaseTime;
    static const timestamp_diff_type kMaximumReleaseTime;
    
    static const float kWhiteKeySingleAxisThreshold;
    
    enum {
        kStateInactive = 0,
        kStateSwitchingOn,
        kStateActive,
        kStateSwitchingOff
    };
    
public:
	// ***** Constructors *****
	
	// Default constructor, passing the buffer on which to trigger
	TouchkeyVibratoMapping(PianoKeyboard &keyboard, MappingFactory *factory, int noteNumber, Node<KeyTouchFrame>* touchBuffer,
               Node<key_position>* positionBuffer, KeyPositionTracker* positionTracker);

    // ***** Destructor *****
    
    ~TouchkeyVibratoMapping();
	
    // ***** Modifiers *****
    
    // Disable mappings from being sent
    void disengage(bool shouldDelete = false);
	
    // Reset the state back initial values
	void reset();
    
    // Resend the current state of all parameters
    void resend();

    // Parameters for vibrato algorithm
    //void setType(int vibratoType);
    void setRange(float rangeSemitones);
    void setPrescaler(float prescaler);
    void setThresholds(float thresholdX, float thresholdY, float ratioX, float ratioY);
    void setTimeout(timestamp_diff_type timeout);
    
	// ***** 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 changeStateSwitchingOn(timestamp_type timestamp);
    void changeStateActive(timestamp_type timestamp);
    void changeStateSwitchingOff(timestamp_type timestamp);
    void changeStateInactive(timestamp_type timestamp);

    void resetDetectionState();
    void clearBuffers();
    
    bool keyIsWhite();
    
    void sendVibratoMessage(float pitchBendSemitones, bool force = false);
    
	// ***** Member Variables *****
    
    int vibratoState_;                          // Whether a vibrato gesture is currently detected
    
    timestamp_type rampBeginTime_;              // If in a switching state, when does the transition begin?
    float rampScaleValue_;                      // If in a switching state, what is the end point of the ramp?
    timestamp_diff_type rampLength_;            // If in a switching state, how long is the transition?
    float lastCalculatedRampValue_;             // Value of the ramp that was last calculated
    
    float onsetThresholdX_, onsetThresholdY_;   // Thresholds for detecting vibrato (first extremum)
    float onsetRatioX_, onsetRatioY_;           // Thresholds for detection vibrato (second extremum)
    timestamp_diff_type onsetTimeout_;          // Timeout between first and second extrema
    
    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
    
    timestamp_type lastZeroCrossingTimestamp_;  // Timestamp of the last zero crossing
    timestamp_diff_type lastZeroCrossingInterval_;   // Interval between the last two zero-crossings of filtered distance
    bool lastSampleWasPositive_;                // Whether the last sample was > 0
    
    bool foundFirstExtremum_;                   // Whether the first extremum has occurred
    float firstExtremumX_, firstExtremumY_;     // Where the first extremum occurred
    timestamp_type firstExtremumTimestamp_;     // Where the first extremum occurred
    timestamp_type lastExtremumTimestamp_;      // When the most recent extremum occurred
    
    float vibratoPrescaler_;                    // Parameter controlling prescaler before nonlinear scaling
    float vibratoRangeSemitones_;               // 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
    IIRFilterNode<float> filteredDistance_;     // Bandpass filtered finger motion
    CriticalSection distanceAccessMutex_;       // Mutex that protects the access buffer from changes
};

#endif /* defined(__touchkeys__TouchkeyVibratoMapping__) */