-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpiano.py
383 lines (299 loc) · 10.8 KB
/
piano.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
import pyglet # pyglet library, the graphical library to use, supporting audio
from os.path import join # Dealing with paths
batch = pyglet.graphics.Batch()
################################################################################
##################### DATA STRUCTURES ##########################################
################################################################################
# ------------------------------------------------------------------------------#
# Strings #
# ------------------------------------------------------------------------------#
SOUND_DIR = "sounds" # Directory of sound resources
ToPlay = "" # Music code to play
Played = "" # Music code already played
mode = "pause"
symbols = " !@#$%^&*()-_+=\"\:;\',./\\[]{}1234567890"
Editor = ""
Info = ""
# ~~~~~~~~~~~~~~~ Songs ~~~~~~~~~~~~~~~~~~
Dragon = \
"F-GHI-JIH-HGF F-GHI-JIH-HIJ F-GHI-JIH-HGF G-G-G-HGF-FEF \
J-J-J-IHI-IJI H-H-H-IHG-GHG J-J-J-IHI-IJI H-H-G-HGF-FEF"
Tomorrow = \
"JJL-L-LL-LMLK- JKL-L-JIH-I--- JIHH-HHOO-NN-ONML-MLK-KLMLL---- O-O---MLL-ML----\
M-M-LH-IJ----- I-H--HHJ-JJII-I--II-I-NM- \
MMMNO-MLNN-OP-N- ONMLLHIJJ-- J-N-NN-JP-P-O-NMM----\
MMMNON-MNN-OP-NNQ-----POO----- NM-M-MM--L-L-IJ--H"
# ------------------------------------------------------------------------------#
# Numerical States #
# ------------------------------------------------------------------------------#
winw = 800
winh = 500
texth = 40
texts = 20
interval = 0.12
maxPlayedSize = 30
maxAppendSize = 30
# ------------------------------------------------------------------------------#
# Pyglet Objects #
# ------------------------------------------------------------------------------#
window = pyglet.window.Window() # Create window
window.set_size(winw, winh) # Set window size
# ------------------------------------------------------------------------------#
# Arrays #
# ------------------------------------------------------------------------------#
# Alphabeta: from "A" to "Z"
alphabeta = [chr(i) for i in range(ord("A"), ord("Z")+1)]
symbolnum = [ord(c) for c in symbols]
# ------------------------------------------------------------------------------#
# Dictionaries #
# ------------------------------------------------------------------------------#
sounds = {} # The map from alphabeta to the corresponding media object
################################################################################
################# FUNCTIONS ####################################################
################################################################################
# ------------------------------------------------------------------------------#
# File Processing #
# ------------------------------------------------------------------------------#
# Open file s.mp3 and store the media object into the dictionary
def getSound(s):
# Open audio files
m = pyglet.media.load(join(SOUND_DIR, "%s.mp3" % s), streaming=False)
# Store m into dictionary sounds, under key s
sounds[s] = m
# ------------------------------------------------------------------------------#
# Command Operation #
# ------------------------------------------------------------------------------#
def startPlaying():
global mode
mode = "playing"
pyglet.clock.unschedule(autoPlay) # In case already playing
pyglet.clock.schedule_interval(autoPlay, interval) # Start playing
def pause():
global mode
mode = "pause"
pyglet.clock.unschedule(autoPlay) # Stop playing
# Entering append mode
def appendMode():
global mode
mode = "append"
# Entering edit mode
def editMode():
global mode
mode = "edit"
# Entering file open mode
def fileMode():
global mode
mode = "file"
# Entering play append mode
def playAppend():
global mode
mode = "play append"
# Entering play append mode
def playEdit():
global mode
mode = "play edit"
# Reset
def reset():
global mode, ToPlay, Played, Editor
mode = "pause"
ToPlay = ""
Played = ""
Editor = ""
# Enter practise mode
def practise():
global mode
mode = "practise"
def command(c):
if c == "c": # Ctrl-C
pause()
elif c == "p": # Ctrl-P
startPlaying()
elif c == "a": # Ctrl-A
appendMode()
elif c == "e": # Ctrl-E
editMode()
elif c == "f": # Ctrl-F
fileMode()
elif c == "r": # Ctrl-R
reset()
elif c == "l": # Ctrl-L
practise()
elif c == "A": # Shift-A
playAppend()
elif c == "E": # Shift-A
playEdit()
# ------------------------------------------------------------------------------#
# Sound Manipulation #
# ------------------------------------------------------------------------------#
# Play the sound given single char c
def playC(c):
global Played
Played += c
# Play the char and log it in the string Played
if c.isupper():
sounds[c].play()
elif c.islower():
sounds[c.upper()].play()
# Play function invoked by schedule
def autoPlay(dt):
global ToPlay
# If the music to play is empty, stop
if ToPlay == "":
return
# Take the first character out and play it
c = ToPlay[0]
playC(c)
ToPlay = ToPlay[1:]
# Append music to the ToPlay string
def appendPlay(music):
global ToPlay
ToPlay += music
# Play an encoding of a piece of music
def play(music):
global ToPlay
ToPlay = music
startPlaying()
# ------------------------------------------------------------------------------#
# Functions invoked by callback functions #
# ------------------------------------------------------------------------------#
# Function invoked when an alphabeta key is pressed
def alphaKeyPress(c):
global Editor, ToPlay
if mode == "playing" or mode == "pause":
if c.isupper():
command(c)
elif c.islower():
playC(c.upper())
elif mode == "append" or mode == "edit" or mode == "file":
Editor += c
elif mode == "play append" or mode == "play edit":
Editor += c.upper()
playC(c.upper())
elif mode == "practise":
if ToPlay == "":
pause()
else:
n = ToPlay[0]
if c == n or c.upper() == n:
playC(c.upper())
while True:
ToPlay = ToPlay[1:]
if len(ToPlay) == 0 or ToPlay[0].isalpha():
break
# Function invoked when a symbol key is pressed
def symbolKeyPress(c):
global Editor
if mode == "playing" or mode == "pause":
playC(c)
elif mode == "append" or mode == "edit" or mode == "file":
Editor += c
elif mode == "play append" or mode == "play edit":
Editor += c
playC(c)
# Function invoked when ESC is pressed
def escPress():
pass
def backspacePress():
global Editor
if mode == "append" or mode == "play append" \
or mode == "edit" or mode == "play edit" \
or mode == "file":
Editor = Editor[:-1]
def returnPress():
global mode, ToPlay, Editor, filename, Info
if mode == "append" or mode == "play append":
pause()
ToPlay += Editor
Editor = ""
elif mode == "edit" or mode == "play edit":
pause()
ToPlay = Editor
Editor = ""
elif mode == "file":
try:
with open(Editor) as f:
ToPlay += "".join(f.readlines())
except:
Info = "%s: File not found." % Editor
pause()
Editor = ""
# ------------------------------------------------------------------------------#
# Draw functions #
# ------------------------------------------------------------------------------#
# Routine of Drawing Rectangle
def drawText(text, x0, y0, size, fill):
r, g, b = fill[0], fill[1], fill[2]
pyglet.text.Label(text, anchor_x="left", anchor_y="bottom",
x=x0, y=y0, color=(r, g, b, 255), font_name="Arial", font_size=size).draw()
# Draw the background -- white box filling the window
def drawBackground():
pyglet.shapes.BorderedRectangle(0, 0, winw, winh).draw()
# Draw the text informations
def drawInfo():
drawText("Music: %s" % ToPlay, 0, winh-texth, texts, [0, 0, 0])
drawText("Played: %s" % Played[-1-maxPlayedSize:],
0, winh-texth*2, texts, [0, 0, 0])
if mode == "append":
drawText("Append: %s" % Editor[-1-maxAppendSize:],
0, winh-texth*3, texts, [0, 0, 0])
elif mode == "play append":
drawText("Append: %s" % Editor[-1-maxAppendSize:],
0, winh-texth*3, texts, [0, 0, 0])
elif mode == "edit":
drawText("Edit: %s" % Editor[-1-maxAppendSize:],
0, winh-texth*3, texts, [0, 0, 0])
elif mode == "play edit":
drawText("Edit: %s" % Editor[-1-maxAppendSize:],
0, winh-texth*3, texts, [0, 0, 0])
elif mode == "file":
drawText("File: %s" % Editor[-1-maxAppendSize:],
0, winh-texth*3, texts, [0, 0, 0])
if Info == "":
drawText("%s" % mode, 0, 0, texts, [0, 0, 0])
else:
drawText("%s" % Info, 0, 0, texts, [0, 0, 0])
################################################################################
################# INITIALIZATION ###############################################
################################################################################
# Load all the sound files
for s in alphabeta:
getSound(s)
# appendPlay(Tomorrow)
################################################################################
################### CALL BACK FUNCTIONS ########################################
################################################################################
# Callback function: onKeyPress
@window.event
def on_key_press(symbol, modifiers):
global Info # Information string, any key will erase it
Info = ""
# If symbol is between 'a' and 'z'
if symbol >= pyglet.window.key.A and symbol <= pyglet.window.key.Z:
# Pressing ctrl, invoke command
if modifiers & pyglet.window.key.MOD_CTRL:
command(chr(symbol))
elif modifiers & pyglet.window.key.MOD_SHIFT:
alphaKeyPress(chr(symbol).upper())
else:
# Invoke the alphakeyPress function
alphaKeyPress(chr(symbol))
elif symbol in symbolnum:
symbolKeyPress(chr(symbol))
# Esc key pressed
elif symbol == pyglet.window.key.ESCAPE:
escPress()
elif symbol == pyglet.window.key.BACKSPACE:
backspacePress()
elif symbol == pyglet.window.key.RETURN:
returnPress()
# Callback function: onDraw
@window.event
def on_draw():
# Clear the window
window.clear()
drawBackground()
drawInfo() # Draw the text informations
batch.draw()
################################################################################
# Start the application
pyglet.app.run()