-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuzzer.py
More file actions
executable file
·263 lines (194 loc) · 7.75 KB
/
buzzer.py
File metadata and controls
executable file
·263 lines (194 loc) · 7.75 KB
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
#!/usr/bin/python3
import RPi.GPIO as GPIO # Import Raspberry Pi GPIO library
from omxplayer.player import OMXPlayer
from time import sleep
from gilligames.Timer import Timer
from gilligames.UIImages import UIImages
from tkinter import *
import pygame as pg
import os
########################################################################################
def loadPgSound(name):
class NoneSound:
def play(self):
pass
if not pg.mixer or not pg.mixer.get_init():
return NoneSound()
global appPath
sound = pg.mixer.Sound("{}/{}".format(appPath, name))
return sound
################################################################################################
def createPlayer(filePath, layer):
global screenWidth
global screenHeight
dbusName = "org.mpris.MediaPlayer2.omxplayer{}".format(layer)
videoArgs = "--win 0,0,{},{} -o hdmi --layer {}".format(screenWidth, screenHeight, layer)
player = OMXPlayer(
filePath,
args=videoArgs,
dbus_name=dbusName
)
player.exitEvent += videoExited
return player
################################################################################################
def videoExited(player, event):
global leftPlayer
global rightPlayer
global currentVideoPlayer
# Turn the LED's back on when ready to press again.
GPIO.output(LEFT_LED, 1)
GPIO.output(RIGHT_LED, 1)
currentVideoPlayer = None
################################################################################################
# When a buzzer video is playing, flash the LED of the side that matches.
def flashLED():
global leftPlayer
global rightPlayer
global isFlashOn
if (not flashingLedTimer.isExpired()):
return
if (currentVideoPlayer == leftPlayer):
GPIO.output(LEFT_LED, isFlashOn)
else:
GPIO.output(RIGHT_LED, isFlashOn)
isFlashOn = not isFlashOn
flashingLedTimer.reset()
################################################################################################
def checkForInput():
global nextTieWinnerLeft
global currentVideoPlayer
global doPlayLeftVideo
global doPlayRightVideo
global videoTimer
global isFlashOn
if (doPlayLeftVideo):
currentVideoPlayer = playLeftVideo()
if (doPlayRightVideo):
currentVideoPlayer = playRightVideo()
if (videoTimer != None and videoTimer.isExpired()):
# After a video has played for a short bit, reset the background.
videoTimer = None
setBackground("buzzer_background_{}x{}.png".format(screenWidth, screenHeight))
if (currentVideoPlayer == None):
isLeftPressed = not GPIO.input(LEFT_BUTTON_PIN)
isRightPressed = not GPIO.input(RIGHT_BUTTON_PIN)
if (isLeftPressed or isRightPressed):
GPIO.output(LEFT_LED, 0)
GPIO.output(RIGHT_LED, 0)
isFlashOn = True
if (isLeftPressed and isRightPressed):
# Both pressed at the same time, so we need to choose one to be the winner.
if (nextTieWinnerLeft):
isRightPressed = False
else:
isLeftPressed = False
# Now alternate who wins the next tie.
nextTieWinnerLeft = not nextTieWinnerLeft
if (isLeftPressed):
setBackground("buzzer_left_still_{}x{}.png".format(screenWidth, screenHeight))
doPlayLeftVideo = True
buzzerSound.play()
elif (isRightPressed):
setBackground("buzzer_right_still_{}x{}.png".format(screenWidth, screenHeight))
doPlayRightVideo = True
buzzerSound.play()
else:
flashLED()
win.after(1, checkForInput)
################################################################################################
def keyPressed(event):
if (event.keysym == "Escape"):
cleanup()
################################################################################################
def cleanup():
if (leftPlayer != None):
leftPlayer.quit()
if (rightPlayer != None):
rightPlayer.quit()
pg.quit()
GPIO.output(17, 0)
GPIO.cleanup()
win.destroy()
################################################################################################
def playLeftVideo():
global leftPlayer
global doPlayLeftVideo
global videoTimer
videoTimer = Timer(1)
doPlayLeftVideo = False
if (leftPlayer == None):
leftPlayer = createPlayer(leftVideoPath, 1)
else:
leftPlayer.load(leftVideoPath)
return leftPlayer
################################################################################################
def playRightVideo():
global rightPlayer
global doPlayRightVideo
global videoTimer
videoTimer = Timer(1)
doPlayRightVideo = False
if (rightPlayer == None):
rightPlayer = createPlayer(rightVideoPath, 2)
else:
rightPlayer.load(rightVideoPath)
return rightPlayer
################################################################################################
def setBackground(path):
global mainCanvas
global background
mainCanvas.itemconfig(background, image=UIImages.get(path))
################################################################################################
# Uses BCM pin numbering.
LEFT_BUTTON_PIN = 13
LEFT_LED = 17
RIGHT_BUTTON_PIN = 27
RIGHT_LED = 19
flashingLedTimer = Timer(0.1)
isFlashOn = True
win = Tk()
win.title("Buzzer")
win.attributes("-fullscreen", True)
win.configure(bg="black", cursor="none") # hide the mouse pointer over the window
win.bind("<KeyPress>", keyPressed)
screenWidth = win.winfo_screenwidth()
screenHeight = win.winfo_screenheight()
print("Screen size: {}x{}".format(screenWidth, screenHeight))
# Create the canvas to hold the background image.
mainCanvas = Canvas(win, width=screenWidth, height=screenHeight, highlightthickness=0)
background = mainCanvas.create_image(0, 0, anchor=NW, image=UIImages.get("buzzer_background_{}x{}.png".format(screenWidth, screenHeight)))
mainCanvas.pack(fill="both", expand=True)
leftVideoPath = "./av/buzzer_left.mov"
rightVideoPath = "./av/buzzer_right.mov"
leftPlayer = None
rightPlayer = None
currentVideoPlayer = None
videoTimer = None
# Use these to defer starting videos to the next loop iteration, so the background image can be updated ASAP,
# to avoid the noticeable delay in starting the video. The background image appears quicker.
doPlayLeftVideo = False
doPlayRightVideo = False
# Use pygame for playing standalone sounds, because it's more responsive than omxplayer.
# However, to use pygame sounds, we need to create a pygame window, even if it's positioned offscreen.
pg.init()
pg.display.set_mode((1,1), pg.HIDDEN)
# Set the app path for loading sound using pygame.
appPath = os.path.split(os.path.abspath(__file__))[0]
# Pre-load the buzzer sound so it can play ASAP when a button is pressed, instead of lagging from video loading.
# In order to force pygame sounds to use HDMI instead of the headphone jack, set it in the audio options when running "sudo raspi-config" from command line.
buzzerSound = loadPgSound("av/buzzer.wav")
GPIO.setwarnings(False) # Ignore warning for now
GPIO.setmode(GPIO.BCM)
GPIO.setup(LEFT_BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(RIGHT_BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(LEFT_LED, GPIO.OUT)
GPIO.setup(RIGHT_LED, GPIO.OUT)
nextTieWinnerLeft = True
GPIO.output(LEFT_LED, 1)
GPIO.output(RIGHT_LED, 1)
# Do other stuff every frame.
win.after(1, checkForInput)
try:
win.mainloop()
except KeyboardInterrupt:
cleanup()