I’m a long time Spotify user right now. I decided to opt for a unlimited account due to the price. The main issue : Spotify for Linux is a closed source software, doesn’t allow the user to change the sound card and I want to use my almost finished USB DAC.
In fact, if you use Jackd (and perhaps Pulse) you can change the sound card used by Spotify, but this is overkill for my needs (mainly cause, I use Spotify on a litte old netbook and Jackd is a nightmare to configure)
After some strace dump, Alsa (libasound2) source reading, I decided to fix it by myself. Let’s go for some Monkey Patching. Monkey patching on closed source binary isn’t that easy, the hard step : find the right function to hack. Here the Alsa tutorial come to rescue (ldd of course too). So we need to modify the snd_pcm_open call.
Let’s give it a try : First we need to dynamically re-route this call to a wrapper. The easier way to do this, is to build a special library and to force Spotify to use the wrapper with an LD_PRELOAD hack. (see code below). In fact, Spotify call the snd_pcm_open with name=”default” which is the default sound card, so we can change this to “usb” for example, and fix the .asoundrc to point “usb” sound card to the right hardware card.
That’s enough, let’s code :
#define _GNU_SOURCE #include <dlfcn.h> #include <stdio.h> #include <alsa/asoundlib.h> static int (*wrap_snd_pcm_open)(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode) = NULL; void * wrap(void * func,char *name) { char *msg; if (func == NULL) { func = dlsym(RTLD_NEXT, name); fprintf(stderr, "** wrapping %s => %p\n", name,func); if ((msg = dlerror()) != NULL) { fprintf(stderr, "** %s: dlopen failed : %s\n", name,msg); exit(1); } else { fprintf(stderr, "** %s: wrapping done\n",name); } } return func; } int snd_pcm_open(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode) { int temp; sprintf(name,"usb"); wrap_snd_pcm_open = wrap(wrap_snd_pcm_open,"snd_pcm_open"); temp = wrap_snd_pcm_open(pcm,name,stream,mode); fprintf(stderr, "** Calling snd_pcm_open for path:[%s] => fd:[%d]\n",name,temp); fflush(stderr); return temp; }
As you can see in the code, I use a generic wrapper function because during my code session, I used this function to re-route some other functions. To build the library :
gcc -g -fPIC -shared -Wl,-soname,preload.so -ldl -o preload.so main.c
Load the lib, with export LD_PRELOAD=./preload.so before running Spotify. Spotify (and all other software using Alsa) will use “usb” as default sound card.
Note: C reader will note that name is a const char pointer, and so we can’t change it, but even if gcc raise a big warning, the code do trick fine.
Enjoy good quality sound even w/ closed source
This post has some interesting infos, if one day you change your mind about PulseAudio :)
http://tormod.landet.net/2009/06/19/streaming-audio-from-spotify-on-linux-to-squeezebox/
Dude, this is an awesome solution, took me 1 minute to fix. I used to use pulseaudio, however, bluetooth + pulseaudio is broken more often than it works. Thanks a lot
Works like a charm, except “usb” did not work and I had to use “sysdefault:CARD=C10″ instead for my Cambridge DacMagic Plus (Cambridge Audio USB Audio 1.0) running Slackware 14 (-current)
The USB is an alias for soundcard. I fix this in the asoundrc. In fact, I need to resample the output to 48kHz.
Awesome, easy fix! Thanks!! I just replaced `usb` with `DragonFly` to get it working for my USB DAC.