Sonart (controlling Sonos and Spotify using album art)
I have Sonos speakers in my flat. In combination with Spotify, this allows me to play just about whatever music I want, in whichever rooms of the flat, simply by opening the Sonos app on my phone and searching for the artist. The biggest challenge is deciding what to listen to.
I also have a record player in my flat, and a number of vinyl records, in nice sleeves with nice cover art. Even though putting on a record is much less convenient than opening Sonos on my phone, I still get a lot from having them around - primarily the ability to wonder over and flick through a big pile of album art to help decide what I want to listen to: something which is lost by music streaming services.
I had an idea for how I could combine the two; keep the convenience and low cost of music streaming, but also retain a physical presence for my music collection (in my case a pile of records).
The solution is built using a webcam and some Python code (using SimpleCV, SoCo libraries) - take a look at it in action:
Click permalink below to get to the code...
As you'll see below, the code is fairly concise. The basic premise is as follows:
- Load in a configuration file. This lists the name, album cover and Spotify track/album reference for each record cover.
- Pull images from a camera (in my case I'm using an old iPhone running ipCam. (When I visit 'http://192.168.1.65/image.jpg' on my network, it returns an image from the camera.)
- Using the SimpleCV/OpenCV keypoint matching functionality, test the retrieved image to see if there is a match to one of the cover art images.
- If there is a match, use the SoCo library to play the corresponding referenced track or album.
- Wait half a second, and try again.
Here's the code:
# In[1]:
from SimpleCV import *
import soco
import json
disp = Display(displaytype='notebook')
sonos = soco.SoCo('192.168.1.82')
# In[15]:
import warnings
warnings.filterwarnings("ignore")
# In[13]:
file_location = r'C:\Users\will\Documents\Projects\Sonart'
camera_location = r'http://192.168.1.65/image.jpg'
timer_duration = 0.5
# In[3]:
# open the config file and read in the images
with open(file_location + r"\config.json", 'r+') as f:
config = json.load(f)
for artist in config:
image_location = os.path.join(file_location, 'Covers', artist['cover'])
artist['img'] = Image(str(image_location))
# In[18]:
from IPython.display import clear_output
while True:
try:
img = Image(camera_location)
except:
print("Cannot acquire image. Quitting")
break
for artist in config:
match = img.findKeypointMatch(artist['img'], minDist=0.15)
if match is not None:
clear_output()
print('Recognised {0}'.format(artist['artist']))
sonos.play_uri(artist['metadata']['uri'], artist['metadata']['metadata'], artist['metadata']['title'])
time.sleep(timer_duration)
I intend to get this cleaned up into a repo soon, including functions to help build the config file. For now though, if you want to try it out, I suggest you start by pulling the sightmachine/simpleCV docker image, and running the code from there.
The configuration file which is loaded includes three attributes for each config item: the name, the name of the cover art file on disk, and a stored reference to the Sonos/Spotify track metadata. I got the reference by putting on the right track using the normal Sonos controller, and then calling 'sonos.get_current_track_info()' and saving it to the config file.
