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: MappingScheduler.h: implements a thread in which mapping actions are andrewm@0: performed. Each Mapping object implements a triggerReceived() method andrewm@0: which is called by the hardware I/O thread. This method should do a andrewm@0: minimal amount of work but pass the real work off to the performMapping() andrewm@0: method which is called by the MappingScheduler thread. The scheduler andrewm@0: also allows mapping calls to be performed in the absence of received data, andrewm@0: for example to cause a parameter to ramp down over time if no touch data andrewm@0: is received. andrewm@0: */ andrewm@0: andrewm@0: #ifndef __TouchKeys__MappingScheduler__ andrewm@0: #define __TouchKeys__MappingScheduler__ andrewm@0: andrewm@11: #undef DEBUG_MAPPING_SCHEDULER_STATISTICS andrewm@11: andrewm@0: #include andrewm@0: #include andrewm@0: #include andrewm@0: #include "../JuceLibraryCode/JuceHeader.h" andrewm@0: #include "Mapping.h" andrewm@0: andrewm@0: /* andrewm@0: * LockFreeQueue andrewm@0: * andrewm@0: * Placeholder implementation for a ring buffer. Will have issues with andrewm@0: * instruction reordering, but should serve the purpose for testing for now. andrewm@0: */ andrewm@0: andrewm@0: template andrewm@0: struct LockFreeQueue andrewm@0: { andrewm@0: LockFreeQueue() andrewm@0: { andrewm@0: list.push_back(T()); andrewm@0: iHead = list.begin(); andrewm@0: iTail = list.end(); andrewm@0: } andrewm@0: andrewm@0: void Produce(const T& t) andrewm@0: { andrewm@0: list.push_back(t); andrewm@0: iTail = list.end(); andrewm@0: list.erase(list.begin(), iHead); andrewm@0: } andrewm@0: andrewm@0: bool Consume(T& t) andrewm@0: { andrewm@0: typename TList::iterator iNext = iHead; andrewm@0: ++iNext; andrewm@0: if (iNext != iTail) andrewm@0: { andrewm@0: iHead = iNext; andrewm@0: t = *iHead; andrewm@0: return true; andrewm@0: } andrewm@0: return false; andrewm@0: } andrewm@0: andrewm@0: T Consume() andrewm@0: { andrewm@0: T tmp; andrewm@0: while (!Consume(tmp)) andrewm@0: { andrewm@0: ; andrewm@0: } andrewm@0: return tmp; andrewm@0: } andrewm@0: andrewm@11: #ifdef DEBUG_MAPPING_SCHEDULER_STATISTICS andrewm@11: unsigned long size() andrewm@11: { andrewm@11: return list.size(); andrewm@11: } andrewm@11: #endif andrewm@11: andrewm@0: private: andrewm@0: typedef std::list TList; andrewm@0: TList list; andrewm@0: typename std::list::iterator iHead, iTail; andrewm@0: }; andrewm@0: andrewm@0: /* andrewm@0: * MappingScheduler andrewm@0: * andrewm@0: * This class manages the timing and execution of Mapping objects. It performs a function andrewm@0: * similar to Scheduler but optimized specifically for running the performMapping() method andrewm@0: * of objects inheriting from Mapping. It maintains facilities to run mappings either now andrewm@0: * or in the future, including the ability to preempt future mapping calls with more immediate andrewm@0: * ones. andrewm@0: */ andrewm@0: andrewm@0: class MappingScheduler : public Thread { andrewm@0: private: andrewm@0: static const timestamp_diff_type kAllowableAdvanceExecutionTime; andrewm@0: andrewm@0: enum { andrewm@0: kActionUnknown = 0, andrewm@0: kActionRegister, andrewm@0: kActionPerformMapping, andrewm@0: kActionUnschedule, andrewm@0: kActionUnregister, andrewm@0: kActionUnregisterAndDelete andrewm@0: }; andrewm@0: andrewm@0: struct MappingAction { andrewm@0: public: andrewm@0: MappingAction() : who(0), counter(0), action(kActionUnknown) {} andrewm@0: MappingAction(Mapping *x, unsigned long y, int z) : andrewm@0: who(x), counter(y), action(z) {} andrewm@0: andrewm@0: Mapping *who; andrewm@0: unsigned long counter; andrewm@0: int action; andrewm@0: }; andrewm@0: andrewm@0: public: andrewm@0: // ***** Constructor ***** andrewm@0: // andrewm@0: // Note: This class is not copy-constructable. andrewm@0: andrewm@11: MappingScheduler(PianoKeyboard& keyboard, String threadName = "MappingScheduler"); andrewm@0: // ***** Destructor ***** andrewm@0: andrewm@0: ~MappingScheduler(); andrewm@0: andrewm@0: // ***** Thread Methods ***** andrewm@0: // andrewm@0: // These start and stop the thread that handles the scheduling of events. andrewm@0: andrewm@0: void start(); andrewm@0: void stop(); andrewm@0: andrewm@0: bool isRunning() { return isRunning_; } andrewm@0: andrewm@0: // The main Juce::Thread run loop andrewm@0: void run(); andrewm@0: andrewm@0: // ***** Event Management Methods ***** andrewm@0: // andrewm@0: // This interface provides the ability to schedule and unschedule events for andrewm@0: // current or future times. andrewm@0: andrewm@0: void registerMapping(Mapping *who); andrewm@0: andrewm@0: void scheduleNow(Mapping *who); andrewm@0: void scheduleLater(Mapping *who, timestamp_type timestamp); andrewm@0: andrewm@0: void unschedule(Mapping *who); andrewm@0: andrewm@0: void unregisterMapping(Mapping *who); andrewm@0: void unregisterAndDelete(Mapping *who); andrewm@0: andrewm@0: private: andrewm@0: // ***** Private Methods ***** andrewm@0: void performAction(MappingAction const& mappingAction); andrewm@0: andrewm@0: // Reference to the main PianoKeyboard object which holds the master timestamp andrewm@0: PianoKeyboard& keyboard_; andrewm@0: andrewm@0: // These variables keep track of the status of the separate thread running the events andrewm@0: CriticalSection actionsInsertionMutex_; andrewm@0: CriticalSection actionsLaterMutex_; andrewm@0: andrewm@0: WaitableEvent waitableEvent_; andrewm@0: bool isRunning_; andrewm@0: andrewm@0: // This counter keeps track of the sequence of insertions and executions andrewm@0: // of mappings. It is incremented whenever the scheduler finishes the "now" andrewm@0: // set of actions, and can be used to figure out whether an event has been andrewm@0: // duplicated or preempted. andrewm@0: unsigned long counter_; andrewm@0: std::map countersForMappings_; andrewm@0: andrewm@0: // These variables hold a ring buffer of actions to happen as soon as possible and a andrewm@0: // lock-synchronized collection of events that happen at later timestamps andrewm@0: LockFreeQueue actionsNow_; andrewm@0: std::multimap actionsLater_; andrewm@11: andrewm@11: #ifdef DEBUG_MAPPING_SCHEDULER_STATISTICS andrewm@11: timestamp_type lastDebugStatisticsTimestamp_; andrewm@11: andrewm@11: // Debugging method to indicate what is in the queue andrewm@11: void printDebugStatistics(); andrewm@11: #endif andrewm@0: }; andrewm@0: andrewm@0: andrewm@0: #endif /* defined(__TouchKeys__MappingScheduler__) */