A Spotify Connect player written in CPP targeting, but not limited to embedded devices (ESP32).
Currently in state of rapid development.
Only to be used with premium spotify accounts
Summary:
- cmake (version 3.0 or higher)
- gcc / clang for the CLI target
- esp-idf for building for the esp32
- portaudio for playback on MacOS
- downloaded submodules
- golang (1.16)
- protoc
- on Linux you will additionally need:
libasound
andlibavahi-compat-libdnssd
This project utilizes submodules, please make sure you are cloning with the --recursive
flag or use git submodule update --init --recursive
.
This library uses nanopb to generate c files from protobuf definitions. Nanopb itself is included via submodules, but it requires a few external python libraries to run the generators.
To install them you can use pip:
$ sudo pip3 install protobuf grpcio-tools
(You probably should use venv, but I am no python developer)
To install avahi and asound dependencies on Linux you can use:
$ sudo apt-get install libavahi-compat-libdnssd-dev libasound2-dev
The cli target is used mainly for testing and development purposes, as of now it has the same features as the esp32 target.
# navigate to the targets/cli directory
$ cd targets/cli
# create a build directory and navigate to it
$ mkdir -p build && cd build
# use cmake to generate build files
$ cmake ..
# compile
$ make
See running the CLI for information on how to run cspot on a desktop computer.
After building the app, the only thing you need to do is to run it through CLI.
$ ./cspotcli
Now open a real Spotify app and you should see a cspot device on your local network. Use it to play audio.
cspot
is meant to be used as a lightweight C++ library for playing back Spotify music and receive control notifications from Spotify connect.
It exposes an interface for starting the communication with Spotify servers and expects the embedding program to provide an interface for playing back raw audio samples (AudioSink
).
You can view the cspot-cli
program for a reference on how to include cspot in your program. It provides a few audio sinks for various platforms and uses:
ALSAAudioSink
- Linux, requireslibasound
PortAudioSink
- MacOS (PortAudio also supports more platforms, but we currently use it only on MacOS), requires the PortAudio libraryNamedPipeAudioSink
- all platforms, writes to a file/FIFO pipe calledoutputFifo
which can later be played back by FFmpeg. Used mainly for testing and development.
Additionaly the following audio sinks are implemented for the esp32 target:
ES9018AudioSink
- provides playback via a ES9018 DAC connected to the ESP32AC101AudioSink
- provides playback via the AC101 DAC used in cheap ESP32 A1S audiokit boards, commonly found on aliexpress.PCM5102AudioSink
- provides playback via a PCM5102 DAC connected to the ESP32, commonly found in the shape of small purple modules at various online retailers. Wiring can be configured in the sink and defaults to:- SCK to Ground
- BCK to PGIO27
- DIN to GPIO25
- LCK to GPIO32
- GND to Ground
- VIN to 3.3V (but supposedly 5V tolerant)
- TODO: internal esp32 DAC for crappy quality testing.
You can also easily add support for your own DAC of choice by implementing your own audio sink. Each new audio sink must implement the void feedPCMFrames(std::vector<uint8_t> &data)
method which should accept stereo PCM audio data at 44100 Hz and 16 bits per sample. Please note that the sink should somehow buffer the data, because playing it back may result in choppy audio.
An audio sink can optionally implement the void volumeChanged(uint16_t volume)
method which is called everytime the user changes the volume (for example via Spotify Connect). If an audio sink implements it it should set softwareVolumeControl
to false
in its consructor to let cspot know to disable the software volume adjustment. Properly implementing external volume control (for example via dedicated hardware) will result in a better playback quality since all the dynamic range is used to encode the samples.
The embedding program should also handle caching the authentication data, so that the user does not have to authenticate via the local network (Zeroconf) each time cspot is started. For reference on how to do it please refer to the cspot-cli
target (It stores the data in authBlob.json
).
The connection with Spotify servers to play music and recieve control information is pretty complex. First of all an access point address must be fetched from Spotify (ApResolve
fetches the list from http://apresolve.spotify.com/). Then a PlainConnection
with the selected Spotify access point must be established. It is then upgraded to an encrypted ShannonConnection
.