2020-07-21 21:26:21 -04:00
|
|
|
import signal
|
|
|
|
import RPi.GPIO as GPIO
|
|
|
|
from SoundslabDisplay import SoundslabDisplay
|
|
|
|
import time
|
|
|
|
import musicpd
|
2020-08-02 12:03:26 -04:00
|
|
|
from math import floor
|
2020-07-21 21:26:21 -04:00
|
|
|
|
2020-08-02 19:47:56 -04:00
|
|
|
class SoundslabInputHandler:
|
|
|
|
def __init__(self):
|
|
|
|
# connect to mpd
|
|
|
|
self.player_client = musicpd.MPDClient()
|
|
|
|
self.player_client.connect()
|
|
|
|
|
|
|
|
# initialize connection to display
|
2020-08-02 21:26:50 -04:00
|
|
|
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 = []
|
2020-08-02 19:47:56 -04:00
|
|
|
|
|
|
|
# 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.
|
2020-08-02 19:52:54 -04:00
|
|
|
GPIO.setup(self.BUTTONS, GPIO.IN, pull_up_down=GPIO.PUD_UP)
|
2020-08-02 19:47:56 -04:00
|
|
|
|
|
|
|
# This is a dictionary of the combination of pins and labels
|
|
|
|
self.LABELLED_BUTTONS = {
|
2020-08-02 19:52:54 -04:00
|
|
|
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
|
2020-08-02 19:47:56 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
# 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)
|
|
|
|
|
2020-08-02 21:26:50 -04:00
|
|
|
# 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
|
|
|
|
|
2020-08-02 19:47:56 -04:00
|
|
|
# some button handlers
|
|
|
|
def NE(self):
|
2020-08-02 21:26:50 -04:00
|
|
|
# this is ALWAYS play/pause toggle
|
2020-08-02 19:47:56 -04:00
|
|
|
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):
|
2020-08-02 21:26:50 -04:00
|
|
|
# this is ALWAYS previous track
|
2020-08-02 19:47:56 -04:00
|
|
|
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):
|
2020-08-02 21:26:50 -04:00
|
|
|
# this is ALWAYS toggle menu
|
2020-08-02 19:47:56 -04:00
|
|
|
print("toggling menu")
|
2020-08-02 21:26:50 -04:00
|
|
|
if self.state != self.MENU:
|
|
|
|
self.last_state = self.state
|
|
|
|
self.state = self.MENU
|
2020-08-02 19:47:56 -04:00
|
|
|
else:
|
2020-08-02 21:26:50 -04:00
|
|
|
self.state = self.last_state
|
|
|
|
self.current_menu_level = 1
|
|
|
|
self.current_menu_position = 0
|
|
|
|
self.current_menu_items = []
|
2020-08-02 19:47:56 -04:00
|
|
|
|
|
|
|
def SE(self):
|
2020-08-02 21:26:50 -04:00
|
|
|
# this is ALWAYS next track
|
2020-08-02 19:47:56 -04:00
|
|
|
print("next track")
|
|
|
|
self.player_client.next()
|
|
|
|
|
|
|
|
def UP(self):
|
2020-08-02 21:26:50 -04:00
|
|
|
# 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
|
2020-08-02 19:47:56 -04:00
|
|
|
|
|
|
|
def DN(self):
|
2020-08-02 21:26:50 -04:00
|
|
|
# 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
|
2020-08-02 19:47:56 -04:00
|
|
|
|
|
|
|
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()
|
2020-07-21 21:26:21 -04:00
|
|
|
|
|
|
|
while True:
|
2020-08-02 21:26:50 -04:00
|
|
|
# test menu
|
|
|
|
input_handler.display.updateMenu()
|
2020-07-21 21:26:21 -04:00
|
|
|
# test album art
|
2020-08-02 19:47:56 -04:00
|
|
|
input_handler.display.updateAlbumArt()
|
2020-07-21 21:26:21 -04:00
|
|
|
# test setting up the overlay
|
2020-08-02 19:47:56 -04:00
|
|
|
input_handler.display.updateOverlay()
|
2020-07-21 21:26:21 -04:00
|
|
|
# test pushing the update to the display
|
2020-08-02 19:47:56 -04:00
|
|
|
input_handler.display.updateDisplay()
|
2020-07-21 21:26:21 -04:00
|
|
|
# wait before continuing
|
2020-08-02 19:47:56 -04:00
|
|
|
time.sleep(0.75)
|
2020-07-21 21:26:21 -04:00
|
|
|
|