I recently finished a project to turn the Raspberry Pi into a music streamer. I had seen a couple of other projects here and here that were the same basic concept, so I knew it was possible (this is nothing new…just new for me), and I wanted to try to figure it out by myself. My basic justification for the project was that I listen to a lot of music on Pandora or Spotify while reading and doing homework, but having it up on the computer means I have an instant distraction any time I feel like checking email, news, or whatever. Building a small, standalone player with just a screen to display song titles and a simple interface using a few buttons removes that temptation. I also had a couple of weeks off school between summer and fall sessions, and I wanted a quick project I could finish up before I lose all free time again.
Spotify provides a C API which looks pretty cool, but you need to have a premium membership ($10/month) to use it. It would also be more involved to get working correctly, but I may come back to that in the future if I get more time, because it seems like it'd be a fun project. For Pandora, there is a Linux command line program called pianobar that provides a really clean interface to Pandora and doesn't have any advertisements, and it's free. I decided to stick with Pandora via pianobar for now and create my program to control it in Python.
I'm doing the development work using my Ubuntu desktop to ssh into the Raspberry Pi. I'm using the Raspbian Wheezy distribution on the Pi, and you have to enable ssh when you first configure it. To enter the configuration menu again later you can use the following command.
I connected my Raspberry Pi to a monitor to figure out it's IP address (using the “ifconfig” command), which is 192.168.1.11 (replace that with your IP address the following command). Running the following command from the terminal on your local computer will give you a terminal on the Raspberry Pi. Using the “-X” option lets you open up GUI windows like the text editor “nedit” (or the simple Pandora GUI below). You will be prompted for your password for the “pi” user, which is “raspberry”, unless you've changed it.
ssh -X [email protected]
First off, you need to install pianobar. The instructions here are for Lubuntu, but they work on the Raspberry Pi as well. The “make” program was already installed for me, but the rest were new. You need to install “git” in order to download the source code from github, and then the rest of the packages are needed to build.
sudo apt-get install git git clone https://github.com/PromyLOPh/pianobar.git cd pianobar sudo apt-get install libao-dev libgcrypt11-dev libgnutls-dev libfaad-dev libmad0-dev libjson0-dev make pkg-config make sudo make install
After all of this you can type “pianobar” on the command line and the program will start, prompting you for your Pandora username (email) and password.
When I first started pianobar, it printed the following error: “ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.front”. I found a post here that describes how to fix it. You need to change the line that says “pcm.front cards.pcm.front” to “pcm.front cards.pcm.default” in /usr/share/alsa/alsa.conf. The file is read only, so you'll need to open up the permissions or edit it using sudo. For me, the line number to change was 130 but I'm not sure if that will be true for others.
I first set up pianobar back in May, and at that time there was a loud popping sound at the beginning and end of every song. I did an update since then, and the popping went away, so they must have fixed whatever was causing that issue.
Another issue I've had (and still do) is that if you try to skip songs too many times Pandora locks you out. It starts out by making all tracks 42 seconds long of just silence, and if you keep skipping it eventually locks you out entirely. It's not really consistent how many skips triggers it, but just realize that if it stops working you should probably just give it a break for a bit.
You can store the username and password in a file that pianobar will automatically load when it starts. The filename is “/home/pi/.config/pianobar/config” and it should contain the following two lines. This worked for me when I used pianobar interactively, but I later realized that you have to run programs that use the GPIO pins as root, so you'll want to put the same file at “/root/.config/pianobar/config”.
user = [email protected] password = your_password_here
The final step to configuring pianobar so that you can control it from python is to set up a fifo using the following command. The same thing goes here for running as root, so I've included a second command for that.
mkfifo ~/.config/pianobar/ctl sudo mkfifo /root/.config/pianobar/ctl
I started out by making a simple python GUI as a substitute for the buttons and screen that would later be connected to the Raspberry Pi. This allowed me to get the interface between python and pianobar working correctly before having to worry about any of the hardware. I used Tkinter for the GUI, and it was pretty basic. There were a couple of lines that displayed information that would later be shown on a 16×2 LCD, and then buttons that have the same functions as the physical buttons that would be added later. I have buttons to start/stop pianobar, play/pause the song, skip to next song, ban song (don't play again for 1 month), or “love” song. There are plenty of other things I could add, and pianobar has a very full-featured command set, but I wanted to keep it simple. These are the same buttons I kept on my final physical version.
# input_GUI.py from tkinter import * import subprocess import re fifo = "/home/zach/.config/pianobar/ctl" class App: def __init__(self,master): frame = Frame(master) frame.pack() self.row_counter = 0 self.pianobarOnFlag = 0 #Start/Stop button = Button(frame, text='Start/Stop', command=self.startStopButton) button.grid(row=self.row_counter, column = 0) #Play/Pause button = Button(frame, text='Play/Pause', command=self.playPauseButton) button.grid(row=self.row_counter, column = 1) #Next Song button = Button(frame, text='Next', command=self.nextButton) button.grid(row=self.row_counter, column = 2) #Love Song button = Button(frame, text='Love', command=self.loveButton) button.grid(row=self.row_counter, column = 3) #Ban Song button = Button(frame, text='Ban', command=self.banButton) button.grid(row=self.row_counter, column = 4) self.row_counter+=1 #Display: Line 1 self.line1 =StringVar() Label(frame, text = 'Song').grid(row=self.row_counter, column=0) Entry(frame, textvariable=self.line1).grid(row=self.row_counter, column=1) self.row_counter+=1 #Display: Line 1 self.line2 =StringVar() Label(frame, text = 'Artist').grid(row=self.row_counter, column=0) Entry(frame, textvariable=self.line2).grid(row=self.row_counter, column=1) self.row_counter+=1 def startStopButton(self): print('Start/Stop button pressed') if self.pianobarOnFlag == 0: print('Starting pianobar!') self.pianobar_output = open("pianobar.out","w") self.pianobar_output.close() self.pianobar_output = open("pianobar.out","r+") self.pianobarOnFlag = 1 self.outfilePosition = 0 subprocess.Popen("pianobar", stdout = self.pianobar_output) else: print('Stopping pianobar!') self.writeFifo(command = 'q') self.pianobarOnFlag = 0 self.pianobar_output.close() def playPauseButton(self): print('Play/Pause button pressed') self.writeFifo(command = 'p') def nextButton(self): print('Next button pressed') self.writeFifo(command = 'n') def loveButton(self): print('Love button pressed!') self.writeFifo(command = '+') def banButton(self): print('Ban button pressed!') self.writeFifo(command = '-') def getSongInfo(self): print('getSongInfo Called!') if self.pianobarOnFlag == 1: print('Grepping for song info!') self.pianobar_output.seek(self.outfilePosition) text = self.pianobar_output.read() self.outfilePosition = self.pianobar_output.tell() print(text) infoStr = re.search('\>.*by.*on.*', text) if infoStr is not None: print('Info String:') print(infoStr.group(0)) infoStrSplit = infoStr.group(0).split() print(infoStrSplit) for index in range(len(infoStrSplit)): if infoStrSplit[index]=='by': byIdx = index if infoStrSplit[index]=='on': onIdx = index song = ' '.join(infoStrSplit[1:byIdx]) song = song.replace('"','') artist = ' '.join(infoStrSplit[byIdx+1:onIdx]) artist = artist.replace('"','') self.line1.set(song) self.line2.set(artist) def getSongNameLoop(self): self.getSongInfo() root.after(2000,self.getSongNameLoop) def writeFifo(self, command): fifo_w = open(fifo, 'w') fifo_w.write(command) fifo_w.close() root = Tk() root.wm_title('Jukebox') app = App(root) root.after(2000,app.getSongNameLoop()) #Check for new song info every 2 seconds root.mainloop()
I used Python 3 for the GUI, and I ran it from my Ubuntu desktop until I was ready to transition to the Raspberry Pi. If you copy the code below into a text file called “input_GUI.py”, you can call it from the terminal by typing “python3 input_GUI.py”. It will open up a GUI window that looks like the one below, and you can click the buttons to interface with pianobar.
Once I had the interface between Python and pianobar working correctly, I transitioned to the Raspberry Pi. The main things to add here were the physical screen and buttons. The most common library I was finding for GPIOs was for Python 2, not Python 3, so I switched to that.
For more detail: Raspberry Pi Pandora Streamer