1 # -*- coding: utf-8 -*-
4 bandes arc-en-ciel représentant un clavier.
10 from colorsys
import hls_to_rgb
11 from gradients
import gradients
12 from cursors
import WarpingCursor
14 from eventutils
import event_handler
, EventDispatcher
, EventHandlerMixin
15 from math
import floor
17 from musicxml
import Tone
20 from config
import FRAMERATE
21 from config
import BORDER
22 from config
import FIRST_HUE
23 from config
import OFF_LUMINANCE
24 from config
import OFF_SATURATION
25 from config
import ON_TOP_LUMINANCE
26 from config
import ON_BOTTOM_LUMINANCE
27 from config
import ON_SATURATION
28 from config
import ON_COLUMN_OVERSIZING
29 from config
import ON_COLUMN_ALPHA
30 from config
import FONT
31 from config
import FONT_COLOR
37 class _PlayingScreenBase(pygame
.sprite
.LayeredDirty
, EventHandlerMixin
) :
39 def __init__(self
, synth
, distinctNotes
=[]) :
41 distinctNotes : notes disctinctes présentes dans la chanson
42 triées du plus grave au plus aigu.
44 super(_PlayingScreenBase
, self
).__init
__()
46 self
.distinctNotes
= distinctNotes
47 self
.keyboardLength
= 0
48 self
.keyboardRects
= []
53 self
.draw(pygame
.display
.get_surface())
57 def _initRects(self
) :
58 """ création des espaces réservés pour
59 afficher les colonnes.
61 #ambitus = self.distinctNotes[-1].midi - self.distinctNotes[0].midi
63 # self.keyboardLength = 8
65 # self.keyboardLength = 11
66 self
.keyboardLength
= len(self
.distinctNotes
)
68 screen
= pygame
.display
.get_surface()
70 # taille de la zone d'affichage utile (bordure autour)
71 dispWidth
= screen
.get_width() - 2 * BORDER
72 dispHeight
= screen
.get_height() - 2 * BORDER
74 columnWidth
= int(round(float(dispWidth
) / self
.keyboardLength
))
77 for i
in range(self
.keyboardLength
) :
78 upperLeftCorner
= (i
*columnWidth
+ BORDER
, BORDER
)
79 rect
= pygame
.Rect(upperLeftCorner
, (columnWidth
, dispHeight
))
82 self
.keyboardRects
= rects
84 def _initColumns(self
) :
86 hueStep
= FIRST_HUE
/ (self
.keyboardLength
- 1)
87 for i
, rect
in enumerate(self
.keyboardRects
) :
88 hue
= FIRST_HUE
- hueStep
* i
89 tone
= self
.distinctNotes
[i
]
90 c
= Column(self
, hue
, rect
, tone
)
91 self
.add(c
, layer
=BACKGROUND_LAYER
)
93 def _initCursor(self
) :
94 self
.cursor
= WarpingCursor(blinkMode
=True)
95 self
.add(self
.cursor
, layer
=CURSOR_LAYER
)
99 clock
= pygame
.time
.Clock()
100 pygame
.display
.flip()
101 pygame
.mouse
.set_visible(False)
102 while self
._running
:
103 EventDispatcher
.dispatchEvents()
104 dirty
= self
.draw(pygame
.display
.get_surface())
105 pygame
.display
.update(dirty
)
106 clock
.tick(FRAMERATE
)
108 pygame
.mouse
.set_visible(True)
109 self
.cursor
._stopBlink
()
111 @event_handler(pygame
.KEYDOWN
)
112 def handleKeyDown(self
, event
) :
113 if event
.key
== pygame
.K_q
:
114 self
._running
= False
117 class PlayingScreen(_PlayingScreenBase
) :
118 "fenêtre de jeu pour improvisation"
119 scale
= [55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72]
121 def __init__(self
, synth
) :
123 for midi
in self
.scale
:
125 distinctNotes
.append(tone
)
127 super(PlayingScreen
, self
).__init
__(synth
, distinctNotes
)
129 @event_handler(events
.NOTEON
)
130 def noteon(self
, evt
) :
132 self
.synth
.noteon(0, tone
.midi
, 64)
134 @event_handler(events
.NOTEOFF
)
135 def noteoff(self
, evt
) :
137 self
.synth
.noteoff(0, tone
.midi
)
140 class SongPlayingScreen(_PlayingScreenBase
) :
142 def __init__(self
, synth
, song
) :
143 super(SongPlayingScreen
, self
).__init
__(synth
, song
.distinctNotes
)
146 @event_handler(events
.NOTEON
)
147 def noteon(self
, evt
) :
149 self
.synth
.noteon(0, tone
.midi
, 64)
151 @event_handler(events
.NOTEOFF
)
152 def noteoff(self
, evt
) :
154 self
.synth
.noteoff(0, tone
.midi
)
158 class Column(pygame
.sprite
.DirtySprite
, EventHandlerMixin
) :
160 def __init__(self
, group
, hue
, rect
, tone
) :
161 pygame
.sprite
.DirtySprite
.__init
__(self
, group
)
163 toneName
= FONT
.render(tone
.nom
, True, (0,0,0))
164 sur
= pygame
.surface
.Surface(rect
.size
)
165 rgba
= hls_to_rgba_8bits(hue
, OFF_LUMINANCE
, OFF_SATURATION
)
167 w
, h
= rect
.w
, rect
.h
168 tw
, th
, = toneName
.get_size()
169 toneRect
= pygame
.Rect(((w
- tw
) / 2, h
- th
), (tw
, th
))
170 sur
.blit(toneName
, toneRect
)
174 topRgba
= hls_to_rgba_8bits(hue
, ON_TOP_LUMINANCE
, ON_SATURATION
, ON_COLUMN_ALPHA
)
175 bottomRgba
= hls_to_rgba_8bits(hue
, ON_BOTTOM_LUMINANCE
, ON_SATURATION
, ON_COLUMN_ALPHA
)
176 onWidth
= rect
.width
* ON_COLUMN_OVERSIZING
177 onLeft
= rect
.centerx
- onWidth
/ 2
178 rectOn
= pygame
.Rect((onLeft
, 0),
179 (onWidth
, rect
.height
))
180 self
.stateOn
= gradients
.vertical(rectOn
.size
, topRgba
, bottomRgba
)
181 w
, h
= rectOn
.w
, rectOn
.h
182 toneRect
= pygame
.Rect(((w
- tw
) / 2, h
- th
), (tw
, th
))
183 self
.stateOn
.blit(toneName
, toneRect
)
186 self
.image
= self
.stateOff
189 def update(self
, state
) :
190 group
= self
.groups()[0]
192 group
.change_layer(self
, FOREGROUND_LAYER
)
193 self
.image
= self
.stateOn
194 self
.rect
= self
.rectOn
196 group
.change_layer(self
, BACKGROUND_LAYER
)
197 self
.image
= self
.stateOff
198 self
.rect
= self
.rectOff
200 @event_handler(pygame
.MOUSEBUTTONDOWN
)
201 def onMouseDown(self
, event
) :
202 if self
.rect
.collidepoint(*event
.pos
) :
206 @event_handler(pygame
.MOUSEBUTTONUP
)
207 def onMouseUp(self
, event
) :
211 def raiseNoteOn(self
) :
212 evt
= pygame
.event
.Event(events
.NOTEON
, tone
=self
.tone
)
213 pygame
.event
.post(evt
)
215 def raiseNoteOff(self
) :
216 evt
= pygame
.event
.Event(events
.NOTEOFF
, tone
=self
.tone
)
217 pygame
.event
.post(evt
)
221 def hls_to_rgba_8bits(h
, l
, s
, a
=1) :
222 #convert to rgb ranging from 0 to 255
223 rgba
= [floor(255 * i
) for i
in hls_to_rgb(h
, l
, s
) + (a
,)]