déplacement.
[minwii.git] / src / gui / SongFamiliarizer.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 sys
9 import colorsys
10 import constants
11 from gradients import gradients
12 from logging.PickleableEvent import PickleableEvent
13 from logging.EventLog import EventLog
14
15
16 class SongFamiliarizer:
17 '''
18 The screen on which the game is played
19
20 wiimotes:
21 The wiimotes used in this session
22 window:
23 The main display window
24 screen:
25 The main display surface
26 clock:
27 The clock used to animate the screen
28 savedScreen:
29 The background that is painted every time
30 playerScreen:
31 The buffer for painting everything before bliting
32 width:
33 The width of the window in pixels
34 height:
35 The height of the window in pixels
36 extendScale :
37 True if the scale is G to C instead of C to C
38 cascade:
39 True if crossing from note to note with a button pressed triggers a new note
40 scaleSize:
41 The size of the scale used
42 cursorPositions:
43 The positions of the cursors on the screen, in pixels
44 '''
45
46
47
48 def __init__(self, wiimotes, window, screen, clock, joys, portOffset, song, activeWiimotes, cascade=False, extendedScale=False, easyMode = False, replay = False, eventLog = None, defaultInstrumentChannel = 16, defaultNote = 60):
49 '''
50 Constructor
51 '''
52 self.firstClickTime = None
53 self.firstClickInTime = None
54 self.duration = None
55 self.clicks = 0
56 self.clicksIn = 0
57
58 pygame.font.init()
59 self.font = pygame.font.Font(None,60)
60 self.congratulations = ["Bien !","Tres Bien !","Bravo !","Excellent !","Felicitations !"]
61 self.renderedCongratulations = [self.font.render(congratulation,False,(0,0,0)) for congratulation in self.congratulations]
62 self.congratulationCount = None
63 self.isCongratulating = False
64 self.congratulationTimer = 0
65 self.congratulationLength = 2000
66 self.congratulationPos = None
67
68 self.blinkLength = 200
69 self.minimalVelocity = 90
70 self.shortScaleSize = 8
71 self.longScaleSize = 11
72 if not extendedScale:
73 self.offset = self.longScaleSize - self.shortScaleSize
74 else:
75 self.offset = 0
76 self.borderSize = 5
77 self.highlightedNote = 0
78 self.highlightedNoteNumber = 0
79 self.syllabus = None
80 self.savedHighlightedNote = 0
81 self.scaleFactor = 1
82 self.level = 3
83
84 self.wiimotes = wiimotes
85 self.activeWiimotes = activeWiimotes
86 self.window = window
87 self.screen = screen
88 self.width = int(floor(screen.get_width()*self.scaleFactor))
89 self.height = int(floor(screen.get_height()*self.scaleFactor))
90 self.blitOrigin = ((self.screen.get_width()-self.width)/2,(self.screen.get_height()-self.height)/2)
91 self.joys = joys
92 self.clock = clock
93 self.cursorPositions = []
94 self.savedScreen = pygame.Surface(self.screen.get_size())
95 self.savedScreen.fill((255,255,255))
96 self.playerScreen = pygame.Surface(self.savedScreen.get_size())
97 self.playerScreen.blit(self.savedScreen, (0, 0))
98 self.extendedScale = extendedScale
99 self.cascade = cascade
100 self.portOffset =portOffset
101 self.eventLog = eventLog
102 self.song = song
103 self.songIterator = self.song.getSongIterator()
104 self.midiNoteNumbers = self.song.scale
105 self.replay = replay
106 self.quarterNoteLength = 800
107 self.cascadeLockLengthMultiplier = 1
108 self.cascadeLockLength = self.quarterNoteLength * self.cascadeLockLengthMultiplier
109
110 self.defaultInstrumentChannel = defaultInstrumentChannel
111 self.defaultNote = defaultNote
112
113 self.done = False
114 self.backToInstrumentChoice = False
115 self.easyMode = easyMode
116
117 if eventLog == None:
118 self.eventLog = EventLog()
119 self.replay = False
120 else:
121 self.eventLog = eventLog
122 self.replay = replay
123
124 #Initializes the highlightedNote and highlightedNoteNumber etc...
125 self.moveToNextNote()
126
127 self.blinkOn = False
128 self.savedBlinkOn = False
129 ##Will prevent the song to move on if two consecutive notes are identical and the buttons have not been released in between the two
130 ##i.e. it guarantees that there will be an attack between two identical consecutive notes
131 self.highlightIsFree = True
132
133 self.noteRects = []
134 self.boundingRect = None
135 self.notes = []
136
137 self.buttonDown = []
138 self.velocityLock = []
139
140 self._blinkOffset = 0
141 self._cascadeLockTimer = 0
142 self.cascadeIsFree = True
143
144 self.font = pygame.font.Font(None,50)
145 self.renderedNoteNames = [self.font.render(constants.noteNumberToName(note),False,(0,0,0)) for note in self.midiNoteNumbers]
146
147 self.drawBackground()
148 self.initializeWiimotes()
149
150 events = pygame.event.get()
151
152 #The main loop
153 while not self.done :
154
155 #Clear the cursors from the screen
156 if self.hasChanged():
157 self.drawBackground()
158 self.playerScreen.blit(self.savedScreen, (0, 0))
159
160 # Limit frame speed to 50 FPS
161 #
162 timePassed = self.clock.tick(10000)
163
164 self._blinkOffset += timePassed
165 if self.buttonDown and not self.cascadeIsFree :
166 self._cascadeLockTimer += timePassed
167 if self._cascadeLockTimer > self.cascadeLockLength :
168 self.cascadeIsFree = True
169
170
171 if self._blinkOffset > self.blinkLength:
172 self._blinkOffset -= self.blinkLength
173 self.blinkOn = not self.blinkOn
174
175 if self.replay:
176 self.eventLog.update(timePassed)
177 pickledEventsToPost = self.eventLog.getPickledEvents()
178 for pickledEvent in pickledEventsToPost:
179 pygame.event.post(pickledEvent.event)
180
181 events = pygame.event.get()
182
183 if not self.replay:
184 pickledEvents = [PickleableEvent(event.type,event.dict) for event in events]
185 if pickledEvents != [] :
186 self.eventLog.appendEventGroup(pickledEvents)
187
188 for event in events:
189 self.input(event)
190
191 if self.isCongratulating :
192 self.congratulationTimer += timePassed
193 if self.congratulationTimer < self.congratulationLength :
194 self.blitCongratulation()
195 else :
196 self.isCongratulating = False
197
198 for i in range(len(self.wiimotes)):
199 if self.activeWiimotes[i]:
200 self.wiimotes[i].cursor.update(timePassed, self.cursorPositions[i])
201 if self.buttonDown[i] :
202 self.wiimotes[i].cursor.flash()
203 self.wiimotes[i].cursor.blit(self.playerScreen)
204
205 self.screen.blit(self.playerScreen, (0,0))
206
207 pygame.display.flip()
208
209 for i in range(len(self.wiimotes)):
210 if self.activeWiimotes[i]:
211 self.wiimotes[i].stopNoteByNoteNumber(self.midiNoteNumbers[self.notes[i]])
212 if self.replay :
213 self.duration = self.eventLog.getCurrentTime()
214
215 def drawBackground(self):
216 self.savedScreen.fill((255,255,255))
217
218 if self.extendedScale :
219 self.scaleSize = self.longScaleSize
220 else:
221 self.scaleSize = self.shortScaleSize
222
223 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)]
224 #inflate last noteRect to cover the far right pixels
225 self.noteRects[-1].width = self.noteRects[-1].width + 1
226
227 self.noteRects[self.highlightedNote-self.offset].inflate_ip(self.noteRects[self.highlightedNote-self.offset].width*2,0)
228
229 #create bounding rect
230 self.boundingRect = self.noteRects[0].unionall(self.noteRects)
231
232 self.renderedNoteNames = [self.font.render(constants.noteNumberToName(note),False,(0,0,0)) for note in self.midiNoteNumbers]
233
234 #fill the rectangles with a color gradient
235 #We start with blue
236 startingHue = 0.66666666666666663
237
238 # for rectNumber in range(self.scaleSize):
239 # colorRatio = float(rectNumber) / (self.scaleSize - 1)
240 # #hue will go from 0.6666... (blue) to 0 (red) as colorRation goes up
241 # hue = startingHue * (1 - colorRatio)
242 # if rectNumber + self.offset != self.highlightedNote:
243 # #The color of the bottom of the rectangle in hls coordinates
244 # bottomColorHls = (hue, 0.1, 1)
245 # #The color of the top of the rectangle in hls coordinates
246 # topColorHls = (hue, 0.1, 1)
247 #
248 # #convert to rgb ranging from 0 to 255
249 # bottomColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*bottomColorHls)]
250 # topColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*topColorHls)]
251 # #add transparency
252 # bottomColorRgb.append(255)
253 # topColorRgb.append(255)
254 # #convert to tuple
255 # bottomColorRgb = tuple(bottomColorRgb)
256 # topColorRgb = tuple(topColorRgb)
257 #
258 # self.savedScreen.blit(gradients.vertical(self.noteRects[rectNumber].size, topColorRgb, bottomColorRgb), self.noteRects[rectNumber])
259 #
260 # noteNameBlitPoint = (self.noteRects[rectNumber].left+(self.noteRects[rectNumber].width-self.renderedNoteNames[rectNumber].get_width())/2,
261 # self.noteRects[rectNumber].bottom-self.renderedNoteNames[rectNumber].get_height())
262 #
263 # self.savedScreen.blit(self.renderedNoteNames[rectNumber], noteNameBlitPoint)
264 #
265 # pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 255), self.noteRects[rectNumber], 2)
266
267 colorRatio = float(self.highlightedNote-self.offset) / (self.scaleSize - 1)
268 #hue will go from 0.6666... (blue) to 0 (red) as colorRation goes up
269 hue = startingHue * (1 - colorRatio)
270 #The color of the bottom of the rectangle in hls coordinates
271 bottomColorHls = (hue, 0.6, 1)
272 #The color of the top of the rectangle in hls coordinates
273 topColorHls = (hue, 0.9, 1)
274
275 #convert to rgb ranging from 0 to 255
276 bottomColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*bottomColorHls)]
277 topColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*topColorHls)]
278 #add transparency
279 bottomColorRgb.append(255)
280 topColorRgb.append(255)
281 #convert to tuple
282 bottomColorRgb = tuple(bottomColorRgb)
283 topColorRgb = tuple(topColorRgb)
284
285 self.savedScreen.blit(gradients.vertical(self.noteRects[self.highlightedNote-self.offset].size, topColorRgb, bottomColorRgb), self.noteRects[self.highlightedNote-self.offset])
286
287 # noteNameBlitPoint = (self.noteRects[self.highlightedNote-self.offset].left+(self.noteRects[self.highlightedNote-self.offset].width-self.renderedNoteNames[self.highlightedNote-self.offset].get_width())/2,
288 # self.noteRects[self.highlightedNote-self.offset].bottom-self.renderedNoteNames[self.highlightedNote-self.offset].get_height())
289 #
290 # self.savedScreen.blit(self.renderedNoteNames[self.highlightedNote-self.offset], noteNameBlitPoint)
291 #
292 # if self.syllabus :
293 # renderedSyllabus = self.font.render(self.syllabus,False,(0,0,0))
294 #
295 # syllabusBlitPoint = (self.noteRects[self.highlightedNote-self.offset].left+(self.noteRects[self.highlightedNote-self.offset].width-renderedSyllabus.get_width())/2,
296 # self.noteRects[self.highlightedNote-self.offset].centery-renderedSyllabus.get_height()/2)
297 #
298 # self.savedScreen.blit(renderedSyllabus, syllabusBlitPoint)
299
300 pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 255), self.noteRects[self.highlightedNote-self.offset], 2)
301
302 # if self.song != None and self.blinkOn:
303 # borderSize = self.borderSize
304 # pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 0), self.noteRects[self.highlightedNote-self.offset].inflate(borderSize/2,borderSize/2), borderSize)
305
306 def initializeWiimotes(self):
307 for loop in self.wiimotes:
308 if loop.port == None :
309 loop.port = pygame.midi.Output(loop.portNumber)
310 self.notes.append(0)
311 self.cursorPositions.append(loop.cursor.centerPosition)
312 self.buttonDown.append(False)
313 self.velocityLock.append(False)
314
315 def updateCursorPositionFromJoy(self, joyEvent):
316 joyName = pygame.joystick.Joystick(joyEvent.joy).get_name()
317 correctedJoyId = constants.joyNames.index(joyName)
318 if correctedJoyId < len(self.cursorPositions):
319 if joyEvent.axis == 0 :
320 self.cursorPositions[correctedJoyId] = (int((joyEvent.value + 1) / 2 * self.screen.get_width()), self.cursorPositions[correctedJoyId][1])
321 if joyEvent.axis == 1 :
322 self.cursorPositions[correctedJoyId] = (self.cursorPositions[correctedJoyId][0], int((joyEvent.value + 1) / 2 * self.screen.get_height()))
323
324 def heightToVelocity(self, pos, controllerNumber):
325 if self.song != None:
326 if self.boundingRect.collidepoint(pos) and (self.highlightedNote == self.notes[controllerNumber] or self.velocityLock[controllerNumber]):
327 velocity = int(floor((1 - (float(pos[1])-self.blitOrigin[1]) / self.height) * (127-self.minimalVelocity))+self.minimalVelocity)
328 else :
329 if self.easyMode:
330 velocity = None
331 else:
332 velocity = 60
333 else:
334 if self.boundingRect.collidepoint(pos):
335 velocity = int(floor((1 - (float(pos[1])-self.blitOrigin[1]) / self.height) * (127-self.minimalVelocity))+self.minimalVelocity)
336 else :
337 velocity = self.minimalVelocity
338 return(velocity)
339
340 def widthToNote(self, pos):
341 nn = 0
342 try :
343 if self.noteRects[self.highlightedNote-self.offset].collidepoint(pos) :
344 return self.highlightedNote
345 else :
346 while self.noteRects[nn].collidepoint(pos) == False:
347 nn = nn + 1
348 return(nn + self.offset)
349 except(IndexError):
350 return(None)
351
352 def congratulate(self,targetRect,posy):
353 if self.congratulationCount != None :
354 if self.congratulationCount < len(self.congratulations)-1:
355 self.congratulationCount += 1
356 else :
357 self.congratulationCount = 0
358 self.congratulationTimer = 0
359 self.congratulationPos = (targetRect.left+(targetRect.width-self.renderedCongratulations[self.congratulationCount].get_width())/2,posy)
360 self.isCongratulating = True
361
362 def resetCongratulation(self):
363 self.congratulationCount = None
364 self.congratulationPos = None
365 self.isCongratulating = False
366
367 def blitCongratulation(self):
368 self.playerScreen.blit(self.renderedCongratulations[self.congratulationCount],self.congratulationPos)
369
370 def input(self, event):
371
372 if event.type == pygame.QUIT:
373 for loop in self.wiimotes:
374 del loop.port
375 pygame.midi.quit()
376 sys.exit(0)
377
378 if event.type == pygame.KEYDOWN:
379 if event.key == pygame.K_q:
380 self.nextLevel = None
381 self.done = True
382
383 if event.key == pygame.K_w:
384 self.nextLevel = 0
385 self.done = True
386
387 if event.key == pygame.K_e:
388 self.nextLevel = 1
389 self.done = True
390
391 if event.key == pygame.K_r:
392 self.nextLevel = 2
393 self.done = True
394
395 if event.key == pygame.K_t:
396 self.nextLevel = 3
397 self.done = True
398
399 if event.type == pygame.JOYAXISMOTION:
400
401 joyName = pygame.joystick.Joystick(event.joy).get_name()
402 correctedJoyId = constants.joyNames.index(joyName)
403 if self.activeWiimotes[correctedJoyId]:
404 self.updateCursorPositionFromJoy(event)
405 wiimote = self.wiimotes[correctedJoyId]
406 pos = self.cursorPositions[correctedJoyId]
407
408 if self.buttonDown[correctedJoyId]:
409 if self.notes[correctedJoyId] != None:
410 velocity = self.heightToVelocity(pos, correctedJoyId)
411 if velocity != None :
412 CCHexCode = wiimote.getCCHexCode()
413 wiimote.port.write_short(CCHexCode, 07, velocity)
414 if self.cascade and self.cascadeIsFree :
415 n = self.widthToNote(pos)
416 if self.highlightedNote == n:
417 wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])
418 self.notes[correctedJoyId] = n
419 velocity = self.heightToVelocity(pos, correctedJoyId)
420 self.velocityLock[correctedJoyId] = True
421 wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity)
422 self.moveToNextNote()
423 self._cascadeLockTimer = 0
424 self.cascadeIsFree = False
425
426 if event.type == pygame.JOYBUTTONDOWN :
427
428 joyName = pygame.joystick.Joystick(event.joy).get_name()
429 correctedJoyId = constants.joyNames.index(joyName)
430 if self.activeWiimotes[correctedJoyId]:
431 wiimote = self.wiimotes[correctedJoyId]
432 pos = self.cursorPositions[correctedJoyId]
433 self.wiimotes[correctedJoyId].cursor.flash()
434 if self.replay:
435 self.clicks += 1
436 if self.firstClickTime == None :
437 self.firstClickTime = self.eventLog.getCurrentTime()
438
439 if not self.buttonDown[correctedJoyId]:
440 n = self.widthToNote(pos)
441 if self.highlightedNote == n:
442 self._cascadeLockTimer = 0
443 self.cascadeIsFree = False
444 if self.easyMode:
445 wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])
446 self.notes[correctedJoyId] = n
447 velocity = self.heightToVelocity(pos, correctedJoyId)
448 self.velocityLock[correctedJoyId] = True
449 wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity)
450 self.congratulate(self.noteRects[self.notes[correctedJoyId]],pos[1])
451 if self.replay:
452 self.clicksIn += 1
453 if self.firstClickInTime == None :
454 self.firstClickInTime = self.eventLog.getCurrentTime()
455
456 self.moveToNextNote()
457 else :
458 self.resetCongratulation()
459 if not self.easyMode :
460 self._cascadeLockTimer = 0
461 self.cascadeIsFree = False
462 self.notes[correctedJoyId] = n
463 velocity = self.heightToVelocity(pos, correctedJoyId)
464 if velocity != None :
465 wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity)
466 self.buttonDown[correctedJoyId] = True
467
468 if event.type == pygame.JOYBUTTONUP:
469 joyName = pygame.joystick.Joystick(event.joy).get_name()
470 correctedJoyId = constants.joyNames.index(joyName)
471 if self.activeWiimotes[correctedJoyId]:
472 self.buttonDown[correctedJoyId] = False
473 wiimote = self.wiimotes[correctedJoyId]
474 if not self.easyMode:
475 wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])
476 self.velocityLock[correctedJoyId] = False
477
478 if event.type == pygame.MOUSEMOTION:
479
480 self.updateCursorPositionFromMouse(event)
481
482 correctedJoyId = 0
483 while not self.activeWiimotes[correctedJoyId] :
484 correctedJoyId += 1
485 wiimote = self.wiimotes[correctedJoyId]
486 pos = self.cursorPositions[correctedJoyId]
487
488 if self.buttonDown[correctedJoyId]:
489 self.wiimotes[correctedJoyId].cursor.flash()
490 if self.notes[correctedJoyId] != None:
491 velocity = self.heightToVelocity(pos, correctedJoyId)
492 if velocity != None :
493 CCHexCode = wiimote.getCCHexCode()
494 wiimote.port.write_short(CCHexCode, 07, velocity)
495 if self.cascade and self.cascadeIsFree :
496 n = self.widthToNote(pos)
497 if self.highlightedNote == n:
498 wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])
499 self.notes[correctedJoyId] = n
500 velocity = self.heightToVelocity(pos, correctedJoyId)
501 self.velocityLock[correctedJoyId] = True
502 wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity)
503 self.moveToNextNote()
504 self._cascadeLockTimer = 0
505 self.cascadeIsFree = False
506
507 if event.type == pygame.MOUSEBUTTONDOWN:
508
509 if event.button == 1:
510 correctedJoyId = 0
511 while not self.activeWiimotes[correctedJoyId] :
512 correctedJoyId += 1
513 wiimote = self.wiimotes[correctedJoyId]
514 pos = self.cursorPositions[correctedJoyId]
515 self.wiimotes[correctedJoyId].cursor.flash()
516 if self.replay:
517 self.clicks += 1
518 if self.firstClickTime == None :
519 self.firstClickTime = self.eventLog.getCurrentTime()
520
521 if not self.buttonDown[correctedJoyId]:
522 n = self.widthToNote(pos)
523 if self.highlightedNote == n:
524 self._cascadeLockTimer = 0
525 self.cascadeIsFree = False
526 if self.easyMode:
527 wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])
528 self.notes[correctedJoyId] = n
529 velocity = self.heightToVelocity(pos, correctedJoyId)
530 self.velocityLock[correctedJoyId] = True
531 wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity)
532 self.congratulate(self.noteRects[self.notes[correctedJoyId]],pos[1])
533 if self.replay:
534 self.clicksIn += 1
535 if self.firstClickInTime == None :
536 self.firstClickInTime = self.eventLog.getCurrentTime()
537
538 self.moveToNextNote()
539 else :
540 self.resetCongratulation()
541 if not self.easyMode :
542 self._cascadeLockTimer = 0
543 self.cascadeIsFree = False
544 self.notes[correctedJoyId] = n
545 velocity = self.heightToVelocity(pos, correctedJoyId)
546 if velocity != None :
547 wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity)
548 self.buttonDown[correctedJoyId] = True
549
550 if event.button == 2:
551
552 self.done = True
553
554 if event.type == pygame.MOUSEBUTTONUP:
555 if event.button == 1 :
556 correctedJoyId = 0
557 while not self.activeWiimotes[correctedJoyId] :
558 correctedJoyId += 1
559 wiimote = self.wiimotes[correctedJoyId]
560 self.buttonDown[correctedJoyId] = False
561 if not self.easyMode:
562 wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])
563 self.velocityLock[correctedJoyId] = False
564
565 def hasChanged(self):
566 changed = False
567 if self.song != None:
568 if self.blinkOn != self.savedBlinkOn or self.highlightedNote != self.savedHighlightedNote:
569 self.savedBlinkOn = self.blinkOn
570 self.savedHighlightedNote = self.highlightedNote
571 changed = True
572 return(changed)
573
574 def updateCursorPositionFromMouse(self, mouseEvent):
575 correctedJoyId = 0
576 while not self.activeWiimotes[correctedJoyId] :
577 correctedJoyId += 1
578 self.cursorPositions[correctedJoyId] = mouseEvent.pos
579
580 def moveToNextNote(self):
581 self.savedMidiNoteNumbers = self.midiNoteNumbers[:]
582 self.highlightedNote, self.highlightedNoteNumber, self.syllabus, self.cascadeLockLengthMultiplier = self.songIterator.next()
583 self.midiNoteNumbers[self.highlightedNote] = self.highlightedNoteNumber