Created
February 4, 2015 19:53
-
-
Save dradtke/ee4bf3e6bb3bed3cd21a to your computer and use it in GitHub Desktop.
A first attempt to stream Spotify music through Allegro.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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