1
2
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
17 import time
18 import bisect
19 import mutex
20
21
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
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
74 while (self.isRunning):
75 if self.runClock == 1:
76
77
78 while self.queueMutex.test():
79 self.queueMutex.unlock()
80
81
82 timePerBeat = 60.0 / self.bpm
83 self.timeNow = time.clock()
84
85 if self.timeNow >= nextBeatTime:
86 self.beatCounter += 1
87 self.fractionalBeat = 0
88 nextBeatTime = self.timeNow + timePerBeat
89
90 if self.queue != []:
91 self.checkQueue(self.beatCounter+self.fractionalBeat)
92
93 if self.timeNow >= self.prevTime + self.timeResolution:
94 self.fractionalBeat += ((self.timeNow-self.prevTime)/timePerBeat)
95
96
97 self.prevTime = self.timeNow
98 time.sleep(self.timeResolution)
99
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:
111 self.beatCounter += offset
112 self.fractionalBeat = 0
113 self.prevTime = time.clock()
114 self.runClock = state
115
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
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
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
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
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
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
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
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
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
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
223 """
224 Set the clock tempo in beats per minute.
225
226 @param self: The object pointer.
227 """
228 self.bpm = bpm
229
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