Introduction: From Record Player to Multi-room Spotify Player

About: Maker in the making :-)

In this project, I've used an older, non functionning record player as the controler for a multi room Spotify setup. Let me show you how !

We'll see how to :

  • install a snapcast server with librespot
  • install the different snapcast clients to play audio in each room
  • use the buttons on the record player to control the volume
  • add a screen on top of the player to display the cover image of the currently playing song

Supplies

For the controller :

  • 1 Raspberry Pi (I used a Rpi 3 although I suspect this could be done with less powerful versions)
  • 1Arduino board, I used a Uno clone I had from another project
  • 1 old record player, including all of its electronics
  • 1 screen for the Raspberry Pi, I used waveshare's 5 inch low power HDMI screen

For the multi room setup :

  • 1 Raspberry Pi or other computer per room where you want to play music : it should be able to play some sound, either by plugging in a speaker via jack, by plugging it into an HDMI screen that has sound capabilities, or by using a dedicated amp + speaker (I'm using Hifiberry's Amp+ in my bedroom and mini amp in the kitchen)
  • 1 central computer for the server - this can be any of the players as well, as long as it's powerful enough to run both the server and client
  • Any enclosure you see fit for the Raspberry Pis
  • 1 premium Spotify account

Step 1: Assess Your Needs

The first step in this project was to understand how the overall project would work.

My use case was that I want to have four different audio players with the same music playing.

I opted for using Snapcast, an open source multi room project that allows you to do just that. The way it works is that there's a central server running Snapserver, which sends the audio to different clients that connect to it.

In order to get music from Spotify, we'll install LibreSpot and configure Snapcast so that it launches Librespot and registers to Spotify as a player using our credentials. As a result, Spotify sees this whole setup as a single device, with a single volume level.

This means that the object I'm creating will have to interact with the Snapcast API on one side, to set the volume for each player, and the Spotify API on the other side, if I want to retrieve the currently playing status and cover.

Of course, you could also stick to using the object as the sole player, in which case you do not have to use Snapcast. Or you could have Snapcast launch Airplay rather that Spotify, and still be able to control the volume in each room.

Step 2: Install Raspbian on Each Raspberry Pi

Each of the Raspberry Pi I mentionned uses Raspbian as a base (except the one on the living room that runs Librelec). The first thing to do is to install Raspbian on each one using the Raspberry Pi imager software : there are numerous guides online on how to do that, I recommend following the one from the Raspberry Pi Foundation.

Since we'll be using these Raspberry Pi without screens, you might want to enable SSH in order to be able to access them remotely from your computer (click on the cogwheel icon).

I've enabled the public key authentification method, so that only my computer is able to SSH into them

You might also want to give them a hostname in order to make it easier to differentiate them

Step 3: Install Snapcast - Server Side

So the first step in order to setup the multi room solution is to install Snapcast , which will allow us to stream audio from one server to multiple players.

Let's start with the server : you'll want to go to the releases page, and find the version corresponding to the operating system and architecture of the computer you're using as a server (if using a Raspberry Pi for this step, please download the ARM version and not the one highlighted on my screenshot).

In order to download the file to the server, you first have to SSH into it : use the syntax

ssh pi@YourServerIP

or if you've specified a hostname in the previous step

ssh pi@YourServerHostname 

You'll be prompted to enter your password.

Once logged in, copy the link from the Snapcast release page and type "wget" followed by that link :

wget https://github.com/badaix/snapcast/releases/download/v0.26.0/snapserver_0.26.0-1_amd64.deb

This will download the file to your server, you'll then be able to install it with "sudo dpkg -i" followed by the name of the file :

sudo dpkg -i snapserver_0.26.0-1_amd64.deb

Step 4: Install Librespot

In order to stream media from Spotify, you will have to install Libespot : the easiest way is to first install Crates, using the command below :

curl https://sh.rustup.rs -sSf | sh

After that, you'll be able to install Librespot using :

cargo install librespot

To check that the install succeeded, type

which librespot

If all went well you should be given a path (that you're going to need in the next step)

Step 5: Configure Snapserver

You now need to configure the Snapcast server to be able to launch Librespot and capture this audio in order to stream it to the clients.

This is done by editing the file "/etc/snapserver.conf" :

sudo nano /etc/snapserver.conf

Once you're there, change the values according to my screenshot, in order to allow external clients to connect to the server, and add Librespot to the list of sources (I've blurred out where you should put your username and password).

You should then restart the snapserver service using :

sudo systemctl restart snapserver.service

And if all went well, you should see a new device listed on your Spotify acount

Step 6: Install Snapcast - Client Side

For each room where you want to be able to play music, you will need one device : in my case I've used

  • Bedroom - One Raspberry Pi with the Hifiberry Amp+ shield connected to 2 wall mounted speakers (it has since been replaced by the Amp2)
  • Bathroom - One Raspberry Pi with the SpeakerPhat from Pimoroni in the bathroom (it has since been discontinued but you should be able to use any othe the Pirate Audio products)
  • Living room - Since I'm using a Raspberry Pi connected to my TV with Librelec, I just installed the "Snapclient" add-on on Kodi
  • Kitchen - One Raspberry Zero W with the HifiBerry Miniamp connected to some repurposed computer speakers.

Apart from the living room, the procedure to install the Snapcast client was always the same : SSH into the Pi, download the ARM Client from the Snapcast repository, and follow the same steps as before

wget https://github.com/badaix/snapcast/releases/download/v0.26.0/snapclient_0.26.0-1_armhf.deb

This will download the file to your server, you'll then be able to install it with "sudo dpkg -i" followed by the name of the file as previously :

sudo dpkg -i snapclient_0.26.0-1_armhf.deb

To check that all is OK, type

sudo systemctl status snapclient.service

You should see the service running.

In order to make it easier to recognize each client, we're going to assign them IDs by editing the configuration file :

sudo nano /etc/default/snapclient

In this file, you should be able to add a line that reads :

SNAPCLIENT_OPTS="--hostID=bedroom"

or whatever name you want to give to that specific client. You can then restart the client service :

sudo systemctl restart snapclient.service


Once you've done that for each client, you should be able to open a browser and go to YOURSERVERIP:1780 and see the Snapweb interface listing all your clients : you now should be able to click on the little pencil next to each client to put them in the same "group" since they'll be playing the same audio, and click on the source name to change it to Spotify if that's not already the case (see the difference between the two screenshots attached)

If you've made it this far, you deserve a little break : you now have a complete multi room setup that you created from (almost) scratch ! You can use this webpage to control the volume in each room

As far as this project goes, the next objective is to be able to use the record player rather than the webpage, so let's continue !

Step 7: Disassemble the Record Player

Once I received the record player, the first thing I did was to take it apart to understand what I could re-use. My main focus were the gorgeous rotary buttons on the front, and in order to keep them that way I decided I would try to use the existing components. Luckily, this record player was made in 1974 so all of the internals were big through-holes components that I could easily solder. I cut the wires going to each rotary button, and discarded the rest of the electronics, as I didn't find a way to use them.

Since I wanted to give the illusion that the record is playing, I needed to have a circular hole on the record player, so that the screen would be visible underneath with the cover image. I asked a friend to help me with that since they had a hole was drill bit.

Step 8: Understand How the Rotary Buttons Work

I now had access to the four rotary buttons, but I was not sure how to use them. I took one of them out of the record player, and hooked it up to my Arduino board. For some reason I first thought they were rotary encoders so I tried the simplest rotary encoder code I could find. Upon further inspection of the component, I realized they were actually potentiometers, so I tried some sample code for that, and it worked !

I put the rotary button back in the record player, I wired the three other rotary buttons to the Arduino the same way and checked that everything was still ok... And it wasn't. I had one minor and one major problem : the minor one was that the reading from the potentiometers kind of "jumped around", in the sense that the value would move a little bit at each reading. The sample code took the potentiometer value (analog, from 0 to 1024) and scaled it to 0-255, so I changed that to scale it to 0-100 instead, since I only needed a percentage to set the volume. This kind of resolved the "jumping" issue as the scaling + rounding of the value made these tiny changes less impactful.

The other problem I had that one of my potentiometers was giving me weird values : I would put every rotary button at roughly the same position, three of them would give me a similar value, and the last one just didn't make sense. I checked the wiring and soldering multiple times, but still.

While searching online for resources on how to read values from your potentiometer, I stumbled upon an article that mentionned logarithmic potentiometers vs linear ones, and I realized all of my potentiometers were simply not all the same kind ! I decided to keep it for now and adapt the Arduino code as is for that specific button, given that I still have a general sense of whether the button was turned left or right.

I think it'd be easier to replace it with a linear one, but I have to find one with the exact same footprint so that the little chrome button on top still fits (or design a 3D printed part for it to work).

(The final Arduino code is attached to this step but if you want to use it, please check the latest version on the github repository linked at the end of the instructables as it might have changed since writing this)

Step 9: Using the Snapcast API

Now that my buttons were able to give me a value between 0 and 100 for each room, my next goal was to use that value to control the corresponding snapcast players. Luckily, the Snapcast server offers a fairly simple JSON-RPC API. By default, clients are given an ID, but since we've given each one an ID, we should be able to use that (except for the Kodi add-on in my case) so that I could send this kind of JSON data to the API - I first used Postman to test this out :

{
    "id": "8",
    "jsonrpc": "2.0",
    "method": "Client.SetVolume",
    "params": {
        "id": "kitchen",
        "volume": {
            "percent": 1
        }
}

The payload being sent (via POST) to the Snapcast server

As far as the living room, using the Kodi add-on meant I could not explicitely give this client an ID : I used Postman to get the list of connected clients, using the method "getStatus" : I looked for the client that did not have one of the textual IDs that I gave it, and sure enough, it was there, with an IP address instead of the textual ID. To double check that this was OK, I tested the setVolume method on it :

{
    "id": "8",
    "jsonrpc": "2.0",
    "method": "Client.SetVolume",
    "params": {
        "id": "192.168.1.126",
        "volume": {
            "percent": 1
        }
}

and it worked !


Step 10: Putting It All Together

So I was now able to control the Snapcast volume for each zone using the JSON RPC API, and I had access to a value between 0 and 100 for each of my rotary buttons.

I initially thought I'd transfer the wiring from the Arduino to the Raspberry Pi, but then decided to keep the Arduino instead : without the internals of the record player, I actually had a lot of space, and I felt like an Arduino was better suited for reading the values from the rotary buttons, being a micro-controler, than a Raspberry Pi that is not intended for time sensitive operations like these. Plus, I already had the Arduino code for reading the values and outputting them in Serial from the previous steps !

What I did was to create a python script on the Raspberry Pi and have it communicate with the Arduino via Serial. Since the Arduino outputs the value for each rotary button via

  Serial.print(outputValue);
  Serial.print(",");

I knew exactly what my data would look like and how to parse it on the Raspberry side : a new line looking like "13,55,19,34" every 200ms

The final code for this part is attached to this step , what it does it essentially read the data from Serial whenever there's some available, wait for the string to be complete, and then parse the volume to send to each player. It then constructs the JSON payload for each player, inserting the value of the volume, and sends that to the JSON API.

Step 11: It Works !

I put everything back in the record player and it works !

Now, let's add the screen on top of the player

Step 12: Querying the Spotify API

Now that the first part of the project was done, I had a way to control the volume for each of my player - yay ! I still wanted to take the project one step further, as I was inspired by this post on the Raspberry blog : the person who made this used the Spotify API to get the cover image of the song currently playing, and displayed it on a screen to make it look like the record was spinning - genius ! While they used the record player as the output for the sound where I used it to control external players, I still felt inspired to try and do the same thing as them for the cover image.


What I needed was to be able to draw an image on the screen of the Raspberry Pi at all times, while doing other things (like downloading the image itself). I decided to use the library PyGame, as it allowed me to do just that and came with a lot of code examples.

In order to make things easier, I separated all this part of the code in a different file so that I have the two scripts running independently one from the other.

PyGame uses events so I created a custom event in the code called REFRESHEVENT that fires off every 5 seconds, so that I don't overload the API for nothing. This gets the current playback status from the Spotify API and compares the covert art URL with the previous one : if it's the same, it doesn't do anything, but if the URL has changed since last time, the cover is downloaded and saved.

While this worked for getting the original, square album art, I wanted to make it a bit more record player like : I created a mask and the start of the program with only a circle on it - the mask gets applied to the cover art and it now looks circular. On top of that, I draw a circle with no fill and with a black outline at 80% of the "record" size, in order to generate a kind of border.

Step 13: Putting It All Together (again)

While putting the enclosure back together, I realized that the USB cable from the Raspberry Pi to the Arduino made it so the screen was not centered anymore, but rather pushed to the side by the presence of the USB plug. I just had to tweak the code a bit so that the cover would get drawn on the side of the screen rather than in the center. I also put a piece of paper underneath the Rapsberry Pi so that the PCB wouldn't touch the metal case of the record player.

Step 14: Finishing the Enclosure

Now for the finishing touches : I created a vector file with icons for each of the rotary buttons in order to know which one controls which room. I then cut said vector file on a laser cutter using 3mm acrylic protected with some masking tape, so that I could attach this to the record player. I tried both a white and a black version.

In order to add more contrast to the icons, I then used a chalk pen on the black version and a red, wipe erase pen on the white version to color the engraving before removing the masking tape. While this worked pretty well with the chalk pen on the black version, the dry-erase pen on the white version proved harder to clean, so I kept the black one.

I inserted on top of the rotary buttons and put back the metal button tops

I also added a circle of black paper on top of the screen so that the border isn't so obvious, as the hole is much bigger than the screen I ended up using (I had initiall planned to use a 7 inch screen)



Step 15: Autostarting the Scripts

On the software side, we want both my scripts to run at startup and to restart if there was an error.

I used PM2, which is mainly known for Node.js but also supports Python files :

npm install pm2@latest -g

In order to have pm2 run at startup, type

pm2 startup

This will give you a command depending on your OS, that you should copy and paste in your terminal. In my case it was

sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u pi --hp /home/pi


PM2 is now set to run at startup. The next step is to launch our two scripts :

pm2 start snapcastControl.py
pm2 start Documents/RecordPlayer/spotify.py --name SpotifyCover

If you now type

pm2 list

You should see your two processes running. Since that's the state we want pm2 to default to, type

pm2 save


The two scripts will now be launched at startup and whenever they fail. To test that, reboot the server and check that the two processes do start.

I also added a line to the PyGame script so that the scripts starts in fullscreen and the cursor icon is hidden after a few seconds.

 display_surface = pygame.display.set_mode((target_w,target_h), pygame.FULLSCREEN)

Step 16: Closing Thoughts

As you might have guessed from the icons I put on the front size of the record player, I already have a few next steps planned for this project :

  • there are two toggle buttons on the left of the rotary buttons, so I would like to use the left one to switch the playing device from my phone or computer to my audio-system, so that I can have my music in every room when I get home. I'd also like to use the second one to toggle playlist randomness on the Spotify API
  • Right now the Spotify API doesn't send a cover when you're listening to a podcast episode, so I'd like to see if I can at least get it to send me the name of the podcast so that I can display it on the screen
Trash to Treasure Contest

Participated in the
Trash to Treasure Contest