2 Created on 23 juil. 2009
4 @author: Samuel Benveniste
6 from math
import floor
, ceil
11 from gradients
import gradients
12 from logging
.PickleableEvent
import PickleableEvent
17 The screen on which the game is played
20 The wiimotes used in this session
22 The main display window
24 The main display surface
26 The clock used to animate the screen
28 The background that is painted every time
30 The buffer for painting everything before bliting
32 The width of the window in pixels
34 The height of the window in pixels
36 True if the scale is G to C instead of C to C
38 True if crossing from note to note with a button pressed triggers a new note
40 The size of the scale used
42 The positions of the cursors on the screen, in pixels
47 def __init__(self
, instrumentChoice
=None, song
= None, cascade
=False, extendedScale
=False, defaultInstrumentChannel
= 16, defaultNote
= 60):
51 self
.blinkLength
= 200
52 self
.minimalVelocity
= 64
53 self
.shortScaleSize
= 8
54 self
.longScaleSize
= 11
56 self
.offset
= self
.longScaleSize
- self
.shortScaleSize
60 self
.savedHighlightedNote
= 0
62 self
.wiimotes
= instrumentChoice
.wiimotes
63 self
.activeWiimotes
= instrumentChoice
.activeWiimotes
64 self
.window
= instrumentChoice
.window
65 self
.screen
= instrumentChoice
.screen
66 self
.blitOrigin
= instrumentChoice
.blitOrigin
67 self
.clock
= instrumentChoice
.clock
68 self
.width
= instrumentChoice
.width
69 self
.height
= instrumentChoice
.height
70 self
.cursorPositions
= instrumentChoice
.cursorPositions
71 self
.savedScreen
= instrumentChoice
.savedScreen
72 self
.playerScreen
= instrumentChoice
.playerScreen
73 self
.extendedScale
= extendedScale
74 self
.cascade
= cascade
75 self
.joys
= instrumentChoice
.joys
76 self
.portOffset
= instrumentChoice
.portOffset
77 self
.eventLog
= instrumentChoice
.eventLog
78 self
.cursorPositions
= instrumentChoice
.cursorPositions
80 self
.songIterator
= self
.moveToNextNote()
81 self
.replay
= instrumentChoice
.replay
83 self
.defaultInstrumentChannel
= defaultInstrumentChannel
84 self
.defaultNote
= defaultNote
87 self
.backToInstrumentChoice
= False
90 self
.highlightedNote
= self
.songIterator
.next()
93 self
.savedBlinkOn
= False
94 ##Will prevent the song to move on if two consecutive notes are identical and the buttons have not been released in between the two
95 ##i.e. it guarantees that there will be an attack between two identical consecutive notes
96 self
.highlightIsFree
= True
99 self
.boundingRect
= None
102 self
.velocityLock
= []
104 self
._blinkOffset
= 0
106 self
.font
= pygame
.font
.Font(None,50)
107 self
.firstWiimote
= 0
108 while not self
.activeWiimotes
[self
.firstWiimote
] :
109 self
.firstWiimote
+= 1
110 self
.renderedNoteNames
= [self
.font
.render(constants
.noteNumberToName(note
),False,(0,0,0)) for note
in self
.wiimotes
[self
.firstWiimote
].instrument
.notes
[self
.offset
:]]
112 self
.drawBackground()
113 self
.initializeWiimotes()
116 while not self
.done
:
118 #Clear the cursors from the screen
119 if self
.hasChanged():
120 self
.drawBackground()
121 self
.playerScreen
.blit(self
.savedScreen
, (0, 0))
123 # Limit frame speed to 50 FPS
125 timePassed
= self
.clock
.tick(50)
127 self
._blinkOffset
+= timePassed
128 if self
._blinkOffset
> self
.blinkLength
:
129 self
._blinkOffset
-= self
.blinkLength
130 self
.blinkOn
= not self
.blinkOn
133 self
.eventLog
.update(timePassed
)
134 pickledEventsToPost
= self
.eventLog
.getPickledEvents()
135 for pickledEvent
in pickledEventsToPost
:
136 pygame
.event
.post(pickledEvent
.event
)
138 events
= pygame
.event
.get()
141 pickledEvents
= [PickleableEvent(event
.type,event
.dict) for event
in events
]
142 if pickledEvents
!= [] :
143 self
.eventLog
.appendEventGroup(pickledEvents
)
148 for i
in range(len(self
.wiimotes
)):
149 if self
.activeWiimotes
[i
]:
150 self
.wiimotes
[i
].cursor
.update(timePassed
, self
.cursorPositions
[i
])
151 if self
.buttonDown
[i
] :
152 self
.wiimotes
[i
].cursor
.flash()
153 self
.wiimotes
[i
].cursor
.blit(self
.playerScreen
)
155 self
.screen
.blit(self
.playerScreen
, (0,0))
157 pygame
.display
.flip()
159 for i
in range(len(self
.wiimotes
)):
160 if self
.activeWiimotes
[i
]:
161 self
.wiimotes
[i
].stopNote(self
.notes
[i
])
163 def drawBackground(self
):
164 self
.savedScreen
.fill((255,255,255))
166 if self
.extendedScale
:
167 self
.scaleSize
= self
.longScaleSize
169 self
.scaleSize
= self
.shortScaleSize
171 self
.noteRects
= [pygame
.Rect(i
* self
.width
/ self
.scaleSize
+self
.blitOrigin
[0], self
.blitOrigin
[1], self
.width
/ self
.scaleSize
+ 1, self
.height
+1) for i
in range(self
.scaleSize
)]
172 #inflate last noteRect to cover the far right pixels
173 self
.noteRects
[-1].width
= self
.noteRects
[-1].width
+ 1
175 #create bounding rect
176 self
.boundingRect
= self
.noteRects
[0].unionall(self
.noteRects
)
178 #fill the rectangles with a color gradient
180 startingHue
= 0.66666666666666663
182 for rectNumber
in range(self
.scaleSize
):
183 colorRatio
= float(rectNumber
) / (self
.scaleSize
- 1)
184 #hue will go from 0.6666... (blue) to 0 (red) as colorRation goes up
185 hue
= startingHue
* (1 - colorRatio
)
186 if self
.song
!= None:
187 if rectNumber
+ self
.offset
== self
.highlightedNote
:
188 #The color of the bottom of the rectangle in hls coordinates
189 bottomColorHls
= (hue
, 0.6, 1)
190 #The color of the top of the rectangle in hls coordinates
191 topColorHls
= (hue
, 0.9, 1)
193 #The color of the bottom of the rectangle in hls coordinates
194 bottomColorHls
= (hue
, 0.2, 1)
195 #The color of the top of the rectangle in hls coordinates
196 topColorHls
= (hue
, 0.4, 1)
198 #The color of the bottom of the rectangle in hls coordinates
199 bottomColorHls
= (hue
, 0.6, 1)
200 #The color of the top of the rectangle in hls coordinates
201 topColorHls
= (hue
, 0.9, 1)
203 #convert to rgb ranging from 0 to 255
204 bottomColorRgb
= [floor(255 * i
) for i
in colorsys
.hls_to_rgb(*bottomColorHls
)]
205 topColorRgb
= [floor(255 * i
) for i
in colorsys
.hls_to_rgb(*topColorHls
)]
207 bottomColorRgb
.append(255)
208 topColorRgb
.append(255)
210 bottomColorRgb
= tuple(bottomColorRgb
)
211 topColorRgb
= tuple(topColorRgb
)
213 self
.savedScreen
.blit(gradients
.vertical(self
.noteRects
[rectNumber
].size
, topColorRgb
, bottomColorRgb
), self
.noteRects
[rectNumber
])
215 textBlitPoint
= (self
.noteRects
[rectNumber
].left
+(self
.noteRects
[rectNumber
].width
-self
.renderedNoteNames
[rectNumber
].get_width())/2,
216 self
.noteRects
[rectNumber
].bottom
-self
.renderedNoteNames
[rectNumber
].get_height())
218 self
.savedScreen
.blit(self
.renderedNoteNames
[rectNumber
], textBlitPoint
)
220 pygame
.draw
.rect(self
.savedScreen
, pygame
.Color(0, 0, 0, 255), self
.noteRects
[rectNumber
], 2)
223 if self
.song
!= None and self
.blinkOn
:
224 borderSize
= self
.borderSize
225 pygame
.draw
.rect(self
.savedScreen
, pygame
.Color(0, 0, 0, 0), self
.noteRects
[self
.highlightedNote
-self
.offset
].inflate(borderSize
/2,borderSize
/2), borderSize
)
227 def initializeWiimotes(self
):
228 for loop
in self
.wiimotes
:
229 if loop
.port
== None :
230 loop
.port
= pygame
.midi
.Output(loop
.portNumber
)
232 self
.buttonDown
.append(False)
233 self
.velocityLock
.append(False)
235 def updateCursorPositionFromJoy(self
, joyEvent
):
236 joyName
= pygame
.joystick
.Joystick(joyEvent
.joy
).get_name()
237 correctedJoyId
= constants
.joyNames
.index(joyName
)
238 if correctedJoyId
< len(self
.cursorPositions
):
239 if joyEvent
.axis
== 0 :
240 self
.cursorPositions
[correctedJoyId
] = (int((joyEvent
.value
+ 1) / 2 * self
.screen
.get_width()), self
.cursorPositions
[correctedJoyId
][1])
241 if joyEvent
.axis
== 1 :
242 self
.cursorPositions
[correctedJoyId
] = (self
.cursorPositions
[correctedJoyId
][0], int((joyEvent
.value
+ 1) / 2 * self
.screen
.get_height()))
244 def heightToVelocity(self
, pos
, controllerNumber
):
245 if self
.song
!= None:
246 if self
.boundingRect
.collidepoint(pos
) and (self
.highlightedNote
== self
.notes
[controllerNumber
] or self
.velocityLock
[controllerNumber
]):
247 velocity
= int(floor((1 - (float(pos
[1])-self
.blitOrigin
[1]) / self
.height
) * (127-self
.minimalVelocity
))+self
.minimalVelocity
)
252 velocity
= self
.minimalVelocity
/3
254 if self
.boundingRect
.collidepoint(pos
):
255 velocity
= int(floor((1 - (float(pos
[1])-self
.blitOrigin
[1]) / self
.height
) * (127-self
.minimalVelocity
))+self
.minimalVelocity
)
257 velocity
= self
.minimalVelocity
260 def widthToNote(self
, pos
):
263 while self
.noteRects
[nn
].collidepoint(pos
) == False:
265 return(nn
+ self
.offset
)
269 def input(self
, event
):
271 if event
.type == pygame
.QUIT
:
272 for loop
in self
.wiimotes
:
277 if event
.type == pygame
.KEYDOWN
:
278 if event
.key
== pygame
.K_q
:
281 if event
.key
== pygame
.K_i
:
282 self
.backToInstrumentChoice
= True
285 if event
.type == pygame
.JOYAXISMOTION
:
286 joyName
= pygame
.joystick
.Joystick(event
.joy
).get_name()
287 correctedJoyId
= constants
.joyNames
.index(joyName
)
288 if self
.activeWiimotes
[correctedJoyId
]:
289 self
.updateCursorPositionFromJoy(event
)
290 wiimote
= self
.wiimotes
[correctedJoyId
]
291 pos
= self
.cursorPositions
[correctedJoyId
]
293 if self
.buttonDown
[correctedJoyId
]:
294 if self
.notes
[correctedJoyId
] != None:
295 velocity
= self
.heightToVelocity(pos
, correctedJoyId
)
296 CCHexCode
= wiimote
.getCCHexCode()
297 wiimote
.port
.write_short(CCHexCode
, 07, velocity
)
299 n
= self
.widthToNote(pos
)
300 if n
!= self
.notes
[correctedJoyId
]:
301 wiimote
.stopNote(self
.notes
[correctedJoyId
])
302 self
.notes
[correctedJoyId
] = n
304 if self
.song
!= None :
305 if self
.highlightedNote
== self
.notes
[correctedJoyId
]:
306 self
.highlightedNote
= self
.songIterator
.next()
307 self
.velocityLock
[correctedJoyId
] = True
309 self
.velocityLock
[correctedJoyId
] = False
311 velocity
= self
.heightToVelocity(pos
, correctedJoyId
)
313 wiimote
.playNote(self
.notes
[correctedJoyId
],velocity
)
315 if event
.type == pygame
.JOYBUTTONDOWN
:
317 joyName
= pygame
.joystick
.Joystick(event
.joy
).get_name()
318 correctedJoyId
= constants
.joyNames
.index(joyName
)
319 if self
.activeWiimotes
[correctedJoyId
]:
320 wiimote
= self
.wiimotes
[correctedJoyId
]
321 pos
= self
.cursorPositions
[correctedJoyId
]
323 if not self
.buttonDown
[correctedJoyId
]:
324 savedNote
= self
.notes
[correctedJoyId
]
325 self
.notes
[correctedJoyId
] = self
.widthToNote(pos
)
327 if self
.song
!= None :
328 if self
.highlightedNote
== self
.notes
[correctedJoyId
]:
329 self
.highlightedNote
= self
.songIterator
.next()
330 self
.velocityLock
[correctedJoyId
] = True
332 velocity
= self
.heightToVelocity(pos
, correctedJoyId
)
334 if velocity
!= None :
336 wiimote
.stopNote(savedNote
)
337 wiimote
.playNote(self
.notes
[correctedJoyId
],velocity
)
338 self
.buttonDown
[correctedJoyId
] = True
340 if event
.type == pygame
.JOYBUTTONUP
:
341 joyName
= pygame
.joystick
.Joystick(event
.joy
).get_name()
342 correctedJoyId
= constants
.joyNames
.index(joyName
)
343 if self
.activeWiimotes
[correctedJoyId
]:
344 self
.buttonDown
[correctedJoyId
] = False
345 wiimote
= self
.wiimotes
[correctedJoyId
]
346 if not self
.easyMode
:
347 wiimote
.stopNote(self
.notes
[correctedJoyId
])
348 self
.velocityLock
[correctedJoyId
] = False
350 if event
.type == pygame
.MOUSEMOTION
:
352 self
.updateCursorPositionFromMouse(event
)
355 while not self
.activeWiimotes
[correctedJoyId
] :
357 wiimote
= self
.wiimotes
[correctedJoyId
]
358 pos
= self
.cursorPositions
[correctedJoyId
]
360 if self
.buttonDown
[correctedJoyId
]:
361 if self
.notes
[correctedJoyId
] != None:
362 velocity
= self
.heightToVelocity(pos
, correctedJoyId
)
363 CCHexCode
= wiimote
.getCCHexCode()
364 wiimote
.port
.write_short(CCHexCode
, 07, velocity
)
366 n
= self
.widthToNote(pos
)
367 if n
!= self
.notes
[correctedJoyId
]:
368 wiimote
.stopNote(self
.notes
[correctedJoyId
])
369 self
.notes
[correctedJoyId
] = n
371 if self
.song
!= None :
372 if self
.highlightedNote
== self
.notes
[correctedJoyId
]:
373 self
.highlightedNote
= self
.songIterator
.next()
374 self
.velocityLock
[correctedJoyId
] = True
376 self
.velocityLock
[correctedJoyId
] = False
378 velocity
= self
.heightToVelocity(pos
, correctedJoyId
)
380 wiimote
.playNote(self
.notes
[correctedJoyId
],velocity
)
382 if event
.type == pygame
.MOUSEBUTTONDOWN
:
384 if event
.button
== 1:
386 while not self
.activeWiimotes
[correctedJoyId
] :
388 wiimote
= self
.wiimotes
[correctedJoyId
]
389 pos
= self
.cursorPositions
[correctedJoyId
]
391 if not self
.buttonDown
[correctedJoyId
]:
392 self
.notes
[correctedJoyId
] = self
.widthToNote(pos
)
394 if self
.song
!= None :
395 if self
.highlightedNote
== self
.notes
[correctedJoyId
]:
396 self
.highlightedNote
= self
.songIterator
.next()
397 self
.velocityLock
[correctedJoyId
] = True
399 velocity
= self
.heightToVelocity(pos
, correctedJoyId
)
401 wiimote
.playNote(self
.notes
[correctedJoyId
],velocity
)
402 self
.buttonDown
[correctedJoyId
] = True
404 if event
.button
== 2:
408 if event
.type == pygame
.MOUSEBUTTONUP
:
411 while not self
.activeWiimotes
[correctedJoyId
] :
413 wiimote
= self
.wiimotes
[correctedJoyId
]
414 wiimote
.stopNote(self
.notes
[correctedJoyId
])
415 self
.buttonDown
[correctedJoyId
] = False
416 self
.velocityLock
[correctedJoyId
] = False
418 def hasChanged(self
):
420 if self
.song
!= None:
421 if self
.blinkOn
!= self
.savedBlinkOn
or self
.highlightedNote
!= self
.savedHighlightedNote
:
422 self
.savedBlinkOn
= self
.blinkOn
423 self
.savedHighlightedNote
= self
.highlightedNote
427 def updateCursorPositionFromMouse(self
, mouseEvent
):
429 while not self
.activeWiimotes
[correctedJoyId
] :
431 self
.cursorPositions
[correctedJoyId
] = mouseEvent
.pos
433 def moveToNextNote(self
):
435 if self
.song
== None:
438 for note
in self
.song
: