Skip to content

Instantly share code, notes, and snippets.

@dradtke
Created February 4, 2015 19:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dradtke/ee4bf3e6bb3bed3cd21a to your computer and use it in GitHub Desktop.
Save dradtke/ee4bf3e6bb3bed3cd21a to your computer and use it in GitHub Desktop.
A first attempt to stream Spotify music through Allegro.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <libspotify/api.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_audio.h>
#define DEBUG 0 // change to 1 for debugging information
extern const uint8_t g_appkey[];
extern const size_t g_appkey_size;
extern const char *username;
extern const char *password;
static bool g_logged_in;
static bool g_playing;
static bool g_running;
static ALLEGRO_AUDIO_STREAM *stream;
static ALLEGRO_EVENT_SOURCE sp_notify_event_source;
ALLEGRO_EVENT sp_notify_event;
void debug(const char *format, ...)
{
if (!DEBUG)
return;
va_list argptr;
va_start(argptr, format);
vprintf(format, argptr);
printf("\n");
}
void play(sp_session *session, sp_track *track)
{
sp_error error = sp_session_player_load(session, track);
if (error != SP_ERROR_OK) {
fprintf(stderr, "Error: %s\n", sp_error_message(error));
exit(1);
}
sp_session_player_play(session, 1);
al_emit_user_event(&sp_notify_event_source, &sp_notify_event, NULL);
}
static void on_search_complete(sp_search *search, void *userdata)
{
debug("callback: on_search_complete");
sp_error error = sp_search_error(search);
if (error != SP_ERROR_OK) {
fprintf(stderr, "Error: %s\n", sp_error_message(error));
exit(1);
}
int num_tracks = sp_search_num_tracks(search);
if (num_tracks == 0) {
fprintf(stderr, "Sorry, couldn't find that track. =/\n\n");
sp_search_release(search);
g_playing = 0;
return;
}
sp_track *track = sp_search_track(search, 0);
play((sp_session*)userdata, track);
sp_search_release(search);
}
static void on_login(sp_session *session, sp_error error)
{
debug("callback: on_login");
if (error != SP_ERROR_OK) {
fprintf(stderr, "login failed: %s\n", sp_error_message(error));
exit(2);
}
g_logged_in = 1;
}
static void on_notify_main_thread(sp_session *session)
{
debug("callback: on_notify_main_thread");
al_emit_user_event(&sp_notify_event_source, &sp_notify_event, NULL);
}
static int on_music_delivery(sp_session *session, const sp_audioformat *format, const void *frames, int num_frames)
{
debug("callback: on_music_delivery");
debug(" sample type: %d", format->sample_type);
debug(" sample rate: %d", format->sample_rate);
debug(" channels: %d", format->channels);
void *buffer = al_get_audio_stream_fragment(stream);
if (buffer == NULL) {
return 0;
}
int max = (num_frames < 2048 ? num_frames : 2048);
memcpy(buffer, frames, max);
al_set_audio_stream_fragment(stream, buffer);
return max;
}
static void on_end_of_track(sp_session *session)
{
debug("callback: on_end_of_track");
}
static void on_log(sp_session *session, const char *data)
{
debug("spotify: %s\n", data);
}
static sp_session_callbacks session_callbacks = {
.logged_in = &on_login,
.notify_main_thread = &on_notify_main_thread,
.music_delivery = &on_music_delivery,
.log_message = &on_log,
.end_of_track = &on_end_of_track
};
static sp_session_config spconfig = {
.api_version = SPOTIFY_API_VERSION,
.cache_location = "/tmp/spot",
.settings_location = "/tmp/spot",
.application_key = g_appkey,
.application_key_size = 0, // set in main()
.user_agent = "spot",
.callbacks = &session_callbacks,
NULL
};
// Initialize the Spotify session.
sp_session *init_spotify()
{
sp_error error;
sp_session *session;
spconfig.application_key_size = g_appkey_size;
if ((error = sp_session_create(&spconfig, &session)) != SP_ERROR_OK) {
fprintf(stderr, "unable to create session: %s\n", sp_error_message(error));
exit(1);
}
g_logged_in = 0;
sp_session_login(session, username, password, 0, NULL);
return session;
}
// Initialize Allegro.
void init_allegro()
{
if (!al_init()) {
printf("failed to start allegro!\n");
exit(1);
}
if (!al_install_audio()) {
printf("failed to install audio subsystem!\n");
exit(1);
}
if (!al_reserve_samples(1)) {
printf("failed to reserve an audio sample!\n");
exit(1);
}
al_init_user_event_source(&sp_notify_event_source);
sp_notify_event.user.type = ALLEGRO_GET_EVENT_TYPE('S', 'P', 'O', 'T');
ALLEGRO_CHANNEL_CONF chan_conf = ALLEGRO_CHANNEL_CONF_2;
ALLEGRO_AUDIO_DEPTH depth = ALLEGRO_AUDIO_DEPTH_INT16;
int bytes_per_fragment = 2048; // no idea what I'm doing here
int fragment_count = 2;
int freq = 2048; // again, no idea
int sample_size = al_get_channel_count(chan_conf) * al_get_audio_depth_size(depth);
int samples = bytes_per_fragment / sample_size;
//int buffer_size = bytes_per_fragment * fragment_count;
stream = al_create_audio_stream(fragment_count, samples, freq, depth, chan_conf);
al_attach_audio_stream_to_mixer(stream, al_get_default_mixer());
}
void play_song(sp_session *session, char *artist, char *track)
{
char q[4096];
sprintf(q, "artist:\"%s\" track:\"%s\"", artist, track);
sp_search_create(session, q, 0, 1, 0, 0, 0, 0, 0, 0, SP_SEARCH_STANDARD, &on_search_complete, session);
}
int main(void)
{
sp_session *session = init_spotify();
play_song(session, "Green Day", "Welcome to Paradise");
init_allegro();
ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue();
al_register_event_source(event_queue, &sp_notify_event_source);
// main loop
g_running = 1;
int next_timeout = 0;
// start the ball rolling
sp_session_process_events(session, &next_timeout);
ALLEGRO_EVENT event;
while (g_running) {
al_wait_for_event(event_queue, &event);
switch (event.type) {
case ALLEGRO_GET_EVENT_TYPE('S', 'P', 'O', 'T'):
// process Spotify events
do {
sp_session_process_events(session, &next_timeout);
} while (next_timeout == 0);
break;
default:
break;
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment