import signal import RPi.GPIO as GPIO from SoundslabDisplay import SoundslabDisplay import time import musicpd from math import floor class SoundslabInputHandler: def __init__(self): # connect to mpd self.player_client = musicpd.MPDClient() self.player_client.connect() # initialize connection to display self.display = SoundslabDisplay(self.player_client, self) # set up some states and an initial state (self.NOWPLAYING, self.MENU, self.LOCKED) = [1, 2, 3] self.state = self.NOWPLAYING self.current_menu_level = 1 self.current_menu_position = 0 self.current_menu_items = [] # The buttons on Pirate Audio are connected to pins 5, 6, 16 and 24 # Boards prior to 23 January 2020 used 5, 6, 16 and 20 # try changing 24 to 20 if your Y button doesn't work. self.BUTTONS = [5, 6, 16, 24, 17, 27, 22, 23, 20] # These correspond to buttons A, B, X and Y then the five-way switch positions self.LABELS = ['NW', 'SW', 'NE', 'SE', 'UP', 'SELECT', 'LF', 'DN', 'RT'] # Set up RPi.GPIO with the "BCM" numbering scheme GPIO.setmode(GPIO.BCM) # Buttons connect to ground when pressed, so we should set them up # with a "PULL UP", which weakly pulls the input signal to 3.3V. GPIO.setup(self.BUTTONS, GPIO.IN, pull_up_down=GPIO.PUD_UP) # This is a dictionary of the combination of pins and labels self.LABELLED_BUTTONS = { 5: self.NW, 6: self.SW, 16: self.NE, 24: self.SE, 17: self.UP, 27: self.SELECT, 22: self.LF, 23: self.DN, 20: self.RT } # Loop through out buttons and attach the "handle_button" function to each # We're watching the "FALLING" edge (transition from 3.3V to Ground) and # picking a generous bouncetime of 150ms to smooth out button presses. for pin in self.BUTTONS: GPIO.add_event_detect(pin, GPIO.FALLING, self.handle_button, bouncetime=150) # menu handler def getMenuOptions(self): if self.current_menu_level == 1: menu = { "options": [ "Toggle repeat", "Toggle shuffle", "View queue >", "Browse playlists >", "Browse artists >", "Browse genres >", "Browse decades >", "Browse albums >", "Browse songs >", "Shuffle all songs", "Shuffle all albums", "Settings >", "Power off", "About" ] } if self.current_menu_position == 0: rows = ["", menu["options"][self.current_menu_position], menu["options"][self.current_menu_position + 1]] elif self.current_menu_position == len(menu["options"]) - 1: rows = [menu["options"][self.current_menu_position - 1], menu["options"][self.current_menu_position], ""] else: # one before current, current, and one after current rows = [ menu["options"][self.current_menu_position - 1], menu["options"][self.current_menu_position], menu["options"][self.current_menu_position + 1] ] self.current_menu_items = menu["options"] return { "has_previous": False if self.current_menu_position == 0 else True, "has_next": False if self.current_menu_position == len(menu["options"]) else True, "selected": self.current_menu_position, "rows": rows } else: return None def handle_menu_option_select(self): pass # some button handlers def NE(self): # this is ALWAYS play/pause toggle status = self.player_client.status() if status['state'] == "play": print("pausing") player_client.pause(1) elif status['state'] == "pause": print("resuming") player_client.pause(0) else: # state == "stop" print("playing") player_client.play() def SW(self): # this is ALWAYS previous track status = player_client.status() if float(status['elapsed']) > 5.0: # restart track print("restart current track") self.player_client.seekcur(0) else: # go to previous track print("previous track") self.player_client.previous() def NW(self): # this is ALWAYS toggle menu print("toggling menu") if self.state != self.MENU: self.last_state = self.state self.state = self.MENU else: self.state = self.last_state self.current_menu_level = 1 self.current_menu_position = 0 self.current_menu_items = [] def SE(self): # this is ALWAYS next track print("next track") self.player_client.next() def UP(self): # check current state if self.state == self.NOWPLAYING: # volume up 10% print("vol up") status = self.player_client.status() if int(status['volume']) < 100: self.player_client.setvol(int(status['volume']) + 10) elif self.state == self.MENU: # move up print("up one menu item") if self.current_menu_position != 0: self.current_menu_position -= 1 else: print("cannot go up from here!") else: # no op when screen is locked pass def DN(self): # check current state if self.state == self.NOWPLAYING: # volume down 10% print("vol down") status = self.player_client.status() if int(status['volume']) > 0: self.player_client.setvol(int(status['volume']) - 10) elif self.state == self.MENU: # move down print("down one menu item") if self.current_menu_position < len(self.current_menu_items) - 1: self.current_menu_position += 1; else: print("cannot go down from here!") else: # no op when screen is locked pass def LF(self): # seek back 10s print("seek -10s") status = self.player_client.status() self.player_client.seekcur(floor(float(status['elapsed']) - 10.0)) def RT(self): # seek forward 10s print("seek +10s") status = self.player_client.status() self.player_client.seekcur(floor(float(status['elapsed']) + 10.0)) def SELECT(self): # display lock/unlock self.display.toggleDisplayOnOff() # "handle_button" will be called every time a button is pressed # It receives one argument: the associated input pin. def handle_button(self, pin): label = self.LABELS[self.BUTTONS.index(pin)] print("Button press detected on pin: {} label: {}".format(pin, label)) func = self.LABELLED_BUTTONS.get(pin) func() # for now, we'll just go ahead and keep kicking things off from here input_handler = SoundslabInputHandler() while True: # test menu input_handler.display.updateMenu() # test album art input_handler.display.updateAlbumArt() # test setting up the overlay input_handler.display.updateOverlay() # test pushing the update to the display input_handler.display.updateDisplay() # wait before continuing time.sleep(0.75)