Software

The Smart Music player uses song characteristics and the current weather to determine what songs to play at a given time. This is accomplished by using data provided by the Spotify API, several python libaries, command line tools, and a few bash scripts. All the links for the third party-softwares we used can be found in the 'Appendices' section. A block diagram of the overall software structure and the libraries that were used for this project can be seen below.

High Level Software Block Diagram

The purple boxes in the diagram are third-party python libraries. Spotipy is a python library allows users to conveniently access data from the Spotify Web API. Pywapi (Python Weather API) is another Python library that allows users to access weather data from a variety of different sources. The algorithm takes weather data and a large selection of songs from Spotify as an input, and it outputs a subset of this large selection depending on the weather the time the algorithm was run at. The URIs (unique identifiers given to each song on Spotify) of the 'weather-appropriate' songs are wirelessly transmitted via a text file to the device that will be used to actually play the songs. Shpotify is a command line utility that can be used to control the Spotify app. The transmitted URI was read into Shpotify using a bash script (discussed in the 'Wireless Protocol' subsection below) and then the song was played on Spotify.

Spotify Web API & Spotipy

Spotify has a classification system where they identify musical characteristics for a given song and assign these characteristics a numerical value. These characteristics are provided by the Spotify web API once a developer's application is registered on the Spotify website. Spotipy (download from Github & build) is a python library that allows users to access these characteristics without having to create a server-side web application. The only thing needed to register an application on the Spotify website is a Spotify account (free or premium). Once the application has been registered, the user will be provided with a spotify client ID and a spotify client secret. In order for Spotipy (the python library) to successfully access music characteristic information, it needs to know the client ID, client secret, and the redirect URI (this is the URL the web application would be redirected to, but since this project did not require a web application, we used 'http://localhost:8888/callback'as the redirect URI). To avoid having to input these three parameters every time the Spotipy library is used, they should be saved as environmental variables in .bashrc:

export SPOTIPY_CLIENT_ID = [insert client ID here]

export SPOTIPY_CLIENT_SECRET = [insert client secret here]

export SPOTIPY_REDIRECT_URI = http://localhost:8888/callback

Pywapi (Python Weather API)

Pywapi (sudo apt-get install 'pywapi') is a python library that can was used to access weather from NOAA. Calling 'pywapi.get_weather_from_noaa('KITH')' returns the following json string:

{'icon_url_name': u'ra.png', 'weather': u'Light Rain', 'windchill_c': u'13', 'ob_url': u'http://www.weather.gov/data/METAR/KITH.1.txt', 'windchill_f': u'56', 'pressure_mb': u'1019.6', 'dewpoint_string': u'42.1 F (5.6 C)', 'suggested_pickup_period': u'60', 'dewpoint_f': u'42.1', 'location': u'Ithaca, Ithaca Tompkins Regional Airport, NY', 'dewpoint_c': u'5.6', 'latitude': u'42.49083', 'wind_mph': u'18.4', 'temp_f': u'59.0', 'temp_c': u'15.0', 'pressure_string': u'1019.6 mb', 'windchill_string': u'56 F (13 C)', 'station_id': u'KITH', 'wind_string': u'from the South at 18.4 gusting to 23.0 MPH (16 gusting to 20 KT)', 'pressure_in': u'30.11', 'temperature_string': u'59.0 F (15.0 C)', 'two_day_history_url': u'http://www.weather.gov/data/obhistory/KITH.html', 'wind_dir': u'South', 'wind_degrees': u'160', 'icon_url_base': u'http://forecast.weather.gov/images/wtf/small/', 'wind_gust_mph': u'23.0', 'observation_time': u'Last Updated on May 21 2017, 7:56 pm EDT', 'longitude': u'-76.45833', 'suggested_pickup': u'15 minutes after the hour', 'relative_humidity': u'54', 'observation_time_rfc822': u'Sun, 21 May 2017 19:56:00 -0400'}

'KITH' is the identifier NOAA uses for Ithaca-Tompkins Regional Airport, which is the location it takes the weather from. Note that in Python, a json string can be parsed like a dictionary; for instance, calling 'print weather['temp_f'] will print the current weather in degrees Fahrenheit.

final_find_track.py & algorithm

final_find_track.py is the python script that takes in data from pywapi and spotipy and outputs the URI of the weather-appropriate song to be played. If final_find_track.py is run without any arguments, spotipy will get song characteristcs and sort songs according to weather from the songs in a playlist called 'DJ_Spotify_Playlist.' The other alternative is to run final_find_track.py with command-line arguments; running 'python final_find_track.py artist "tame impala"' will instead obtain song characteristics and sort songs according to weather from the top 8 songs by the artist Tame Impala from Spotify. Spotipy uses the sp.search() method to search for playlists and songs by artists on Spotify. sp.search() returns a json string for each song that contains the URI of the song, the countries it is available to be played on Spotify, who added the song to the playlist, what album the song is on, but no information about song characteristics. The main purpose of calling sp.search() is to acquire the URIs of the songs that will eventually be sorted. These URIs are stored in an array called 'tids' (short for track IDs). The audio features method is then called on the array of URIs to get the song characteristics (sp.audio_features(tids)); the song characteristics are returned as json strings, which are then stored in an array called 'features.' An example of song characteristics for the song 'Island in the Sun' by Weezer can be seen below:

{ "track_href": "https://api.spotify.com/v1/tracks/2MLHyLy5z5l5YRp7momlgw", "analysis_url": "https://api.spotify.com/v1/audio-analysis/2MLHyLy5z5l5YRp7momlgw", "energy": 0.778, "liveness": 0.144, "tempo": 114.647, "speechiness": 0.0273, "uri": "spotify:track:2MLHyLy5z5l5YRp7momlgw", "acousticness": 0.0108, "instrumentalness": 0.00654, "time_signature": 4, "danceability": 0.646, "key": 4, "duration_ms": 200307, "loudness": -6.222, "valence": 0.784, "type": "audio_features", "id": "2MLHyLy5z5l5YRp7momlgw", "mode": 0 }

Of these song characteristics, we decided to focus on 'instrumentalness,' 'danceability,' 'acousticness,' 'energy,' 'liveness,' and 'speechiness' when it came to sorting songs by the weather. From our personal preferences, we know that when the weather is warmer, we like to listen to more danceable and lively music than when the weather is cooler, so we had the algorithm sort for songs that had higher values in in energy, liveness, danceability, and speechiness, and lower values in acousticness and instrumentalness in order to reflect our preferences. Thus, the inputs to the algorithm are the 'features' array (containing dictionaries of song characteristics from all the songs in the DJ_Spotify_Playlist) and the weather. The outputs of the algorithm are the URIs of the subset of songs from the DJ_Spotify_Playlist that meet the warm-weather criteria and they are saved in an array called 'selected_songs.'

Once all the songs have been sorted and the appropriate ones have been saved to the 'selected_songs' array, final_find_track.py enters an infinite while loop where it waits for inputs from buttons on the TFT to write a new song's URI to 'send_song.txt.' Button #27 writes a new URI to the file (essentially moving forward through the selected songs playlist), and button #23 is the 'dislike' button, which can be used to flag a song so that it is not played the next time a the selected songs are looped through. autoplay.py is similar to final_find_track.py except for the fact that it writes a new URI to 'send_song.txt' when the current song has finished playing, so that the user does not have to hit button #27 in order to listen to another song.

Wireless Protocol

The algorithm outputs a song by writing its URI to 'send_song.txt'. Thus, every time it is time to play a new song, 'send_song.txt' is completely overwritten so that it only contains the URI of the new song to be played. Every time 'send_song.txt' is updated (i.e. it is written to by the algorithm), it is read by a bash script on the Pi called 'orig_test' which uses the inotifywait utility to register when a file is updated. Once a file upate has been detected, 'orig_test' uses scp and sshpass to send 'send_song.txt' to the laptop where the song will eventually be played from. On the laptop, there are two more bash scripts used to detect when a new song URI has been sent over. 'play_again' is a bash script that uses fswatch (another utility that monitors file system changes) to see when 'send_song.txt' has been modified. 'send_song.txt' is sent over from the Pi to a specific location onto the laptop, thus every time 'send_song.txt' gets sent over from the Pi, it overwrites the existing 'send_song.txt' on the laptop. fswatch detects when this has happened, and calls another bash script called 'play_song.' 'play_song' then reads the newly updated 'send_song.txt' and passes the URI of the song to a Shpotify command so that it can be played using the Spotify app on the laptop. All the bash scripts can be found in the 'Appendices' section. Additionally, in order for the bash scripts to successfully transmit 'senx_song.txt', both the Pi and the music-playing device (the laptop in this case) must be on the same Wifi network. These scripts haven't been tested on devices that are located on different networks.

Both inotifywait and fswatch are utilities that keep track of filesystem changes. inotifywait was recommended was recommended for Linux systems, and fswatch was recommended to be used on OSX. inotifywait is installed by running 'sudo apt-get install inotifywait-tools' in the terminal and fswatch is installed by running 'brew install fswatch' (after first installing the Homebrew, which is a package manager for macOS). sshpass is a command line utility that saves a device's password so that the user does not need to input it every time they try to ssh into another device. Initially, every time the script 'orig_test' transmitted 'send_song.txt' from the Pi to the laptop, the laptop's password had to be physically typed into the terminal on the Pi, which did not allow for a seamless user experience. Thus, sshpass was used to store the password of the laptop in the 'orig_test' script, which prevented the need to type in the password every time the script ran. sshpass can be installed by running 'sudo apt-get install sshpass.'

Shpotify

As mentioned earlier, Shpotify is a command line utility that is used to control Spotify via the command line. It must be installed on the device that will actually be playing the songs from the Spotify application (a prerequisite is that the Spotify app must be installed on this device as well). Shpotify can be installed by running 'brew install shpotify' or by following the installation instructions on its Github repo. The Shpotify command for playing a song in the Spotify application is 'spotify play uri [insert song URI here]'; this is the command that gets run in the 'play_song' bash script.