1 # -*- coding: utf-8 -*-
4 bandes arc-en-ciel représentant un clavier.
10 from cursors
import WarpingCursor
11 from column
import Column
13 from eventutils
import event_handler
, EventDispatcher
, EventHandlerMixin
15 from musicxml
import Tone
17 from config
import FRAMERATE
18 from config
import BORDER
19 from config
import FIRST_HUE
20 from config
import DEFAULT_MIDI_VELOCITY
22 from globals import BACKGROUND_LAYER
23 from globals import CURSOR_LAYER
24 from globals import PLAYING_MODES_DICT
26 class _PlayingScreenBase(pygame
.sprite
.LayeredDirty
, EventHandlerMixin
) :
28 def __init__(self
, synth
, distinctNotes
=[]) :
30 distinctNotes : notes disctinctes présentes dans la chanson
31 triées du plus grave au plus aigu.
33 super(_PlayingScreenBase
, self
).__init
__()
35 self
.distinctNotes
= distinctNotes
36 self
.keyboardLength
= 0
37 self
.keyboardRects
= []
43 self
.draw(pygame
.display
.get_surface())
46 def _initRects(self
) :
47 """ création des espaces réservés pour
48 afficher les colonnes.
50 #ambitus = self.distinctNotes[-1].midi - self.distinctNotes[0].midi
52 # self.keyboardLength = 8
54 # self.keyboardLength = 11
55 self
.keyboardLength
= len(self
.distinctNotes
)
57 screen
= pygame
.display
.get_surface()
59 # taille de la zone d'affichage utile (bordure autour)
60 dispWidth
= screen
.get_width() - 2 * BORDER
61 dispHeight
= screen
.get_height() - 2 * BORDER
63 columnWidth
= int(round(float(dispWidth
) / self
.keyboardLength
))
66 for i
in range(self
.keyboardLength
) :
67 upperLeftCorner
= (i
*columnWidth
+ BORDER
, BORDER
)
68 rect
= pygame
.Rect(upperLeftCorner
, (columnWidth
, dispHeight
))
71 self
.keyboardRects
= rects
73 def _initColumns(self
) :
75 hueStep
= FIRST_HUE
/ (self
.keyboardLength
- 1)
76 for i
, rect
in enumerate(self
.keyboardRects
) :
77 hue
= FIRST_HUE
- hueStep
* i
78 tone
= self
.distinctNotes
[i
]
82 elif i
== self
.keyboardLength
-1 :
84 c
= Column(self
, hue
, rect
, tone
, atBorder
)
85 self
.add(c
, layer
=BACKGROUND_LAYER
)
86 self
.columns
[tone
.midi
] = c
89 def _initCursor(self
) :
90 self
.cursor
= WarpingCursor(blinkMode
=True)
91 self
.add(self
.cursor
, layer
=CURSOR_LAYER
)
95 clock
= pygame
.time
.Clock()
97 pygame
.mouse
.set_visible(False)
99 EventDispatcher
.dispatchEvents()
100 dirty
= self
.draw(pygame
.display
.get_surface())
101 pygame
.display
.update(dirty
)
102 clock
.tick(FRAMERATE
)
105 self
._running
= False
106 self
.synth
.system_reset()
107 pygame
.mouse
.set_visible(True)
108 self
.cursor
._stopBlink
()
110 @event_handler(pygame
.KEYDOWN
)
111 def handleKeyDown(self
, event
) :
112 if event
.key
== pygame
.K_q
:
115 @event_handler(pygame
.MOUSEBUTTONDOWN
)
116 def onMouseDown(self
, event
) :
117 # TODO à cleaner : on vire le dernier élément
118 # parce qu'il s'agit du curseur
119 for col
in reversed(self
.sprites()[:-1]) :
120 if col
.rect
.collidepoint(*event
.pos
):
121 self
.raiseColDown(col
)
124 @event_handler(pygame
.MOUSEBUTTONUP
)
125 def onMouseUp(self
, event
) :
126 for col
in reversed(self
.sprites()[:-1]) :
127 if col
.rect
.collidepoint(*event
.pos
) :
131 @event_handler(pygame
.MOUSEMOTION
)
132 def onMouseMove(self
, event
) :
133 for col
in reversed(self
.sprites()[:-1]) :
134 if col
.rect
.collidepoint(*event
.pos
) :
135 self
.raiseColOver(col
)
138 def raiseColDown(self
, col
) :
139 evt
= pygame
.event
.Event(events
.COLDOWN
, column
=col
)
140 pygame
.event
.post(evt
)
142 def raiseColUp(self
, col
) :
143 evt
= pygame
.event
.Event(events
.COLUP
, column
=col
)
144 pygame
.event
.post(evt
)
146 def raiseColOver(self
, col
) :
147 evt
= pygame
.event
.Event(events
.COLOVER
, column
=col
)
148 pygame
.event
.post(evt
)
151 class PlayingScreen(_PlayingScreenBase
) :
152 "fenêtre de jeu pour improvisation"
154 scale
= [55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72]
156 def __init__(self
, synth
) :
158 for midi
in self
.scale
:
160 distinctNotes
.append(tone
)
162 super(PlayingScreen
, self
).__init
__(synth
, distinctNotes
)
164 @event_handler(events
.NOTEON
)
165 def noteon(self
, evt
) :
167 self
.synth
.noteon(0, tone
.midi
, DEFAULT_MIDI_VELOCITY
)
169 @event_handler(events
.NOTEOFF
)
170 def noteoff(self
, evt
) :
172 self
.synth
.noteoff(0, tone
.midi
)
175 class SongPlayingScreen(_PlayingScreenBase
) :
177 def __init__(self
, synth
, song
, mode
=PLAYING_MODES_DICT
['EASY']) :
178 super(SongPlayingScreen
, self
).__init
__(synth
, song
.distinctNotes
)
180 self
.quarterNoteDuration
= song
.quarterNoteDuration
181 self
.currentColumn
= None
182 self
.noteIterator
= self
.song
.iterNotes()
184 if mode
== PLAYING_MODES_DICT
['NORMAL'] :
185 EventDispatcher
.addEventListener(events
.COLDOWN
, self
.handleColumnDown
)
186 EventDispatcher
.addEventListener(events
.COLUP
, self
.handleColumnUp
)
187 elif mode
== PLAYING_MODES_DICT
['EASY'] :
188 EventDispatcher
.addEventListener(events
.COLOVER
, self
.handleColumnOver
)
191 def displayNext(self
, event
=None) :
192 if self
.currentColumn
:
193 self
.currentColumn
.update(False)
194 note
, verseIndex
= self
.noteIterator
.next()
195 syllabus
= note
.lyrics
[verseIndex
].syllabus()
196 column
= self
.columns
[note
.midi
]
197 column
.update(True, syllabus
)
198 self
.currentColumn
= column
199 self
.currentNote
= note
200 self
.currentNotePlayed
= False
202 def handleColumnDown(self
, event
) :
205 self
.synth
.noteon(0, col
.tone
.midi
, DEFAULT_MIDI_VELOCITY
)
206 self
.currentNotePlayed
= True
208 def handleColumnUp(self
, event
) :
209 if self
.currentNotePlayed
:
210 self
.synth
.noteoff(0, self
.currentColumn
.tone
.midi
)
213 def handleColumnOver(self
, event
) :
215 if col
.state
and not self
.currentNotePlayed
:
216 self
.synth
.noteon(0, col
.tone
.midi
, DEFAULT_MIDI_VELOCITY
)
217 SongPlayingScreen
.setNoteTimeout(
218 int(self
.currentNote
.duration
* \
219 self
.quarterNoteDuration
)
221 self
.currentNotePlayed
= True
223 @event_handler(events
.NOTEEND
)
224 def clearTimeOutAndDisplayNext(self
, evt
) :
225 pygame
.time
.set_timer(evt
.type, 0)
226 self
.synth
.noteoff(0, self
.currentNote
.midi
)
230 def setNoteTimeout(delay
) :
231 pygame
.time
.set_timer(events
.NOTEEND
, delay
)
234 pygame
.time
.set_timer(events
.NOTEEND
, 0)
235 super(SongPlayingScreen
, self
).stop()