Touchscreen audio controller for presentation sound

Program Startup

When the Raspberry Pi boots, the audio program starts automatically. It reads the config file and instantiates a class for each playlist, announcement, or background sound. Playlists preload their tracks, so this process takes a little while, but it displays progress as it loads each file.

Once everything is loaded, the screen layout is generated (Figure 4). Playlists are shown first – one per row. Next, announcements are shown, and they will wrap onto as many rows as needed. Finally, background sounds are shown – two per row. The middle column is left empty.

Figure 4: The layout of the touchscreen.

Code Walkthrough

Listing 2 shows the complete Python program domeAudio. In lines 1-2, import is Python's command to include an external library. Here, I'm using pygame for the screen display and audio playback and os for access to the filesystem.

Listing 2

domeAudio.py

001 import pygame
002 import os
003
004 class background:
005    def __init__ ( self , caption , path ):
006       self.path = path
007       self.caption = caption
008       self.state = "PAUSED"
009       self.volume = 100
010
011       self.buttonFont = pygame.font.SysFont ( "" , 30 )
012
013       self.oldButtonState = False
014       self.drawPlayButton ( False )
015
016    def drawPlayButton ( self , active ):
017       if active == None: active = self.oldButtonState
018
019       if active == False:
020          playText = self.buttonFont.render ( self.caption , True , ( 0 , 0 , 0 ) )
021          self.playButton = pygame.Surface ( ( 100 , 50 ) )
022          self.playButton.fill ( ( 100 , 0 , 0 ) )
023          self.playButton.blit ( playText , ( 50 - ( playText.get_width() / 2 ) , 10 ) )
024          self.oldButtonState = False
025
026       else:
027          playText = self.buttonFont.render ( self.caption , True , ( 0 , 0 , 0 ) )
028          self.playButton = pygame.Surface ( ( 100 , 50 ) )
029          self.playButton.fill ( ( 255 , 0 , 0 ) )
030          self.playButton.blit ( playText , ( 50 - ( playText.get_width() / 2 ) , 10 ) )
031          self.oldButtonState = True
032
033       halfVolume = int ( float ( self.volume ) / 2.0 )
034       top = 50 - halfVolume
035       pygame.draw.rect ( self.playButton , ( 0 , 0 , 255 ) , ( 0 , top , 3 , halfVolume ) , 0 )
036
037    def toggle ( self ):
038       if self.state == "PLAYING":
039          self.stop()
040          self.state = "PAUSED"
041       else:
042          self.play()
043          self.state = "PLAYING"
044
045    def play ( self ):
046       self.track = pygame.mixer.Sound ( self.path )
047       self.track.play ( -1 )
048       self.track.set_volume ( ( float ( self.volume ) / 100 ) )
049
050       self.drawPlayButton ( True )
051
052    def stop ( self ):
053       self.track.fadeout ( 1000 )
054       self.state = "PAUSED"
055
056       self.drawPlayButton ( False )
057
058    def controls ( self ):
059       self.buttons = list()
060
061       volupText = self.buttonFont.render ( "VOL UP" , True , ( 0 , 0 , 0 ) )
062       volupButton = pygame.Surface ( ( 100 , 50 ) )
063       volupButton.fill ( ( 255 , 0 , 0 ) )
064       volupButton.blit ( volupText , ( 50 - ( volupText.get_width() / 2 ) , 10 ) )
065       self.buttons.append ( ( volupButton , self.caption , "BGVOLUP" ) )
066
067       voldnText = self.buttonFont.render ( "VOL DN" , True , ( 0 , 0 , 0 ) )
068       voldnButton = pygame.Surface ( ( 100 , 50 ) )
069       voldnButton.fill ( ( 255 , 0 , 0 ) )
070       voldnButton.blit ( voldnText , ( 50 - ( voldnText.get_width() / 2 ) , 10 ) )
071       self.buttons.append ( ( voldnButton , self.caption , "BGVOLDN" ) )
072
073       self.drawPlayButton ( None )
074
075       return self.buttons
076
077    def command ( self , cmd ):
078       if cmd == "BGVOLUP":
079          self.volume += 2
080          if self.volume > 100: self.volume = 100
081          if self.state == "PLAYING": self.track.set_volume ( ( float ( self.volume ) / 100 ) )
082       elif cmd == "BGVOLDN":
083          self.volume -= 2
084          if self.volume < 0: self.volume = 0
085          if self.state == "PLAYING": self.track.set_volume ( ( float ( self.volume ) / 100 ) )
086       self.drawPlayButton ( None )
087
088
089 class announcement:
090    def __init__ ( self , caption , path ):
091       self.path = path
092       self.caption = caption
093
094    def play ( self ):
095       announce = pygame.mixer.Sound ( self.path )
096       announce.play()
097
098 class playlist:
099    def __init__ ( self , caption , path , screen ):
100       self.screen = screen
101       self.caption = caption
102       self.tracks = list()
103
104       self.trackFont = pygame.font.SysFont ( "" , 20 )
105       self.buttonFont = pygame.font.SysFont ( "" , 30 )
106
107       y = 10
108       self.screen.fill ( ( 0 , 0 , 0 ) )
109       pygame.display.flip()
110       for track in os.listdir ( path ):
111          nameSurf = self.trackFont.render ( path + "/" + track , True , ( 255 , 0 , 0 ) )
112          self.screen.blit ( nameSurf , ( 10 , y ) )
113          y += 25
114          pygame.display.flip()
115          self.tracks.append ( pygame.mixer.Sound ( path + "/" + track ) )
116
117       self.trackIndex = 0
118       self.currentTime = 0
119       self.trackLength = 0
120       self.volume = 100
121       self.volumeChange = False
122       self.state = "PAUSED"
123       self.channel = None
124
125    def play ( self ):
126       if self.channel != None: self.channel.set_endevent()      #Clear the end event
127       self.currentTrack = self.tracks [ self.trackIndex ]
128       self.currentTrack.set_volume ( float ( self.volume ) / 100 )
129       self.trackLength = int ( self.currentTrack.get_length() )
130       self.channel = self.currentTrack.play()
131       self.state = "PLAYING"
132       self.channel.set_endevent ( pygame.USEREVENT )
133
134    def pause ( self ):
135       self.channel.set_endevent()       #Clear the end event
136       self.currentTrack.fadeout ( 1000 )
137       self.state = "PAUSED"
138
139    def checkStopped ( self ):
140       if self.state == "PLAYING":
141          if self.channel.get_busy() == False:
142             self.channel.set_endevent()
143             self.trackIndex += 1
144             if self.trackIndex > len ( self.tracks ) - 1: self.trackIndex = 0
145             self.currentTrack = self.tracks [ self.trackIndex ]
146             self.currentTrack.set_volume ( float ( self.volume ) / 100 )
147
148             self.trackLength = int ( self.currentTrack.get_length() )
149             self.currentTime = 0
150             self.channel = self.currentTrack.play()
151             self.channel.set_endevent ( pygame.USEREVENT )
152
153    def controls ( self ):
154       self.buttons = list()
155       if self.state == "PLAYING":
156          playText = self.buttonFont.render ( "PAUSE" , True , ( 0 , 0 , 0 ) )
157          playButton = pygame.Surface ( ( 100 , 50 ) )
158          playButton.fill ( ( 255 , 0 , 0 ) )
159          playButton.blit ( playText , ( 50 - ( playText.get_width() / 2 ) , 10 ) )
160          self.buttons.append ( ( playButton , self.caption , "PAUSE" ) )
161       else:
162          playText = self.buttonFont.render ( "PLAY" , True , ( 0 , 0 , 0 ) )
163          playButton = pygame.Surface ( ( 100 , 50 ) )
164          playButton.fill ( ( 255 , 0 , 0 ) )
165          playButton.blit ( playText , ( 50 - ( playText.get_width() / 2 ) , 10 ) )
166          self.buttons.append ( ( playButton , self.caption , "PLAY" ) )
167
168       playText = self.buttonFont.render ( "NEXT" , True , ( 0 , 0 , 0 ) )
169       playButton = pygame.Surface ( ( 100 , 50 ) )
170       playButton.fill ( ( 255 , 0 , 0 ) )
171       playButton.blit ( playText , ( 50 - ( playText.get_width() / 2 ) , 10 ) )
172       self.buttons.append ( ( playButton , self.caption , "NEXT" ) )
173
174       playText = self.buttonFont.render ( "PREV" , True , ( 0 , 0 , 0 ) )
175       playButton = pygame.Surface ( ( 100 , 50 ) )
176       playButton.fill ( ( 255 , 0 , 0 ) )
177       playButton.blit ( playText , ( 50 - ( playText.get_width() / 2 ) , 10 ) )
178       self.buttons.append ( ( playButton , self.caption , "PREV" ) )
179
180       playText = self.buttonFont.render ( "VOL UP" , True , ( 0 , 0 , 0 ) )
181       playButton = pygame.Surface ( ( 100 , 50 ) )
182       playButton.fill ( ( 255 , 0 , 0 ) )
183       playButton.blit ( playText , ( 50 - ( playText.get_width() / 2 ) , 10 ) )
184       self.buttons.append ( ( playButton , self.caption , "VOLUP" ) )
185
186       playText = self.buttonFont.render ( "VOL DN" , True , ( 0 , 0 , 0 ) )
187       playButton = pygame.Surface ( ( 100 , 50 ) )
188       playButton.fill ( ( 255 , 0 , 0 ) )
189       playButton.blit ( playText , ( 50 - ( playText.get_width() / 2 ) , 10 ) )
190       self.buttons.append ( ( playButton , self.caption , "VOLDN" ) )
191
192       return self.buttons
193
194    def command ( self , cmd ):
195       if cmd == "PLAY": self.play()
196       elif cmd == "PAUSE": self.pause()
197       elif cmd == "NEXT": self.next()
198       elif cmd == "PREV": self.prev()
199       elif cmd == "VOLUP": self.volup()
200       elif cmd == "VOLDN": self.voldown()
201    def next ( self ):
202       self.channel.set_endevent()       #Clear the end event
203       self.currentTrack.fadeout ( 1000 )
204       self.trackIndex += 1
205       if self.trackIndex > len ( self.tracks ) - 1: self.trackIndex = 0
206       self.currentTrack = self.tracks [ self.trackIndex ]
207       self.trackLength = int ( self.currentTrack.get_length() )
208       self.currentTime = 0
209       self.channel = self.currentTrack.play()
210       self.channel.set_endevent ( pygame.USEREVENT )
211
212    def prev ( self ):
213       self.channel.set_endevent()       #Clear the end event
214       self.currentTrack.fadeout ( 1000 )
215       self.trackIndex -= 1
216       if self.trackIndex < 0: self.trackIndex = len ( self.tracks ) - 1
217       self.currentTrack = self.tracks [ self.trackIndex ]
218       self.trackLength = int ( self.currentTrack.get_length() )
219       self.currentTime = 0
220       self.channel = self.currentTrack.play()
221       self.channel.set_endevent ( pygame.USEREVENT )
222
223    def volup ( self ):
224       self.volume += 2
225       if self.volume > 100: self.volume = 100
226       if self.state == "PLAYING": self.currentTrack.set_volume ( ( float ( self.volume ) / 100 ) )
227       self.volumeChange = True
228
229    def voldown ( self ):
230       self.volume -= 2
231       if self.volume < 0: self.volume = 0
232       if self.state == "PLAYING": self.currentTrack.set_volume ( ( float ( self.volume ) / 100 ) )
233       self.volumeChange = True
234
235    def drawVolume ( self ):
236       return self.trackFont.render ( str ( self.volume ) + "%" , True , ( 255 , 0 , 0 ) )
237
238    def tick ( self ):
239       if self.state == "PLAYING":
240          self.currentTime += 1
241       time = self.trackFont.render ( str ( self.trackLength - self.currentTime ) , True , ( 255 , 0 , 0 ) )
242       return time
243
244 class music:
245    def __init__ ( self , screen ):
246       self.screen = screen
247       self.playlists = dict()
248       self.announcements = dict()
249       self.background = dict()
250
251       pygame.mixer.init()
252       pygame.mixer.init()
253
254       pygame.font.init()
255       self.labelFont = pygame.font.SysFont ( "" , 30 )
256
257       config = open ( "config.txt" , "r" )
258       for line in config:
259          lineParts = line.split ( "\t" )
260          if lineParts [ 2 ].strip() == "PLAYLIST": self.playlists [ lineParts [ 0 ].strip() ] = \
               playlist ( lineParts [ 0 ] , lineParts [ 1 ] , self.screen )
261          if lineParts [ 2 ].strip() == "ANNOUNCEMENT": self.announcements [ lineParts [ 0 ].strip() ] = \
               announcement ( lineParts [ 0 ] , lineParts [ 1 ] )
262          if lineParts [ 2 ].strip() == "BACKGROUND": self.background [ lineParts [ 0 ].strip() ] = \
               background ( lineParts [ 0 ] , lineParts [ 1 ] )
263
264
265    def drawControls ( self ):
266       self.screen.fill ( ( 0 , 0 , 0 ) )
267       self.clickZones = list()
268       y = 10
269       for index , playlist in self.playlists.items():
270          x = 250
271          caption = self.labelFont.render ( index , True , ( 255 , 0 , 0 ) )
272          self.screen.blit ( caption , ( 10 , y ) )
273          self.screen.blit ( playlist.drawVolume() , ( 50 , y + 30 ) )
274          for button , playlist , cmd in playlist.controls():
275             zoneRect = self.screen.blit ( button , ( x , y ) )
276             x += button.get_width() + 10
277             self.clickZones.append ( ( zoneRect , playlist , cmd ) )
278          y += 60
279
280       x = 30
281       lineCount = 0
282       for index , announce in self.announcements.items():
283          playText = self.labelFont.render ( index , True , ( 0 , 0 , 0 ) )
284          playButton = pygame.Surface ( ( 100 , 50 ) )
285          playButton.fill ( ( 255 , 0 , 0 ) )
286          playButton.blit ( playText , ( 50 - ( playText.get_width() / 2 ) , 10 ) )
287          zone = self.screen.blit ( playButton , ( x , y ) )
288          self.clickZones.append ( ( zone , index , "ANNOUNCEMENT" ) )
289          x += 110
290          lineCount += 1
291          if lineCount == 7:
292             lineCount = 0
293             x = 30
294             y += 60
295
296       x = 30
297       y += 60
298       lineCount = 0
299       for index , backTrack in self.background.items():
300          zone = self.screen.blit ( backTrack.playButton , ( x , y ) )
301          self.clickZones.append ( ( zone , index , "BACKGROUND" ) )
302          x += 110
303
304          for button , playlist , cmd in backTrack.controls():
305             zoneRect = self.screen.blit ( button , ( x , y ) )
306             x += button.get_width() + 10
307             self.clickZones.append ( ( zoneRect , playlist , cmd ) )
308
309          lineCount += 1
310          if lineCount == 6:
311             lineCount = 0
312             x = 30
313             y += 60
314
315
316       pygame.display.flip()
317
318    def loop ( self ):
319       pygame.time.set_timer ( pygame.USEREVENT + 1 , 1000 )
320       self.drawControls()
321
322       looping = True
323       while looping:
324          for event in pygame.event.get():
325             if event.type == pygame.MOUSEBUTTONDOWN:
326                for zone in self.clickZones:
327                   if zone [ 0 ].collidepoint ( event.pos ) == True:
328                      if zone [ 2 ] == "ANNOUNCEMENT":
329                         self.announcements [ zone [ 1 ] ].play()
330                      elif zone [ 2 ] == "BACKGROUND":
331                         self.background [ zone [ 1 ] ].toggle()
332                         self.drawControls()
333                      elif zone [ 2 ] == "BGVOLUP":
334                         self.background [ zone [ 1 ] ].command ( zone [ 2 ] )
335                         self.drawControls()
336                      elif zone [ 2 ] == "BGVOLDN":
337                         self.background [ zone [ 1 ] ].command ( zone [ 2 ] )
338                         self.drawControls()
339                      else:
340                         self.playlists [ zone [ 1 ] ].command ( zone [ 2 ] )
341                         self.drawControls()
342             elif event.type == pygame.USEREVENT:
343                for index , playlist in self.playlists.items():
344                   playlist.checkStopped()
345             elif event.type == pygame.USEREVENT + 1:
346                redraw = list()
347                y = 10
348                for index , playlist in self.playlists.items():
349                   pygame.draw.rect ( self.screen , ( 0 , 0 , 0 ) , ( 10 , y + 30 , 100 , 20 ) )
350                   redraw.append ( self.screen.blit ( playlist.tick() , ( 10 , y + 30 ) ) )
351                   y += 60
352                pygame.display.update ( redraw )
353          y = 10
354          for index , playlist in self.playlists.items():
355             if playlist.volumeChange == True:
356                pygame.draw.rect ( self.screen , ( 0 , 0 , 0 ) , ( 50 , y + 30 , 100 , 20 ) )
357                rect = self.screen.blit ( playlist.drawVolume() , ( 50 , y + 30 ) )
358                pygame.display.update ( [ rect ] )
359                playlist.volumeChange = False
360             y += 60
361
362 pygame.display.init()
363 screen = pygame.display.set_mode ( ( 800 , 480 ) )
364
365 sounds = music ( screen )
366 sounds.loop()

Buy this article as PDF

Express-Checkout as PDF

Pages: 2

Price $2.95
(incl. VAT)

Buy Raspberry Pi Geek

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content