Package control :: Module theTime
[hide private]
[frames] | no frames]

Source Code for Module control.theTime

  1  #!/usr/bin/python 
  2  # -*- coding: latin-1 -*- 
  3   
  4  """  
  5  The module for (relaxed) timed automation. 
  6   
  7  This timer module is based on the stardard python time.sleep() and may not be 100% accurate. 
  8  For a lot of purposes however, the timing accuracy is sufficient, e.g. automation on a "per minute basis". 
  9   
 10  @author: Øyvind Brandtsegg 
 11  @contact: obrandts@gmail.com 
 12  @license: GPL 
 13  """ 
 14   
 15  import threading 
 16  #import thread 
 17  import time 
 18  import bisect 
 19  import mutex 
 20   
 21   
22 -class TheTime(threading.Thread):
23 """ 24 The class containing timer and timed queue methods. 25 26 Automation is executed via a queue of events to be executed in time, format for the queue is a list of events, 27 with each event represented as a list with the following format: [type, time, data]. 28 The type field is a string, to be parsed in eventCaller.parseEvents(). Time is in beats and may be fractional. 29 The data field may contain several parameters (not as sublist). 30 """ 31
32 - def __init__(self, eventCaller, bpm=60):
33 """ 34 The class constructor. 35 36 Under normal operation this timer instance is a standard seconds and minutes clock so the bpm should not be changed. 37 38 @param self: The object pointer. 39 @param eventCaller: A pointer to the eventCaller object. 40 @param bpm: The beats per minute for the beat counter. Default = 60, this should not be modified in normal cicumstances. 41 """ 42 threading.Thread.__init__(self) 43 self.eventCaller = eventCaller 44 self.queueMutex = mutex.mutex() 45 """The mutex for thread safe handling of the queue list.""" 46 self.queueMutex.testandset() 47 self.isRunning = True 48 """Flag to keep the clock thread running, set to False to exit thread.""" 49 self.timeResolution = 0.001 50 """Timer resolution in seconds, 0.001=millisecond resolution (not accurate).""" 51 self.queue = [] 52 """The list of events in the timed queue.""" 53 self.beatCounter = 0 54 """Counter for quarter notes at a given bpm.""" 55 self.fractionalBeat = 0.0 56 """Counter for fractions of a beat.""" 57 self.bpm = bpm 58 """The tempo in beats per minute for the beat counter.""" 59 self.timeNow = 0 60 self.timeAtStart = time.clock() 61 self.runClock = 0 62 """Flag to run or pause the clock.""" 63 self.prevTime = 0
64
65 - def run(self):
66 """ 67 The main clock increment method, also polling the event queue. 68 69 @param self: The object pointer. 70 """ 71 nextBeatTime = 0 72 73 # Execute this loop as long as self.isRunning == True 74 while (self.isRunning): 75 if self.runClock == 1: 76 77 # unlock muex, processing any insertion or deletion of events from queue 78 while self.queueMutex.test(): 79 self.queueMutex.unlock() 80 81 # check current time 82 timePerBeat = 60.0 / self.bpm 83 self.timeNow = time.clock() 84 # update beat counter 85 if self.timeNow >= nextBeatTime: 86 self.beatCounter += 1 87 self.fractionalBeat = 0 88 nextBeatTime = self.timeNow + timePerBeat 89 # check queue here, execute pending events in queue, 90 if self.queue != []: 91 self.checkQueue(self.beatCounter+self.fractionalBeat) 92 # update the fractional beat counter 93 if self.timeNow >= self.prevTime + self.timeResolution: 94 self.fractionalBeat += ((self.timeNow-self.prevTime)/timePerBeat) 95 #if self.fractionalBeat > 1.0 - self.timeResolution: self.fractionalBeat = 1.0 - self.timeResolution 96 #print 'beat', self.beatCounter+self.fractionalBeat 97 self.prevTime = self.timeNow 98 time.sleep(self.timeResolution) # millisecond resolution
99
100 - def startStopClock(self, state, offset=1):
101 """ 102 Start or stop (pause) the clock. 103 104 An optional offset may increment the current beat count when starting the clock. 105 106 @param self: The object pointer. 107 @param state: The running state (True or False) of the clock. 108 @param offset: The beat offset to be used for incrementing the beat counter when starting the clock. 109 """ 110 if state: # increment beat counter with offset when starting clock 111 self.beatCounter += offset 112 self.fractionalBeat = 0 113 self.prevTime = time.clock() 114 self.runClock = state
115
116 - def getCurrentBeat(self):
117 """ 118 Get the current beat count. 119 120 @param self: The object pointer. 121 @return: beatCount: The current beat count. 122 """ 123 return self.beatCounter
124
125 - def getQueue(self):
126 """ 127 Get the contents of the timed queue. 128 129 @param self: The object pointer. 130 @return: The list of events in the timed queue. 131 """ 132 return self.queue
133
134 - def removeEvent(self, event):
135 """ 136 Remove an event from the timed queue. 137 138 @param self: The object pointer. 139 @param event: The event to be removed. 140 """ 141 self.queueMutex.lock(self.removeEvent2, event)
142
143 - def removeEvent2(self, event):
144 """ 145 Helper method for removeEvent, thread safe handling of the timed queue list. 146 147 @param self: The object pointer. 148 @param event: The event to be removed. 149 """ 150 try: 151 self.queue.remove(event) 152 except: 153 print 'could not remove event not found in queue', event
154
155 - def checkQueue(self, beat):
156 """ 157 Check if any events in the queue are due for execution. 158 159 Execute any events with timestamp <= timeNow. 160 Times are in beats and so will be relative to the current tempo in bpm, timeNow is a beat counter, and the beat counter may be fractional. 161 162 @param self: The object pointer. 163 @param beat: The beat count (including any fractional part) used as "now" for dispersing timed events in the queue. 164 """ 165 if self.queue == []: 166 return 167 if self.queue[0][0] <= beat: 168 self.eventCaller.parseEvent(self.queue.pop(0)) 169 self.checkQueue(beat)
170
171 - def insertQueue(self, timeStamp, event):
172 """ 173 Insert an event into the queue, keeping the timeStamps in sorted order. 174 175 @param self: The object pointer. 176 @param timeStamp: The time to insert the event at. 177 @param event: The event to insert. 178 """ 179 self.queueMutex.lock(self.insertQueue2, [timeStamp, event])
180
181 - def insertQueue2(self, timeAndEvent):
182 """ 183 Helper method for insertQueue, threadsafe queue access. 184 185 @param self: The object pointer. 186 @param timeAndEvent: A list of [timeStamp, event] for insertion into the queue. 187 """ 188 bisect.insort(self.queue, timeAndEvent)
189
190 - def insertQueueWithOffset(self, timeStamp, event):
191 """ 192 Insert an event into the queue, keeping the timeStamps in sorted order. The time for the event is offset with the current beat time, so e.g. a time of 1 will be executed one beat into the future. 193 194 @param self: The object pointer. 195 @param timeStamp: The time to insert the event at. 196 @param event: The event to insert. 197 """ 198 self.queueMutex.lock(self.insertQueueWithOffset2, [timeStamp, event])
199
200 - def insertQueueWithOffset2(self, timeAndEvent):
201 """ 202 Helper method for insertQueueWithOffset, threadsafe queue access. 203 204 @param self: The object pointer. 205 @param timeAndEvent: A list of [timeStamp, event] for insertion into the queue. 206 """ 207 timeStamp, event = timeAndEvent 208 timeStamp += (self.beatCounter + self.fractionalBeat) 209 bisect.insort(self.queue, [timeStamp, event])
210
211 - def insertListFromNow(self, list):
212 """ 213 Insert a list of timed events into the queue, transposing each event's timestamp relative to the "now" time. 214 215 @param self: The object pointer. 216 @param list: The list of events to be inserted. 217 """ 218 for event in list: 219 event[0] += (self.beatCounter + self.fractionalBeat) 220 self.insertQueue(event[0], event[1:])
221
222 - def setBpm(self, bpm):
223 """ 224 Set the clock tempo in beats per minute. 225 226 @param self: The object pointer. 227 """ 228 self.bpm = bpm
229
230 - def stop(self):
231 """ 232 Stops the thread from running, by setting isRunning = False. 233 234 @param self: The object pointer. 235 """ 236 self.isRunning = False 237 print 'stopping time functions'
238