Advertisement
Guest User

Untitled

a guest
Jun 30th, 2017
5,557
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.27 KB | None | 0 0
  1. #include <errno.h>
  2. #include <libgen.h>
  3. #include <pthread.h>
  4. #include <stdint.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <unistd.h>
  9. #include <spotify/api.h>
  10.  
  11. #include "audio.h"
  12.  
  13.  
  14. /* --- Data --- */
  15. extern const uint8_t g_appkey[];
  16. extern const size_t g_appkey_size;
  17.  
  18. static audio_fifo_t g_audiofifo;
  19. static pthread_mutex_t g_notify_mutex;
  20. static pthread_cond_t g_notify_cond;
  21. static int g_notify_do;
  22. static int g_playback_done;
  23. static sp_session *g_sess;
  24. static sp_playlist *g_jukeboxlist;
  25. const char *g_listname;
  26. static int g_remove_tracks = 0;
  27. static sp_track *g_currenttrack;
  28. static int g_track_index;
  29.  
  30.  
  31. static void try_jukebox_start(void)
  32. {
  33. sp_track *t;
  34.  
  35. if (!g_jukeboxlist)
  36. return;
  37.  
  38. if (!sp_playlist_num_tracks(g_jukeboxlist)) {
  39. fprintf(stderr, "jukebox: No tracks in playlist. Waiting\n");
  40. return;
  41. }
  42.  
  43. if (sp_playlist_num_tracks(g_jukeboxlist) < g_track_index) {
  44. fprintf(stderr, "jukebox: No more tracks in playlist. Waiting\n");
  45. return;
  46. }
  47.  
  48. t = sp_playlist_track(g_jukeboxlist, g_track_index);
  49.  
  50. if (g_currenttrack && t != g_currenttrack) {
  51. /* Someone changed the current track */
  52. audio_fifo_flush(&g_audiofifo);
  53. sp_session_player_unload(g_sess);
  54. g_currenttrack = NULL;
  55. }
  56.  
  57. if (!t)
  58. return;
  59.  
  60. if (sp_track_is_loaded(t) == 0)
  61. return;
  62.  
  63. if (g_currenttrack == t)
  64. return;
  65.  
  66. g_currenttrack = t;
  67.  
  68. printf("jukebox: Now playing \"%s\"...\n", sp_track_name(t));
  69. fflush(stdout);
  70.  
  71. sp_session_player_load(g_sess, t);
  72. sp_session_player_play(g_sess, 1);
  73. }
  74.  
  75. /* -------------------------- PLAYLIST CALLBACKS ------------------------- */
  76. static void tracks_added(sp_playlist *pl, const sp_track **tracks,
  77. int num_tracks, int position, void *userdata)
  78. {
  79. if (pl != g_jukeboxlist)
  80. return;
  81.  
  82. printf("jukebox: %d tracks were added\n", num_tracks);
  83. fflush(stdout);
  84. try_jukebox_start();
  85. }
  86.  
  87. static void tracks_removed(sp_playlist *pl, const int *tracks,
  88. int num_tracks, void *userdata)
  89. {
  90. int i, k = 0;
  91.  
  92. if (pl != g_jukeboxlist)
  93. return;
  94.  
  95. for (i = 0; i < num_tracks; ++i)
  96. if (tracks[i] < g_track_index)
  97. ++k;
  98.  
  99. g_track_index -= k;
  100.  
  101. printf("jukebox: %d tracks were removed\n", num_tracks);
  102. fflush(stdout);
  103. try_jukebox_start();
  104. }
  105.  
  106. static void tracks_moved(sp_playlist *pl, const int *tracks,
  107. int num_tracks, int new_position, void *userdata)
  108. {
  109. if (pl != g_jukeboxlist)
  110. return;
  111.  
  112. printf("jukebox: %d tracks were moved around\n", num_tracks);
  113. fflush(stdout);
  114. try_jukebox_start();
  115. }
  116.  
  117. static void playlist_renamed(sp_playlist *pl, void *userdata)
  118. {
  119. const char *name = sp_playlist_name(pl);
  120.  
  121. if (!strcasecmp(name, g_listname)) {
  122. g_jukeboxlist = pl;
  123. g_track_index = 0;
  124. try_jukebox_start();
  125. } else if (g_jukeboxlist == pl) {
  126. printf("jukebox: current playlist renamed to \"%s\".\n", name);
  127. g_jukeboxlist = NULL;
  128. g_currenttrack = NULL;
  129. sp_session_player_unload(g_sess);
  130. }
  131. }
  132.  
  133. static sp_playlist_callbacks pl_callbacks = {
  134. .tracks_added = &tracks_added,
  135. .tracks_removed = &tracks_removed,
  136. .tracks_moved = &tracks_moved,
  137. .playlist_renamed = &playlist_renamed,
  138. };
  139.  
  140.  
  141. /* -------------------- PLAYLIST CONTAINER CALLBACKS --------------------- */
  142. static void playlist_added(sp_playlistcontainer *pc, sp_playlist *pl,
  143. int position, void *userdata)
  144. {
  145. sp_playlist_add_callbacks(pl, &pl_callbacks, NULL);
  146.  
  147. if (!strcasecmp(sp_playlist_name(pl), g_listname)) {
  148. g_jukeboxlist = pl;
  149. try_jukebox_start();
  150. }
  151. }
  152.  
  153. static void playlist_removed(sp_playlistcontainer *pc, sp_playlist *pl,
  154. int position, void *userdata)
  155. {
  156. sp_playlist_remove_callbacks(pl, &pl_callbacks, NULL);
  157. }
  158.  
  159. static sp_playlistcontainer_callbacks pc_callbacks = {
  160. .playlist_added = &playlist_added,
  161. .playlist_removed = &playlist_removed,
  162. };
  163.  
  164.  
  165. /* --------------------------- SESSION CALLBACKS ------------------------- */
  166. static void logged_in(sp_session *sess, sp_error error)
  167. {
  168. sp_playlistcontainer *pc = sp_session_playlistcontainer(sess);
  169. int i;
  170.  
  171. if (SP_ERROR_OK != error) {
  172. fprintf(stderr, "jukebox: Login failed: %s\n",
  173. sp_error_message(error));
  174. exit(2);
  175. }
  176.  
  177. printf("jukebox: Looking at %d playlists\n", sp_playlistcontainer_num_playlists(pc));
  178.  
  179. for (i = 0; i < sp_playlistcontainer_num_playlists(pc); ++i) {
  180. sp_playlist *pl = sp_playlistcontainer_playlist(pc, i);
  181.  
  182. sp_playlist_add_callbacks(pl, &pl_callbacks, NULL);
  183.  
  184. if (!strcasecmp(sp_playlist_name(pl), g_listname)) {
  185. g_jukeboxlist = pl;
  186. try_jukebox_start();
  187. }
  188. }
  189.  
  190. if (!g_jukeboxlist) {
  191. printf("jukebox: No such playlist. Waiting for one to pop up...\n");
  192. fflush(stdout);
  193. }
  194. }
  195.  
  196. static void notify_main_thread(sp_session *sess)
  197. {
  198. pthread_mutex_lock(&g_notify_mutex);
  199. g_notify_do = 1;
  200. pthread_cond_signal(&g_notify_cond);
  201. pthread_mutex_unlock(&g_notify_mutex);
  202. }
  203.  
  204. static int music_delivery(sp_session *sess, const sp_audioformat *format,
  205. const void *frames, int num_frames)
  206. {
  207. audio_fifo_t *af = &g_audiofifo;
  208. audio_fifo_data_t *afd;
  209. size_t s;
  210.  
  211. if (num_frames == 0)
  212. return 0; // Audio discontinuity, do nothing
  213.  
  214. pthread_mutex_lock(&af->mutex);
  215.  
  216. /* Buffer one second of audio */
  217. if (af->qlen > format->sample_rate) {
  218. pthread_mutex_unlock(&af->mutex);
  219.  
  220. return 0;
  221. }
  222.  
  223. s = num_frames * sizeof(int16_t) * format->channels;
  224.  
  225. afd = malloc(sizeof(audio_fifo_data_t) + s);
  226. memcpy(afd->samples, frames, s);
  227.  
  228. afd->nsamples = num_frames;
  229.  
  230. afd->rate = format->sample_rate;
  231. afd->channels = format->channels;
  232.  
  233. TAILQ_INSERT_TAIL(&af->q, afd, link);
  234. af->qlen += num_frames;
  235.  
  236. pthread_cond_signal(&af->cond);
  237. pthread_mutex_unlock(&af->mutex);
  238.  
  239. return num_frames;
  240. }
  241.  
  242.  
  243. static void end_of_track(sp_session *sess)
  244. {
  245. pthread_mutex_lock(&g_notify_mutex);
  246. g_playback_done = 1;
  247. pthread_cond_signal(&g_notify_cond);
  248. pthread_mutex_unlock(&g_notify_mutex);
  249. }
  250.  
  251.  
  252. static void metadata_updated(sp_session *sess)
  253. {
  254. try_jukebox_start();
  255. }
  256.  
  257. static void play_token_lost(sp_session *sess)
  258. {
  259. audio_fifo_flush(&g_audiofifo);
  260.  
  261. if (g_currenttrack != NULL) {
  262. sp_session_player_unload(g_sess);
  263. g_currenttrack = NULL;
  264. }
  265. }
  266.  
  267. static sp_session_callbacks session_callbacks = {
  268. .logged_in = &logged_in,
  269. .notify_main_thread = &notify_main_thread,
  270. .music_delivery = &music_delivery,
  271. .metadata_updated = &metadata_updated,
  272. .play_token_lost = &play_token_lost,
  273. .log_message = NULL,
  274. .end_of_track = &end_of_track,
  275. };
  276.  
  277. static sp_session_config spconfig = {
  278. .api_version = SPOTIFY_API_VERSION,
  279. .cache_location = "tmp",
  280. .settings_location = "tmp",
  281. .application_key = g_appkey,
  282. .application_key_size = 0, // Set in main()
  283. .user_agent = "spotify-jukebox-example",
  284. .callbacks = &session_callbacks,
  285. NULL,
  286. };
  287. /* ------------------------- END SESSION CALLBACKS ----------------------- */
  288.  
  289.  
  290. static void track_ended(void)
  291. {
  292. int tracks = 0;
  293.  
  294. if (g_currenttrack) {
  295. g_currenttrack = NULL;
  296. sp_session_player_unload(g_sess);
  297. if (g_remove_tracks) {
  298. sp_playlist_remove_tracks(g_jukeboxlist, &tracks, 1);
  299. } else {
  300. ++g_track_index;
  301. try_jukebox_start();
  302. }
  303. }
  304. }
  305.  
  306. static void usage(const char *progname)
  307. {
  308. fprintf(stderr, "usage: %s -u <username> -p <password> -l <listname> [-d]\n", progname);
  309. fprintf(stderr, "warning: -d will delete the tracks played from the list!\n");
  310. }
  311.  
  312. int main(int argc, char **argv)
  313. {
  314. sp_session *sp;
  315. sp_error err;
  316. int next_timeout = 0;
  317. const char *username = NULL;
  318. const char *password = NULL;
  319. int opt;
  320.  
  321. while ((opt = getopt(argc, argv, "u:p:l:d")) != EOF) {
  322. switch (opt) {
  323. case 'u':
  324. username = optarg;
  325. break;
  326.  
  327. case 'p':
  328. password = optarg;
  329. break;
  330.  
  331. case 'l':
  332. g_listname = optarg;
  333. break;
  334.  
  335. case 'd':
  336. g_remove_tracks = 1;
  337. break;
  338.  
  339. default:
  340. exit(1);
  341. }
  342. }
  343.  
  344. if (!username || !password || !g_listname) {
  345. usage(basename(argv[0]));
  346. exit(1);
  347. }
  348.  
  349. audio_init(&g_audiofifo);
  350.  
  351. /* Create session */
  352. spconfig.application_key_size = g_appkey_size;
  353.  
  354. err = sp_session_init(&spconfig, &sp);
  355.  
  356. if (SP_ERROR_OK != err) {
  357. fprintf(stderr, "Unable to create session: %s\n",
  358. sp_error_message(err));
  359. exit(1);
  360. }
  361.  
  362. g_sess = sp;
  363.  
  364. pthread_mutex_init(&g_notify_mutex, NULL);
  365. pthread_cond_init(&g_notify_cond, NULL);
  366.  
  367. sp_playlistcontainer_add_callbacks(
  368. sp_session_playlistcontainer(g_sess),
  369. &pc_callbacks,
  370. NULL);
  371.  
  372. sp_session_login(sp, username, password);
  373. pthread_mutex_lock(&g_notify_mutex);
  374.  
  375. for (;;) {
  376. if (next_timeout == 0) {
  377. while(!g_notify_do && !g_playback_done)
  378. pthread_cond_wait(&g_notify_cond, &g_notify_mutex);
  379. } else {
  380. struct timespec ts;
  381.  
  382. clock_gettime(CLOCK_REALTIME, &ts);
  383.  
  384. ts.tv_sec += next_timeout / 1000;
  385. ts.tv_nsec += (next_timeout % 1000) * 1000000;
  386.  
  387. pthread_cond_timedwait(&g_notify_cond, &g_notify_mutex, &ts);
  388. }
  389.  
  390. g_notify_do = 0;
  391. pthread_mutex_unlock(&g_notify_mutex);
  392.  
  393. if (g_playback_done) {
  394. track_ended();
  395. g_playback_done = 0;
  396. }
  397.  
  398. do {
  399. sp_session_process_events(sp, &next_timeout);
  400. } while (next_timeout == 0);
  401.  
  402. pthread_mutex_lock(&g_notify_mutex);
  403. }
  404.  
  405. return 0;
  406. }
  407. Generated on Fri Aug 28 15:15:11 2009. Copyright © 2006–2009 Spotify Ltd
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement