1
- __copyright__ = """ Copyright (c) 2010 Torsten Schmits
1
+ __copyright__ = """ Copyright (c) 2010-2011 Torsten Schmits
2
2
3
3
This program is free software; you can redistribute it and/or modify it
4
4
under the terms of the GNU General Public License as published by the
17
17
18
18
from time import sleep
19
19
from datetime import datetime , timedelta
20
- import collections , logging , itertools
20
+ import collections , logging , itertools , random
21
21
22
- import pygame , VisionEgg
22
+ import VisionEgg
23
23
24
24
from lib .vision_egg .util .frame_counter import FrameCounter
25
25
29
29
def _frames (time ):
30
30
return int (round (float (time ) * _refresh_rate ))
31
31
32
+ def _is_seq (l ):
33
+ return isinstance (l , collections .Sequence )
34
+
35
+ class StimulusTime (object ):
36
+ def __init__ (self , time , vsync = True ):
37
+ self ._vsync = vsync
38
+ self ._frames = None
39
+ self .set (time )
40
+
41
+ def set (self , time ):
42
+ self ._adapted = self ._time = time
43
+ if self :
44
+ self ._frames = _frames (self ._time )
45
+ if self ._vsync :
46
+ self ._adapted = round (self ._frames * _frame_duration , 6 )
47
+
48
+ @property
49
+ def time (self ):
50
+ return timedelta (seconds = self ._adapted )
51
+
52
+ @property
53
+ def original (self ):
54
+ return self ._time
55
+
56
+ @property
57
+ def adapted (self ):
58
+ return self ._adapted
59
+
60
+ @property
61
+ def frames (self ):
62
+ return self ._frames
63
+
64
+ def __call__ (self , frames ):
65
+ return self .frames if frames else self .time
66
+
67
+ def __nonzero__ (self ):
68
+ return self ._time is not None
69
+
70
+ class RandomStimulusTime (StimulusTime ):
71
+ def __init__ (self , interval , * a , ** kw ):
72
+ self ._interval = interval
73
+ StimulusTime .__init__ (self , 0 , * a , ** kw )
74
+
75
+ def _resample (self ):
76
+ self .set (random .uniform (* self ._interval ))
77
+
78
+ @property
79
+ def original (self ):
80
+ return 'random(%s, %s)' % tuple (self ._interval )
81
+
82
+ @property
83
+ def adapted (self ):
84
+ return self .original
85
+
86
+ def __call__ (self , frames ):
87
+ self ._resample ()
88
+ return StimulusTime .__call__ (self , frames )
89
+
90
+ def _stimulus_time (time , vsync ):
91
+ typ = lambda t : RandomStimulusTime if _is_seq (t ) else StimulusTime
92
+ return typ (time )(time , vsync = vsync )
93
+
32
94
class StimulusPainter (object ):
33
95
""" Painter for a series of stimuli. """
34
96
def __init__ (self , prepare , wait , view , flag , wait_style_fixed = False ,
35
97
print_frames = False , suspendable = True , pre_stimulus = None ,
36
- frame_transition = False ):
98
+ frame_transition = False , vsync = True ):
37
99
self ._prepare_func = prepare
38
100
self ._wait_times = itertools .cycle (wait )
39
101
self ._view = view
@@ -43,10 +105,12 @@ def __init__(self, prepare, wait, view, flag, wait_style_fixed=False,
43
105
self ._suspendable = suspendable
44
106
self ._pre_stimulus = pre_stimulus
45
107
self ._frame_transition = frame_transition
108
+ self ._vsync = vsync
46
109
self ._logger = logging .getLogger ('StimulusPainter' )
47
110
self ._frame_counter = FrameCounter (self ._flag )
48
111
self ._suspended_time = timedelta ()
49
112
self ._wait = self ._frame_wait if frame_transition else self ._time_wait
113
+ self ._online_times = []
50
114
51
115
def run (self ):
52
116
if self ._print_frames or self ._frame_transition :
@@ -67,7 +131,7 @@ def run(self):
67
131
def _frame_wait (self ):
68
132
next_interval = self ._next_duration
69
133
while self ._flag and self ._frame_counter .last_interval < next_interval :
70
- sleep ( 0.001 )
134
+ self . _frame_counter . sync ( )
71
135
if self ._print_frames :
72
136
self ._logger .debug ('Frames after waiting: %d' %
73
137
self ._frame_counter .last_interval )
@@ -94,15 +158,18 @@ def _present(self):
94
158
if self ._print_frames :
95
159
self ._logger .debug ('Frames before stimulus change: %d' %
96
160
self ._frame_counter .last_interval )
97
- self ._frame_counter .lock ()
98
161
if self ._pre_stimulus is not None :
99
162
self ._pre_stimulus ()
163
+ self ._frame_counter .lock ()
100
164
self ._view .update ()
101
165
102
166
@property
103
167
def _next_duration (self ):
104
- nxt = self ._wait_times .next () + self ._suspended
105
- return nxt
168
+ try :
169
+ nxt = self ._online_times .pop (0 ) or self ._wait_times .next ()
170
+ except StopIteration :
171
+ raise Exception ('No specified stimulus times available!' )
172
+ return nxt (self ._frame_transition ) + self ._suspended
106
173
107
174
@property
108
175
def _suspended (self ):
@@ -116,9 +183,13 @@ def _do_prepare(self):
116
183
117
184
class StimulusIterator (StimulusPainter ):
118
185
""" Painter using an iterator. """
186
+ def __init__ (self , * a , ** kw ):
187
+ StimulusPainter .__init__ (self , * a , ** kw )
188
+
119
189
def _do_prepare (self ):
120
190
try :
121
- self ._prepare_func .next ()
191
+ nxt = self ._prepare_func .next ()
192
+ self ._online_times .append (_stimulus_time (nxt , self ._vsync ))
122
193
return True
123
194
except StopIteration :
124
195
return False
@@ -138,37 +209,41 @@ def __init__(self, view, flag, print_frames=False, vsync_times=False,
138
209
self ._frame_transition = frame_transition
139
210
self ._logger = logging .getLogger ('StimulusSequenceFactory' )
140
211
141
- def create (self , prepare , times , wait_style_fixed , suspendable = True ,
142
- pre_stimulus = None ):
212
+ def create (self , prepare , times = None , wait_style_fixed = True ,
213
+ suspendable = True , pre_stimulus = None ):
143
214
""" Create a StimulusPainter using the preparation object
144
215
prepare, with given presentation times and wait style.
145
216
If suspendable is True, the sequence halts when on_pause is
146
217
pressed.
147
218
Global parameters from pyff are used as given in __init__.
148
219
"""
149
- if not isinstance ( times , collections . Sequence ) :
150
- times = [times ]
151
- times = self . _adapt_times ( times )
152
- typ = StimulusIterator if hasattr ( prepare , '__iter__' ) else \
153
- StimulusSequence
154
- if not self . _frame_transition :
155
- times = [ timedelta ( seconds = t ) for t in times ]
220
+ if times is None :
221
+ times = []
222
+ else :
223
+ times = self . _times ( times )
224
+ self . _debug_times ( times )
225
+ typ = ( StimulusIterator if hasattr ( prepare , '__iter__' ) else
226
+ StimulusSequence )
156
227
return typ (prepare , times , self ._view , self ._flag ,
157
228
wait_style_fixed = wait_style_fixed ,
158
229
print_frames = self ._print_frames , suspendable = suspendable ,
159
230
pre_stimulus = pre_stimulus ,
160
- frame_transition = self ._frame_transition )
231
+ frame_transition = self ._frame_transition ,
232
+ vsync = self ._vsync_times )
233
+
234
+ def _times (self , times ):
235
+ if not _is_seq (times ):
236
+ times = [times ]
237
+ return [_stimulus_time (t , vsync = self ._vsync_times ) for t in times ]
161
238
162
- def _adapt_times (self , times ):
163
- frames = [_frames (time ) for time in times ]
164
- new_times = [round (t * _frame_duration , 6 ) for t in frames ]
239
+ def _debug_times (self , times ):
240
+ original = [t .original for t in times ]
241
+ adapted = [t .adapted for t in times ]
242
+ frames = [t .frames for t in times ]
165
243
if self ._frame_transition :
166
244
text = ('Adapted stimulus times %s to %s frames (%s)' %
167
- (times , frames , new_times ))
168
- times = frames
245
+ (original , frames , adapted ))
169
246
self ._logger .debug (text )
170
247
elif self ._vsync_times :
171
- text = 'Adapted stimulus times %s to %s' % (times , new_times )
172
- times = new_times
248
+ text = 'Adapted stimulus times %s to %s' % (original , adapted )
173
249
self ._logger .debug (text )
174
- return times
0 commit comments