Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <errno.h>
- #include <libgen.h>
- #include <pthread.h>
- #include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <spotify/api.h>
- #include "audio.h"
- /* --- Data --- */
- extern const uint8_t g_appkey[];
- extern const size_t g_appkey_size;
- static audio_fifo_t g_audiofifo;
- static pthread_mutex_t g_notify_mutex;
- static pthread_cond_t g_notify_cond;
- static int g_notify_do;
- static int g_playback_done;
- static sp_session *g_sess;
- static sp_playlist *g_jukeboxlist;
- const char *g_listname;
- static int g_remove_tracks = 0;
- static sp_track *g_currenttrack;
- static int g_track_index;
- static void try_jukebox_start(void)
- {
- sp_track *t;
- if (!g_jukeboxlist)
- return;
- if (!sp_playlist_num_tracks(g_jukeboxlist)) {
- fprintf(stderr, "jukebox: No tracks in playlist. Waiting\n");
- return;
- }
- if (sp_playlist_num_tracks(g_jukeboxlist) < g_track_index) {
- fprintf(stderr, "jukebox: No more tracks in playlist. Waiting\n");
- return;
- }
- t = sp_playlist_track(g_jukeboxlist, g_track_index);
- if (g_currenttrack && t != g_currenttrack) {
- /* Someone changed the current track */
- audio_fifo_flush(&g_audiofifo);
- sp_session_player_unload(g_sess);
- g_currenttrack = NULL;
- }
- if (!t)
- return;
- if (sp_track_is_loaded(t) == 0)
- return;
- if (g_currenttrack == t)
- return;
- g_currenttrack = t;
- printf("jukebox: Now playing \"%s\"...\n", sp_track_name(t));
- fflush(stdout);
- sp_session_player_load(g_sess, t);
- sp_session_player_play(g_sess, 1);
- }
- /* -------------------------- PLAYLIST CALLBACKS ------------------------- */
- static void tracks_added(sp_playlist *pl, const sp_track **tracks,
- int num_tracks, int position, void *userdata)
- {
- if (pl != g_jukeboxlist)
- return;
- printf("jukebox: %d tracks were added\n", num_tracks);
- fflush(stdout);
- try_jukebox_start();
- }
- static void tracks_removed(sp_playlist *pl, const int *tracks,
- int num_tracks, void *userdata)
- {
- int i, k = 0;
- if (pl != g_jukeboxlist)
- return;
- for (i = 0; i < num_tracks; ++i)
- if (tracks[i] < g_track_index)
- ++k;
- g_track_index -= k;
- printf("jukebox: %d tracks were removed\n", num_tracks);
- fflush(stdout);
- try_jukebox_start();
- }
- static void tracks_moved(sp_playlist *pl, const int *tracks,
- int num_tracks, int new_position, void *userdata)
- {
- if (pl != g_jukeboxlist)
- return;
- printf("jukebox: %d tracks were moved around\n", num_tracks);
- fflush(stdout);
- try_jukebox_start();
- }
- static void playlist_renamed(sp_playlist *pl, void *userdata)
- {
- const char *name = sp_playlist_name(pl);
- if (!strcasecmp(name, g_listname)) {
- g_jukeboxlist = pl;
- g_track_index = 0;
- try_jukebox_start();
- } else if (g_jukeboxlist == pl) {
- printf("jukebox: current playlist renamed to \"%s\".\n", name);
- g_jukeboxlist = NULL;
- g_currenttrack = NULL;
- sp_session_player_unload(g_sess);
- }
- }
- static sp_playlist_callbacks pl_callbacks = {
- .tracks_added = &tracks_added,
- .tracks_removed = &tracks_removed,
- .tracks_moved = &tracks_moved,
- .playlist_renamed = &playlist_renamed,
- };
- /* -------------------- PLAYLIST CONTAINER CALLBACKS --------------------- */
- static void playlist_added(sp_playlistcontainer *pc, sp_playlist *pl,
- int position, void *userdata)
- {
- sp_playlist_add_callbacks(pl, &pl_callbacks, NULL);
- if (!strcasecmp(sp_playlist_name(pl), g_listname)) {
- g_jukeboxlist = pl;
- try_jukebox_start();
- }
- }
- static void playlist_removed(sp_playlistcontainer *pc, sp_playlist *pl,
- int position, void *userdata)
- {
- sp_playlist_remove_callbacks(pl, &pl_callbacks, NULL);
- }
- static sp_playlistcontainer_callbacks pc_callbacks = {
- .playlist_added = &playlist_added,
- .playlist_removed = &playlist_removed,
- };
- /* --------------------------- SESSION CALLBACKS ------------------------- */
- static void logged_in(sp_session *sess, sp_error error)
- {
- sp_playlistcontainer *pc = sp_session_playlistcontainer(sess);
- int i;
- if (SP_ERROR_OK != error) {
- fprintf(stderr, "jukebox: Login failed: %s\n",
- sp_error_message(error));
- exit(2);
- }
- printf("jukebox: Looking at %d playlists\n", sp_playlistcontainer_num_playlists(pc));
- for (i = 0; i < sp_playlistcontainer_num_playlists(pc); ++i) {
- sp_playlist *pl = sp_playlistcontainer_playlist(pc, i);
- sp_playlist_add_callbacks(pl, &pl_callbacks, NULL);
- if (!strcasecmp(sp_playlist_name(pl), g_listname)) {
- g_jukeboxlist = pl;
- try_jukebox_start();
- }
- }
- if (!g_jukeboxlist) {
- printf("jukebox: No such playlist. Waiting for one to pop up...\n");
- fflush(stdout);
- }
- }
- static void notify_main_thread(sp_session *sess)
- {
- pthread_mutex_lock(&g_notify_mutex);
- g_notify_do = 1;
- pthread_cond_signal(&g_notify_cond);
- pthread_mutex_unlock(&g_notify_mutex);
- }
- static int music_delivery(sp_session *sess, const sp_audioformat *format,
- const void *frames, int num_frames)
- {
- audio_fifo_t *af = &g_audiofifo;
- audio_fifo_data_t *afd;
- size_t s;
- if (num_frames == 0)
- return 0; // Audio discontinuity, do nothing
- pthread_mutex_lock(&af->mutex);
- /* Buffer one second of audio */
- if (af->qlen > format->sample_rate) {
- pthread_mutex_unlock(&af->mutex);
- return 0;
- }
- s = num_frames * sizeof(int16_t) * format->channels;
- afd = malloc(sizeof(audio_fifo_data_t) + s);
- memcpy(afd->samples, frames, s);
- afd->nsamples = num_frames;
- afd->rate = format->sample_rate;
- afd->channels = format->channels;
- TAILQ_INSERT_TAIL(&af->q, afd, link);
- af->qlen += num_frames;
- pthread_cond_signal(&af->cond);
- pthread_mutex_unlock(&af->mutex);
- return num_frames;
- }
- static void end_of_track(sp_session *sess)
- {
- pthread_mutex_lock(&g_notify_mutex);
- g_playback_done = 1;
- pthread_cond_signal(&g_notify_cond);
- pthread_mutex_unlock(&g_notify_mutex);
- }
- static void metadata_updated(sp_session *sess)
- {
- try_jukebox_start();
- }
- static void play_token_lost(sp_session *sess)
- {
- audio_fifo_flush(&g_audiofifo);
- if (g_currenttrack != NULL) {
- sp_session_player_unload(g_sess);
- g_currenttrack = NULL;
- }
- }
- static sp_session_callbacks session_callbacks = {
- .logged_in = &logged_in,
- .notify_main_thread = ¬ify_main_thread,
- .music_delivery = &music_delivery,
- .metadata_updated = &metadata_updated,
- .play_token_lost = &play_token_lost,
- .log_message = NULL,
- .end_of_track = &end_of_track,
- };
- static sp_session_config spconfig = {
- .api_version = SPOTIFY_API_VERSION,
- .cache_location = "tmp",
- .settings_location = "tmp",
- .application_key = g_appkey,
- .application_key_size = 0, // Set in main()
- .user_agent = "spotify-jukebox-example",
- .callbacks = &session_callbacks,
- NULL,
- };
- /* ------------------------- END SESSION CALLBACKS ----------------------- */
- static void track_ended(void)
- {
- int tracks = 0;
- if (g_currenttrack) {
- g_currenttrack = NULL;
- sp_session_player_unload(g_sess);
- if (g_remove_tracks) {
- sp_playlist_remove_tracks(g_jukeboxlist, &tracks, 1);
- } else {
- ++g_track_index;
- try_jukebox_start();
- }
- }
- }
- static void usage(const char *progname)
- {
- fprintf(stderr, "usage: %s -u <username> -p <password> -l <listname> [-d]\n", progname);
- fprintf(stderr, "warning: -d will delete the tracks played from the list!\n");
- }
- int main(int argc, char **argv)
- {
- sp_session *sp;
- sp_error err;
- int next_timeout = 0;
- const char *username = NULL;
- const char *password = NULL;
- int opt;
- while ((opt = getopt(argc, argv, "u:p:l:d")) != EOF) {
- switch (opt) {
- case 'u':
- username = optarg;
- break;
- case 'p':
- password = optarg;
- break;
- case 'l':
- g_listname = optarg;
- break;
- case 'd':
- g_remove_tracks = 1;
- break;
- default:
- exit(1);
- }
- }
- if (!username || !password || !g_listname) {
- usage(basename(argv[0]));
- exit(1);
- }
- audio_init(&g_audiofifo);
- /* Create session */
- spconfig.application_key_size = g_appkey_size;
- err = sp_session_init(&spconfig, &sp);
- if (SP_ERROR_OK != err) {
- fprintf(stderr, "Unable to create session: %s\n",
- sp_error_message(err));
- exit(1);
- }
- g_sess = sp;
- pthread_mutex_init(&g_notify_mutex, NULL);
- pthread_cond_init(&g_notify_cond, NULL);
- sp_playlistcontainer_add_callbacks(
- sp_session_playlistcontainer(g_sess),
- &pc_callbacks,
- NULL);
- sp_session_login(sp, username, password);
- pthread_mutex_lock(&g_notify_mutex);
- for (;;) {
- if (next_timeout == 0) {
- while(!g_notify_do && !g_playback_done)
- pthread_cond_wait(&g_notify_cond, &g_notify_mutex);
- } else {
- struct timespec ts;
- clock_gettime(CLOCK_REALTIME, &ts);
- ts.tv_sec += next_timeout / 1000;
- ts.tv_nsec += (next_timeout % 1000) * 1000000;
- pthread_cond_timedwait(&g_notify_cond, &g_notify_mutex, &ts);
- }
- g_notify_do = 0;
- pthread_mutex_unlock(&g_notify_mutex);
- if (g_playback_done) {
- track_ended();
- g_playback_done = 0;
- }
- do {
- sp_session_process_events(sp, &next_timeout);
- } while (next_timeout == 0);
- pthread_mutex_lock(&g_notify_mutex);
- }
- return 0;
- }
- Generated on Fri Aug 28 15:15:11 2009. Copyright © 2006–2009 Spotify Ltd
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement