view Source/TouchKeys/PianoKey.h @ 20:dfff66c07936

Lots of minor changes to support building on Visual Studio. A few MSVC-specific #ifdefs to eliminate things Visual Studio doesn't like. This version now compiles on Windows (provided liblo, Juce and pthread are present) but the TouchKeys device support is not yet enabled. Also, the code now needs to be re-checked on Mac and Linux.
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Sun, 09 Feb 2014 18:40:51 +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/>.
 
  =====================================================================
 
  PianoKey.h: handles the operation of a single key on the keyboard,
  including fusing touch and MIDI data. Also has hooks for continuous
  key angle.
*/


#ifndef KEYCONTROL_PIANOKEY_H
#define KEYCONTROL_PIANOKEY_H

#include <set>
#include <map>
#include <list>
#include "../JuceLibraryCode/JuceHeader.h"
#include "../Utility/Node.h"
#include "PianoTypes.h"
#include "KeyIdleDetector.h"
#include "KeyPositionTracker.h"
#include "KeyTouchFrame.h"
#include "../Utility/Scheduler.h"
#include "../Utility/IIRFilter.h"

const unsigned int kPianoKeyStateBufferLength = 20;	// How many previous states to save
const unsigned int kPianoKeyIdleBufferLength = 10;  // How many idle/active transitions to save
const unsigned int kPianoKeyPositionTrackerBufferLength = 30; // How many state histories to save
const key_position kPianoKeyDefaultIdleActivityThreshold = scale_key_position(.020);
const key_position kPianoKeyDefaultIdlePositionThreshold = scale_key_position(.05);
const int kPianoKeyDefaultIdleCounter = 20;
const timestamp_diff_type kPianoKeyDefaultTouchTimeoutInterval = microseconds_to_timestamp(0); // was 20000
const timestamp_diff_type kPianoKeyGuiUpdateInterval = microseconds_to_timestamp(15000); // How frequently to update the position display

// Possible key states
enum {
	kKeyStateToBeInitialized = -1,
	kKeyStateUnknown = 0,
	kKeyStateDisabled,
	kKeyStateIdle,
	kKeyStateActive
};

// Possible touch events
enum {
	kTouchEventIdle = 0,
	kTouchEventAdd,
	kTouchEventRemove
};

typedef int key_state;

class PianoKeyboard;
class MidiKeyboardSegment;

/*
 * PianoKey
 *
 * This class holds the buffer and state information for a single piano key,
 * with methods to manage its status.
 */

class PianoKey : public TriggerDestination {
private:
	// ***** Internal Classes *****
	
	// Data on key touch events: what, when and where
	struct KeyTouchEvent {
		int type;
		timestamp_type timestamp;
		KeyTouchFrame frame;
	};
	
public:
	// ***** Constructors *****
	
	PianoKey(PianoKeyboard& keyboard, int noteNumber, int bufferLength);
	//PianoKey(PianoKey const& obj);
    
    // ***** Destructor *****
    
    ~PianoKey();
    		
	// ***** Access Methods *****
	
	Node<key_position>& buffer() { return positionBuffer_; }
	
	// ***** Control Methods *****
	//
	// Force changes in the key state (e.g. to resolve stuck notes)
	
	// Enable or disable a key from generating events
	void disable();
	void enable();
	
	// Force the key to the Idle state, provided it is enabled
	void forceIdle();
	
	// Clear any previous state, go back to initial state
	void reset();
	
	void insertSample(key_position pos, timestamp_type ts);
	
	// ***** Trigger Methods *****
	//
	// This will be called by positionBuffer_ on each new sample.  Examine each sample to see
	// whether the key is idle or not.
	
	void triggerReceived(TriggerSource* who, timestamp_type timestamp);
	
	// ***** MIDI Methods *****
	//
	// If MIDI input is used to control this note, the controller should call these functions
	
	void midiNoteOn(MidiKeyboardSegment *who, int velocity, int channel, timestamp_type timestamp);
	void midiNoteOff(MidiKeyboardSegment *who, timestamp_type timestamp);
	void midiAftertouch(MidiKeyboardSegment *who, int value, timestamp_type timestamp);
	
	bool midiNoteIsOn() { return midiNoteIsOn_; }
	int midiVelocity() { return midiVelocity_; }
	int midiChannel() { return midiChannel_; }
	void changeMidiChannel(int newChannel) { midiChannel_ = newChannel; }
    int midiOutputPort() { return midiOutputPort_; }
    void changeMidiOutputPort(int newPort) { midiOutputPort_ = newPort; }
	
	// ***** Touch Methods *****
	//
	// If touchkeys are used, the controller uses these functions to provide data
	// touchInsertFrame() implies touch is active if not already.
	
	void touchInsertFrame(KeyTouchFrame& newFrame, timestamp_type timestamp);
	void touchOff(timestamp_type timestamp);
	bool touchIsActive() { return touchIsActive_; }
    void setTouchSensorsPresent(bool present) { touchSensorsArePresent_ = present; }
	
	// This function is called on a timer when the key receives MIDI data before touch data
	// and wants to wait to integrate the two.  If the touch data doesn't materialize, this function
	// is called by the scheduler.
	timestamp_type touchTimedOut();
	
private:
	// ***** MIDI Methods (private) *****
	
	// This method does the real work of midiNoteOn(), and might be called from it directly
	// or after a delay.
	void midiNoteOnHelper(MidiKeyboardSegment *who);
	
	// ***** Touch Methods (private) *****
	
	std::pair<float, std::list<int> > touchMatchClosestPoints(const float* oldPoints, const float *newPoints, float count,
														 int oldIndex, std::set<int>& availableNewPoints, float currentTotalDistance); 
	void touchAdd(const KeyTouchFrame& frame, int index, timestamp_type timestamp);
	void touchRemove(const KeyTouchFrame& frame, int idRemoved, int remainingCount, timestamp_type timestamp);
	void touchMultiFingerGestures(const KeyTouchFrame& lastFrame, const KeyTouchFrame& newFrame, timestamp_type timestamp);
	
	// ***** State Machine Methods *****
	//
	// This class maintains a current state that determines its response to the incoming
	// key position data.
	
	void changeState(key_state newState);
	void changeState(key_state newState, timestamp_type timestamp);	
	
	void terminateActivity();
	
	// ***** Member Variables *****
	
	// Reference back to the keyboard which centralizes control
	PianoKeyboard& keyboard_;
	
	// Identity of the key (MIDI note number)
	int noteNumber_;
	
	// --- Data related to MIDI ---
	
	bool midiNoteIsOn_;					// Whether this note is currently active from MIDI
	int midiChannel_;					// MIDI channel currently associated with this note
    int midiOutputPort_;                // Which port MIDI output for this note goes to
	int midiVelocity_;					// Velocity of last MIDI onset
	Node<int> midiAftertouch_;			// Aftertouch history on this note, if any
	
	// Timestamps for the most recent MIDI note on and note off events
	timestamp_type midiOnTimestamp_, midiOffTimestamp_;
	
	// --- Data related to continuous key position ---

	Node<key_position> positionBuffer_;     // Buffer that holds the key positions
	KeyIdleDetector idleDetector_;          // Detector for whether the key is still or moving
    KeyPositionTracker positionTracker_;    // Object to track the various active states of the key
    timestamp_type timeOfLastGuiUpdate_;    // How long it's been since the last key position GUI call
    timestamp_type timeOfLastDebugPrint_;   // TESTING
    
	Node<key_state> stateBuffer_;		// State history
	key_state state_;					// Current state of the key (see enum above)
	CriticalSection stateMutex_;		// Use this to synchronize changes of state
    
    //IIRFilterNode<key_position> testFilter_;    // Filter the raw key position data, for testing
	
	// --- Data related to surface touches ---

    bool touchSensorsArePresent_;                   // Whether touch sensitivity exists on this key
	bool touchIsActive_;							// Whether the user is currently touching the key
	Node<KeyTouchFrame> touchBuffer_;				// Buffer that holds touchkey frames
	std::multimap<int, KeyTouchEvent> touchEvents_;	// Mapping from touch number to event
	bool touchIsWaiting_;							// Whether we're waiting for a touch to occur
    MidiKeyboardSegment *touchWaitingSource_;  // Who we're waiting from a touch for
	timestamp_type touchWaitingTimestamp_;			// When the timeout will occur
	timestamp_diff_type touchTimeoutInterval_;		// How long to wait for a touch before timing out
    
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PianoKey)
};

#endif /* KEYCONTROL_PIANOKEY_H */