0e095c8984a0a139e4a257e7c92b56bafb23b8b6
[minwii.git] / src / gui / StaticFamiliarizer.py
1 '''
2 Created on 23 juil. 2009
3
4 @author: Samuel Benveniste
5 '''
6 from math import floor, ceil
7 import pygame
8 import pygame.midi
9 import sys
10 import colorsys
11 import constants
12 from gradients import gradients
13 from logging.PickleableEvent import PickleableEvent
14 from instruments.Instrument import Instrument
15 from cursor.WarpingCursor import *
16 from controllers.Wiimote import Wiimote
17 from logging.EventLog import EventLog
18
19
20 class StaticFamiliarizer:
21 '''
22 The screen on which the game is played
23
24 wiimotes:
25 The wiimotes used in this session
26 window:
27 The main display window
28 screen:
29 The main display surface
30 clock:
31 The clock used to animate the screen
32 savedScreen:
33 The background that is painted every time
34 playerScreen:
35 The buffer for painting everything before bliting
36 width:
37 The width of the window in pixels
38 height:
39 The height of the window in pixels
40 extendScale :
41 True if the scale is G to C instead of C to C
42 cascade:
43 True if crossing from note to note with a button pressed triggers a new note
44 scaleSize:
45 The size of the scale used
46 cursorPositions:
47 The positions of the cursors on the screen, in pixels
48 '''
49
50
51
52 def __init__(self, wiimotes, window, screen, clock, joys, portOffset,activeWiimotes,replay = False, level = 0, defaultInstrumentChannel = 16, defaultNote = 60, eventLog = None):
53 '''
54 Constructor
55 '''
56 self.firstClickTime = None
57 self.firstClickInTime = None
58 self.duration = None
59 self.clicks = 0
60 self.clicksIn = 0
61
62 pygame.font.init()
63 self.font = pygame.font.Font(None,60)
64 self.congratulations = ["Bien !","Tres Bien !","Bravo !","Excellent !","Felicitations !"]
65 self.renderedCongratulations = [self.font.render(congratulation,False,(0,0,0)) for congratulation in self.congratulations]
66 self.congratulationCount = None
67 self.isCongratulating = False
68 self.congratulationTimer = 0
69 self.congratulationLength = 2000
70 self.congratulationPos = None
71
72 self.blinkLength = 200
73 self.minimalVelocity = 64
74 self.shortScaleSize = 8
75 self.longScaleSize = 11
76 self.borderSize = 5
77 self.savedHighlightedNote = 0
78 self.scaleFactor = 1
79 self.wiimotes = wiimotes
80 self.window = window
81 self.screen = screen
82 self.clock = clock
83 self.width = int(floor(screen.get_width()*self.scaleFactor))
84 self.height = int(floor(screen.get_height()*self.scaleFactor))
85 self.blitOrigin = ((self.screen.get_width()-self.width)/2,(self.screen.get_height()-self.height)/2)
86 self.joys = joys
87 self.portOffset = portOffset
88 self.savedScreen = pygame.Surface(self.screen.get_size())
89 self.savedScreen.fill((255,255,255))
90 self.playerScreen = pygame.Surface(self.savedScreen.get_size())
91 self.playerScreen.blit(self.savedScreen, (0, 0))
92 self.cursorPositions = []
93 self.level = level
94 self.nextLevel = None
95 self.activeWiimotes = activeWiimotes
96
97 for i in range(len(self.wiimotes)):
98 #Set the screen for the cursors (it can't be set before)
99 self.wiimotes[i].cursor.screen = self.playerScreen
100 self.cursorPositions.append(self.wiimotes[i].cursor.centerPosition)
101
102 if eventLog == None:
103 self.eventLog = EventLog()
104 self.replay = False
105 else:
106 self.eventLog = eventLog
107 self.replay = replay
108
109 self.defaultInstrumentChannel = defaultInstrumentChannel
110 self.defaultNote = defaultNote
111
112 self.done = False
113 self.backToInstrumentChoice = False
114 self.easyMode = False
115
116 self.noteRects = []
117 self.boundingRect = None
118 self.notes = []
119 self.buttonDown = []
120 self.velocityLock = []
121
122 self.drawBackground()
123 self.initializeWiimotes()
124 events = pygame.event.get()
125
126 #The main loop
127 while not self.done :
128
129 self.playerScreen.blit(self.savedScreen, (0, 0))
130
131 # Limit frame speed to 50 FPS
132 #
133 timePassed = self.clock.tick(10000)
134
135 if self.replay:
136 self.eventLog.update(timePassed)
137 pickledEventsToPost = self.eventLog.getPickledEvents()
138 for pickledEvent in pickledEventsToPost:
139 pygame.event.post(pickledEvent.event)
140
141 events = pygame.event.get()
142
143 if not self.replay:
144 pickledEvents = [PickleableEvent(event.type,event.dict) for event in events]
145 if pickledEvents != [] :
146 self.eventLog.appendEventGroup(pickledEvents)
147
148 for event in events:
149 self.input(event)
150
151 if self.isCongratulating :
152 self.congratulationTimer += timePassed
153 if self.congratulationTimer < self.congratulationLength :
154 self.blitCongratulation()
155 else :
156 self.isCongratulating = False
157
158 for i in range(len(self.wiimotes)):
159 if self.activeWiimotes[i]:
160 self.wiimotes[i].cursor.update(timePassed, self.cursorPositions[i])
161 if self.buttonDown[i] :
162 self.wiimotes[i].cursor.flash()
163 self.wiimotes[i].cursor.blit(self.playerScreen)
164
165 self.screen.blit(self.playerScreen, (0,0))
166
167 pygame.display.flip()
168
169 for i in range(len(self.wiimotes)):
170 if self.activeWiimotes[i]:
171 if self.notes[i] != None :
172 self.wiimotes[i].stopNote(self.notes[i])
173 if self.replay :
174 self.duration = self.eventLog.getCurrentTime()
175
176 def drawBackground(self):
177 self.savedScreen.fill((255,255,255))
178 if self.level == 0 :
179 A = [4]
180 else :
181 A = [1,7]
182
183 self.noteRects = [pygame.Rect(i * self.width / 11+self.blitOrigin[0], self.blitOrigin[1], (self.width / 11 + 1)*3, self.height+1) for i in A]
184
185 #create bounding rect
186 self.boundingRect = self.noteRects[0].unionall(self.noteRects)
187
188 #fill the rectangles with a color gradient
189 #We start with blue
190 startingHue = 0.66666666666666663
191
192 for rectNumber in range(len(self.noteRects)) :
193 colorRatio = float(A[rectNumber]) / (11 - 1)
194 #hue will go from 0.6666... (blue) to 0 (red) as colorRation goes up
195 hue = startingHue * (1 - colorRatio)
196 #The color of the bottom of the rectangle in hls coordinates
197 bottomColorHls = (hue, 0.6, 1)
198 #The color of the top of the rectangle in hls coordinates
199 topColorHls = (hue, 0.9, 1)
200
201 #convert to rgb ranging from 0 to 255
202 bottomColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*bottomColorHls)]
203 topColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*topColorHls)]
204 #add transparency
205 bottomColorRgb.append(255)
206 topColorRgb.append(255)
207 #convert to tuple
208 bottomColorRgb = tuple(bottomColorRgb)
209 topColorRgb = tuple(topColorRgb)
210
211 self.savedScreen.blit(gradients.vertical(self.noteRects[rectNumber].size, topColorRgb, bottomColorRgb), self.noteRects[rectNumber])
212
213 pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 255), self.noteRects[rectNumber], 2)
214
215 def initializeWiimotes(self):
216 for loop in self.wiimotes:
217 if loop.port == None :
218 loop.port = pygame.midi.Output(loop.portNumber)
219 self.notes.append(0)
220 self.buttonDown.append(False)
221 self.velocityLock.append(False)
222
223 def updateCursorPositionFromJoy(self, joyEvent):
224 joyName = pygame.joystick.Joystick(joyEvent.joy).get_name()
225 correctedJoyId = constants.joyNames.index(joyName)
226 if correctedJoyId < len(self.cursorPositions):
227 if joyEvent.axis == 0 :
228 self.cursorPositions[correctedJoyId] = (int((joyEvent.value + 1) / 2 * self.screen.get_width()), self.cursorPositions[correctedJoyId][1])
229 if joyEvent.axis == 1 :
230 self.cursorPositions[correctedJoyId] = (self.cursorPositions[correctedJoyId][0], int((joyEvent.value + 1) / 2 * self.screen.get_height()))
231
232 def heightToVelocity(self, pos, controllerNumber):
233 velocity = int(floor((1 - (float(pos[1])-self.blitOrigin[1]) / self.height) * (127-self.minimalVelocity))+self.minimalVelocity)
234 return(velocity)
235
236 def widthToNote(self, pos):
237 nn = 0
238 try :
239 while self.noteRects[nn].collidepoint(pos) == False:
240 nn = nn + 1
241 return(nn)
242 except(IndexError):
243 return(None)
244
245 def congratulate(self,targetRect,posy):
246 if self.congratulationCount != None :
247 if self.congratulationCount < len(self.congratulations)-1:
248 self.congratulationCount += 1
249 else :
250 self.congratulationCount = 0
251 self.congratulationTimer = 0
252 self.congratulationPos = (targetRect.left+(targetRect.width-self.renderedCongratulations[self.congratulationCount].get_width())/2,posy)
253 self.isCongratulating = True
254
255 def resetCongratulation(self):
256 self.congratulationCount = None
257 self.congratulationPos = None
258 self.isCongratulating = False
259
260 def blitCongratulation(self):
261 self.playerScreen.blit(self.renderedCongratulations[self.congratulationCount],self.congratulationPos)
262
263 def input(self, event):
264
265 print event
266
267 if event.type == pygame.QUIT:
268 for loop in self.wiimotes:
269 del loop.port
270 pygame.midi.quit()
271 sys.exit(0)
272
273 if event.type == pygame.KEYDOWN:
274 if event.key == pygame.K_q:
275 self.nextLevel = None
276 self.done = True
277
278 if event.key == pygame.K_w:
279 self.nextLevel = 0
280 self.done = True
281
282 if event.key == pygame.K_e:
283 self.nextLevel = 1
284 self.done = True
285
286 if event.key == pygame.K_r:
287 self.nextLevel = 2
288 self.done = True
289
290 if event.key == pygame.K_t:
291 self.nextLevel = 3
292 self.done = True
293
294 if event.type == pygame.JOYAXISMOTION:
295
296
297 joyName = pygame.joystick.Joystick(event.joy).get_name()
298 correctedJoyId = constants.joyNames.index(joyName)
299 if self.activeWiimotes[correctedJoyId]:
300 self.updateCursorPositionFromJoy(event)
301 wiimote = self.wiimotes[correctedJoyId]
302 pos = self.cursorPositions[correctedJoyId]
303
304 if self.buttonDown[correctedJoyId]:
305 wiimote.cursor.flash()
306 if self.notes[correctedJoyId] != None:
307 velocity = self.heightToVelocity(pos, correctedJoyId)
308 CCHexCode = wiimote.getCCHexCode()
309 wiimote.port.write_short(CCHexCode, 07, velocity)
310
311 if event.type == pygame.JOYBUTTONDOWN :
312
313 joyName = pygame.joystick.Joystick(event.joy).get_name()
314 correctedJoyId = constants.joyNames.index(joyName)
315 if self.activeWiimotes[correctedJoyId]:
316 wiimote = self.wiimotes[correctedJoyId]
317 pos = self.cursorPositions[correctedJoyId]
318 wiimote.cursor.flash()
319 if self.replay :
320 self.clicks += 1
321 if self.firstClickTime == None :
322 self.firstClickTime = self.eventLog.getCurrentTime()
323
324 if not self.buttonDown[correctedJoyId]:
325 self.notes[correctedJoyId] = self.widthToNote(pos)
326
327 velocity = self.heightToVelocity(pos, correctedJoyId)
328
329 if self.notes[correctedJoyId] != None :
330 wiimote.playNote(self.notes[correctedJoyId],velocity)
331 self.congratulate(self.noteRects[self.notes[correctedJoyId]],pos[1])
332 if self.replay :
333 self.clicksIn += 1
334 if self.firstClickInTime == None :
335 self.firstClickInTime = self.eventLog.getCurrentTime()
336 else :
337 self.resetCongratulation()
338
339 self.buttonDown[correctedJoyId] = True
340
341 if event.type == pygame.JOYBUTTONUP:
342 joyName = pygame.joystick.Joystick(event.joy).get_name()
343 correctedJoyId = constants.joyNames.index(joyName)
344 if self.activeWiimotes[correctedJoyId]:
345 wiimote = self.wiimotes[correctedJoyId]
346 wiimote.stopNote(self.notes[correctedJoyId])
347 self.buttonDown[correctedJoyId] = False
348 self.velocityLock[correctedJoyId] = False
349
350 if event.type == pygame.MOUSEMOTION:
351
352 self.updateCursorPositionFromMouse(event)
353
354 correctedJoyId = 0
355 while not self.activeWiimotes[correctedJoyId] :
356 correctedJoyId += 1
357 wiimote = self.wiimotes[correctedJoyId]
358 pos = self.cursorPositions[correctedJoyId]
359
360 if self.buttonDown[correctedJoyId]:
361 wiimote.cursor.flash()
362 if self.notes[correctedJoyId] != None:
363 velocity = self.heightToVelocity(pos, correctedJoyId)
364 CCHexCode = wiimote.getCCHexCode()
365 wiimote.port.write_short(CCHexCode, 07, velocity)
366
367 if event.type == pygame.MOUSEBUTTONDOWN:
368
369 if event.button == 1:
370 correctedJoyId = 0
371 while not self.activeWiimotes[correctedJoyId] :
372 correctedJoyId += 1
373 wiimote = self.wiimotes[correctedJoyId]
374 pos = self.cursorPositions[correctedJoyId]
375 wiimote.cursor.flash()
376 if self.replay :
377 self.clicks += 1
378 if self.firstClickTime == None :
379 self.firstClickTime = self.eventLog.getCurrentTime()
380
381 if not self.buttonDown[correctedJoyId]:
382 self.notes[correctedJoyId] = self.widthToNote(pos)
383
384 velocity = self.heightToVelocity(pos, correctedJoyId)
385
386 if self.notes[correctedJoyId] != None :
387 wiimote.playNote(self.notes[correctedJoyId],velocity)
388 self.congratulate(self.noteRects[self.notes[correctedJoyId]],pos[1])
389 if self.replay :
390 self.clicksIn += 1
391 if self.firstClickInTime == None :
392 self.firstClickInTime = self.eventLog.getCurrentTime()
393 else :
394 self.resetCongratulation()
395
396 self.buttonDown[correctedJoyId] = True
397
398 if event.button == 2:
399
400 self.done = True
401
402 if event.type == pygame.MOUSEBUTTONUP:
403
404 correctedJoyId = 0
405 while not self.activeWiimotes[correctedJoyId] :
406 correctedJoyId += 1
407 wiimote = self.wiimotes[correctedJoyId]
408 wiimote.stopNote(self.notes[correctedJoyId])
409 self.buttonDown[correctedJoyId] = False
410 self.velocityLock[correctedJoyId] = False
411
412 def hasChanged(self):
413 return(True)
414
415 def updateCursorPositionFromMouse(self, mouseEvent):
416 correctedJoyId = 0
417 while not self.activeWiimotes[correctedJoyId] :
418 correctedJoyId += 1
419 self.cursorPositions[correctedJoyId] = mouseEvent.pos
420
421 if __name__ == "__main__" :
422 pygame.init()
423 modeResolution = (1024,768)
424 window = pygame.display.set_mode(modeResolution,pygame.FULLSCREEN)
425 pygame.font.init()
426
427 pygame.midi.init()
428 instruments = [Instrument(constants.scaleDict["majorScale"], i + 1, "".join(["../instruments/instrumentImages/", constants.instrumentImagePathList[i], ".jpg"]), constants.octaves[i]) for i in range(9)]
429
430 joys = [[id,pygame.joystick.Joystick(id).get_name()] for id in range(pygame.joystick.get_count())]
431 for joy in joys:
432 if joy[1] in constants.joyNames:
433 pygame.joystick.Joystick(joy[0]).init()
434
435 ports = [pygame.midi.get_device_info(id)[1] for id in range(pygame.midi.get_count())]
436 portOffset = ports.index(constants.portNames[0])
437 print(portOffset)
438
439 events = pygame.event.get()
440
441 screen = pygame.display.get_surface()
442 clock = pygame.time.Clock()
443 cursorImages = [createImageListFromPath('../cursor/cursorImages/black', 11),createImageListFromPath('../cursor/cursorImages/red', 11)]
444 durations = [75 for i in range(len(cursorImages[0]))]
445
446 wiimoteCount = 1
447 cursors = [WarpingCursor(None, cursorImages[i], durations, (300 * i, 300 * i),flashImage = '../cursor/cursorImages/black/flash.png' ) for i in range(wiimoteCount)]
448 wiimotes = [Wiimote(i, i + portOffset, None, instruments[i], cursors[i]) for i in range(wiimoteCount)]
449
450 fam = StaticFamiliarizer(instruments, wiimotes, window, screen, clock, joys, portOffset)
451
452 for loop in fam.wiimotes:
453 del loop.port
454
455 pygame.midi.quit()
456
457 pygame.quit()