Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
playback.c
Go to the documentation of this file.
1 /*
2  * playback.c
3  * Copyright 2009-2012 John Lindgren
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions, and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions, and the following disclaimer in the documentation
13  * provided with the distribution.
14  *
15  * This software is provided "as is" and without any warranty, express or
16  * implied. In no event shall the authors be liable for any damages arising from
17  * the use of this software.
18  */
19 
20 #include <glib.h>
21 #include <pthread.h>
22 #include <string.h>
23 
24 #include <libaudcore/audstrings.h>
25 #include <libaudcore/hook.h>
26 
27 #include "drct.h"
28 #include "i18n.h"
29 #include "interface.h"
30 #include "misc.h"
31 #include "output.h"
32 #include "playback.h"
33 #include "playlist.h"
34 #include "plugin.h"
35 
36 static void playback_restart (int seek_time, bool_t pause);
37 
38 static const struct OutputAPI output_api = {
40  .set_replaygain_info = output_set_replaygain_info,
41  .write_audio = output_write_audio,
42  .abort_write = output_abort_write,
43  .pause = output_pause,
44  .written_time = output_written_time,
45  .flush = output_set_time};
46 
48 
49 static pthread_t playback_thread_handle;
50 static int end_source = 0;
51 
52 static pthread_mutex_t ready_mutex = PTHREAD_MUTEX_INITIALIZER;
53 static pthread_cond_t ready_cond = PTHREAD_COND_INITIALIZER;
54 
55 /* level 1 data (persists to end of song) */
56 static bool_t playing = FALSE;
58 static int time_offset = 0, initial_seek = 0;
59 static bool_t paused = FALSE;
63 
64 static void * current_data = NULL;
66 
67 /* level 2 data (persists when restarting same song) */
68 static int current_entry = -1;
69 static char * current_filename = NULL; /* pooled */
70 static char * current_title = NULL; /* pooled */
71 static int current_length = -1;
72 
76 
77 static int repeat_a = -1, repeat_b = -1;
78 
79 /* level 3 data (persists to end of playlist) */
80 static bool_t stopped = TRUE;
81 static int failed_entries = 0;
82 
83 /* clears gain info if tuple == NULL */
84 static void read_gain_from_tuple (const Tuple * tuple)
85 {
86  memset (& gain_from_playlist, 0, sizeof gain_from_playlist);
87 
88  if (tuple == NULL)
89  return;
90 
91  int album_gain = tuple_get_int (tuple, FIELD_GAIN_ALBUM_GAIN, NULL);
92  int album_peak = tuple_get_int (tuple, FIELD_GAIN_ALBUM_PEAK, NULL);
93  int track_gain = tuple_get_int (tuple, FIELD_GAIN_TRACK_GAIN, NULL);
94  int track_peak = tuple_get_int (tuple, FIELD_GAIN_TRACK_PEAK, NULL);
95  int gain_unit = tuple_get_int (tuple, FIELD_GAIN_GAIN_UNIT, NULL);
96  int peak_unit = tuple_get_int (tuple, FIELD_GAIN_PEAK_UNIT, NULL);
97 
98  if (gain_unit)
99  {
100  gain_from_playlist.album_gain = album_gain / (float) gain_unit;
101  gain_from_playlist.track_gain = track_gain / (float) gain_unit;
102  }
103 
104  if (peak_unit)
105  {
106  gain_from_playlist.album_peak = album_peak / (float) peak_unit;
107  gain_from_playlist.track_peak = track_peak / (float) peak_unit;
108  }
109 }
110 
112 {
114  char * title = playback_entry_get_title ();
115  int length = playback_entry_get_length ();
116 
117  /* pointer comparison works for pooled strings */
118  if (entry == current_entry && title == current_title && length == current_length)
119  {
120  str_unref (title);
121  return FALSE;
122  }
123 
126  current_title = title;
127  current_length = length;
128  return TRUE;
129 }
130 
132 {
133  if (! playing)
134  return FALSE;
135 
136  pthread_mutex_lock (& ready_mutex);
137 
138  /* on restart, always report ready */
139  bool_t ready = ready_flag || restart_flag;
140 
141  pthread_mutex_unlock (& ready_mutex);
142  return ready;
143 }
144 
145 static void set_pb_ready (InputPlayback * p)
146 {
147  g_return_if_fail (playing);
148  pthread_mutex_lock (& ready_mutex);
149 
150  /* on restart, don't update or send "playback ready" */
151  if (! restart_flag)
152  {
154  event_queue ("playback ready", NULL);
155  }
156 
157  ready_flag = TRUE;
158 
159  pthread_cond_signal (& ready_cond);
160  pthread_mutex_unlock (& ready_mutex);
161 }
162 
163 static void wait_until_ready (void)
164 {
165  g_return_if_fail (playing);
166  pthread_mutex_lock (& ready_mutex);
167 
168  /* on restart, we still have to wait, but presumably not long */
169  while (! ready_flag)
170  pthread_cond_wait (& ready_cond, & ready_mutex);
171 
172  pthread_mutex_unlock (& ready_mutex);
173 }
174 
175 static void update_cb (void * hook_data, void * user_data)
176 {
177  g_return_if_fail (playing);
178 
179  if (GPOINTER_TO_INT (hook_data) < PLAYLIST_UPDATE_METADATA || ! drct_get_ready ())
180  return;
181 
182  if (update_from_playlist ())
183  event_queue ("title change", NULL);
184 }
185 
186 int drct_get_time (void)
187 {
188  if (! playing)
189  return 0;
190 
191  wait_until_ready ();
192 
193  int time = -1;
194 
195  if (current_decoder && current_decoder->get_time)
196  time = current_decoder->get_time (& playback_api);
197 
198  if (time < 0)
199  time = output_get_time ();
200 
201  return time - time_offset;
202 }
203 
204 void drct_pause (void)
205 {
206  if (! playing)
207  return;
208 
209  wait_until_ready ();
210 
211  if (! current_decoder || ! current_decoder->pause)
212  return;
213 
214  paused = ! paused;
215  current_decoder->pause (& playback_api, paused);
216 
217  if (paused)
218  hook_call ("playback pause", NULL);
219  else
220  hook_call ("playback unpause", NULL);
221 }
222 
223 static void playback_finish (void)
224 {
225  g_return_if_fail (playing);
226  wait_until_ready ();
227 
228  /* calling stop() is unnecessary if the song finished on its own;
229  * also, it might flush the output buffer, breaking gapless playback */
230  if (current_decoder && ! song_finished)
231  current_decoder->stop (& playback_api);
232 
233  pthread_join (playback_thread_handle, NULL);
235 
236  hook_dissociate ("playlist update", update_cb);
237 
238  if (end_source)
239  {
240  g_source_remove (end_source);
241  end_source = 0;
242  }
243 
244  /* level 1 data cleanup */
245  playing = FALSE;
248  paused = FALSE;
249  ready_flag = FALSE;
252 
253  current_data = NULL;
255 }
256 
257 static void playback_cleanup (void)
258 {
259  g_return_if_fail (current_filename);
260  playback_finish ();
261 
262  event_queue_cancel ("playback ready", NULL);
263  event_queue_cancel ("playback seek", NULL);
264  event_queue_cancel ("info change", NULL);
265  event_queue_cancel ("title change", NULL);
266 
267  set_bool (NULL, "stop_after_current_song", FALSE);
268 
269  /* level 2 data cleanup */
270  current_entry = -1;
275  current_length = -1;
276 
277  current_decoder = NULL;
278 
279  if (current_file)
280  {
281  vfs_fclose (current_file);
282  current_file = NULL;
283  }
284 
286 
287  repeat_a = repeat_b = -1;
288 }
289 
290 void playback_stop (void)
291 {
292  if (stopped)
293  return;
294 
295  if (current_filename)
296  playback_cleanup ();
297 
298  output_drain ();
299 
300  /* level 3 data cleanup */
301  stopped = TRUE;
302  failed_entries = 0;
303 
304  hook_call ("playback stop", NULL);
305 }
306 
307 static void do_stop (int playlist)
308 {
310  playlist_set_position (playlist, playlist_get_position (playlist));
311 }
312 
313 static void do_next (int playlist)
314 {
315  if (! playlist_next_song (playlist, get_bool (NULL, "repeat")))
316  {
317  playlist_set_position (playlist, -1);
318  hook_call ("playlist end reached", NULL);
319  }
320 }
321 
322 static bool_t end_cb (void * unused)
323 {
324  g_return_val_if_fail (playing, FALSE);
325 
326  if (! playback_error)
328 
329  hook_call ("playback end", NULL);
330 
331  if (playback_error)
332  failed_entries ++;
333  else
334  failed_entries = 0;
335 
337 
338  if (get_bool (NULL, "stop_after_current_song"))
339  {
340  do_stop (playlist);
341 
342  if (! get_bool (NULL, "no_playlist_advance"))
343  do_next (playlist);
344  }
345  else if (repeat_a >= 0 || repeat_b >= 0)
346  {
347  if (! failed_entries)
349  else
350  do_stop (playlist);
351  }
352  else if (get_bool (NULL, "no_playlist_advance"))
353  {
354  if (get_bool (NULL, "repeat") && ! failed_entries)
355  playback_restart (0, FALSE);
356  else
357  do_stop (playlist);
358  }
359  else
360  {
361  if (failed_entries < 10)
362  do_next (playlist);
363  else
364  do_stop (playlist);
365  }
366 
367  return FALSE;
368 }
369 
370 static void * playback_thread (void * unused)
371 {
372  if (! current_decoder)
373  {
375  current_decoder = p ? plugin_get_header (p) : NULL;
376 
377  if (! current_decoder)
378  {
379  SPRINTF (error, _("No decoder found for %s."), current_filename);
382  goto DONE;
383  }
384  }
385 
386  Tuple * tuple = playback_entry_get_tuple ();
387  read_gain_from_tuple (tuple);
388 
389  int start_time = time_offset = 0;
390  int end_time = -1;
391 
392  if (tuple && playback_entry_get_length () > 0)
393  {
396 
397  start_time = time_offset + MAX (initial_seek, 0);
398 
399  if (repeat_b >= 0)
400  end_time = time_offset + repeat_b;
402  end_time = tuple_get_int (tuple, FIELD_SEGMENT_END, NULL);
403  }
404 
405  if (tuple)
406  tuple_unref (tuple);
407 
408  if (! current_decoder->schemes || ! current_decoder->schemes[0])
409  {
410  if (current_file)
411  vfs_rewind (current_file);
412  else
413  current_file = vfs_fopen (current_filename, "r");
414 
415  if (! current_file)
416  {
417  SPRINTF (error, _("%s could not be opened."), current_filename);
420  goto DONE;
421  }
422  }
423 
424  playback_error = ! current_decoder->play (& playback_api, current_filename,
425  current_file, start_time, end_time, paused);
426 
427 DONE:
428  if (! ready_flag)
429  set_pb_ready (& playback_api);
430 
431  end_source = g_timeout_add (0, end_cb, NULL);
432  return NULL;
433 }
434 
436 {
437  playing = TRUE;
439  paused = pause;
440  stopped = FALSE;
441 
442  hook_associate ("playlist update", update_cb, NULL);
443  pthread_create (& playback_thread_handle, NULL, playback_thread, NULL);
444 }
445 
447 {
448  if (playing)
449  playback_finish ();
450 
451  restart_flag = TRUE;
452 
453  playback_start (seek_time, pause);
454 
455  /* on restart, send "playback seek" instead of "playback begin" */
456  hook_call ("playback seek", NULL);
457 }
458 
460 {
461  if (current_filename)
462  playback_cleanup ();
463 
465  g_return_if_fail (current_filename);
466 
467  playback_start (seek_time, pause);
468 
469  hook_call ("playback begin", NULL);
470 }
471 
473 {
474  return playing;
475 }
476 
478 {
479  return paused;
480 }
481 
482 void drct_seek (int time)
483 {
484  if (! playing)
485  return;
486 
487  wait_until_ready ();
488 
489  if (! current_decoder || ! current_decoder->mseek || current_length < 1)
490  return;
491 
492  current_decoder->mseek (& playback_api, time_offset + CLAMP (time, 0,
493  current_length));
494 
495  /* If the plugin is using our output system, don't call "playback seek"
496  * immediately but wait for output_set_time() to be called. This ensures
497  * that a "playback seek" handler can call playback_get_time() and get the
498  * new time. */
499  if (! output_is_open ())
500  hook_call ("playback seek", NULL);
501 }
502 
503 static void set_data (InputPlayback * p, void * data)
504 {
505  g_return_if_fail (playing);
506  current_data = data;
507 }
508 
509 static void * get_data (InputPlayback * p)
510 {
511  g_return_val_if_fail (playing, NULL);
512  return current_data;
513 }
514 
515 static void set_params (InputPlayback * p, int bitrate, int samplerate,
516  int channels)
517 {
518  g_return_if_fail (playing);
519 
520  current_bitrate = bitrate;
521  current_samplerate = samplerate;
523 
524  if (drct_get_ready ())
525  event_queue ("info change", NULL);
526 }
527 
528 static void set_tuple (InputPlayback * p, Tuple * tuple)
529 {
530  g_return_if_fail (playing);
531  read_gain_from_tuple (tuple);
532  playback_entry_set_tuple (tuple);
533 }
534 
536 {
537  g_return_if_fail (playing);
538  p->output->set_replaygain_info (& gain_from_playlist);
539 }
540 
541 static InputPlayback playback_api = {
542  .output = & output_api,
543  .set_data = set_data,
544  .get_data = get_data,
545  .set_pb_ready = set_pb_ready,
546  .set_params = set_params,
547  .set_tuple = set_tuple,
548  .set_gain_from_playlist = set_gain_from_playlist,
549 };
550 
551 char * drct_get_filename (void)
552 {
553  if (! playing)
554  return NULL;
555 
556  return str_ref (current_filename);
557 }
558 
559 char * drct_get_title (void)
560 {
561  if (! playing)
562  return NULL;
563 
564  wait_until_ready ();
565 
566  char s[32];
567 
568  if (current_length > 0)
569  {
570  int len = current_length / 1000;
571 
572  if (len < 3600)
573  snprintf (s, sizeof s, get_bool (NULL, "leading_zero") ?
574  " (%02d:%02d)" : " (%d:%02d)", len / 60, len % 60);
575  else
576  snprintf (s, sizeof s, " (%d:%02d:%02d)", len / 3600, (len / 60) %
577  60, len % 60);
578  }
579  else
580  s[0] = 0;
581 
582  if (get_bool (NULL, "show_numbers_in_pl"))
583  return str_printf ("%d. %s%s", 1 + current_entry, current_title, s);
584 
585  return str_printf ("%s%s", current_title, s);
586 }
587 
588 int drct_get_length (void)
589 {
590  if (playing)
591  wait_until_ready ();
592 
593  return current_length;
594 }
595 
596 void drct_get_info (int * bitrate, int * samplerate, int * channels)
597 {
598  if (playing)
599  wait_until_ready ();
600 
601  * bitrate = current_bitrate;
602  * samplerate = current_samplerate;
603  * channels = current_channels;
604 }
605 
606 void drct_get_volume (int * l, int * r)
607 {
608  if (playing && drct_get_ready () && current_decoder &&
609  current_decoder->get_volume && current_decoder->get_volume (l, r))
610  return;
611 
612  output_get_volume (l, r);
613 }
614 
615 void drct_set_volume (int l, int r)
616 {
617  l = CLAMP (l, 0, 100);
618  r = CLAMP (r, 0, 100);
619 
620  if (playing && drct_get_ready () && current_decoder &&
621  current_decoder->set_volume && current_decoder->set_volume (l, r))
622  return;
623 
624  output_set_volume (l, r);
625 }
626 
627 void drct_set_ab_repeat (int a, int b)
628 {
629  if (! playing)
630  return;
631 
632  wait_until_ready ();
633 
634  if (current_length < 1)
635  return;
636 
637  repeat_a = a;
638 
639  if (repeat_b != b)
640  {
641  repeat_b = b;
642 
643  /* Restart playback so the new setting takes effect. We could add
644  * something like InputPlugin::set_stop_time(), but this is the only
645  * place it would be used. */
646  int seek_time = drct_get_time ();
647  bool_t was_paused = paused;
648 
649  if (repeat_b >= 0 && seek_time >= repeat_b)
650  seek_time = MAX (repeat_a, 0);
651 
652  playback_restart (seek_time, was_paused);
653  }
654 }
655 
656 void drct_get_ab_repeat (int * a, int * b)
657 {
658  * a = playing ? repeat_a : -1;
659  * b = playing ? repeat_b : -1;
660 }
void output_close_audio(void)
Definition: output.c:479
static void set_gain_from_playlist(InputPlayback *p)
Definition: playback.c:535
static int channels
Definition: equalizer.c:54
void drct_set_volume(int l, int r)
Definition: playback.c:615
static bool_t ready_flag
Definition: playback.c:60
int playlist_get_playing(void)
Definition: playlist-new.c:921
bool_t playlist_next_song(int playlist_num, bool_t repeat)
static void playback_restart(int seek_time, bool_t pause)
Definition: playback.c:446
void(* pause)(InputPlayback *playback, bool_t paused)
Definition: plugin.h:415
char * playback_entry_get_title(void)
bool_t(* play)(InputPlayback *playback, const char *filename, VFSFile *file, int start_time, int stop_time, bool_t pause)
Definition: plugin.h:409
float album_peak
Definition: types.h:58
static float a[EQ_BANDS][2]
Definition: equalizer.c:55
void output_set_replaygain_info(const ReplayGainInfo *info)
Definition: output.c:343
static int seek_time
Definition: output.c:62
void drct_seek(int time)
Definition: playback.c:482
float track_peak
Definition: types.h:56
static bool_t paused
Definition: playback.c:59
#define _(String)
Definition: i18n.h:25
static bool_t end_cb(void *unused)
Definition: playback.c:322
playlist
Definition: playlist-api.h:122
int drct_get_time(void)
Definition: playback.c:186
EXPORT int vfs_fclose(VFSFile *file)
Closes a VFS stream and destroys a VFSFile object.
Definition: vfs.c:164
char * drct_get_filename(void)
Definition: playback.c:551
static InputPlayback playback_api
Definition: playback.c:47
int output_get_time(void)
Definition: output.c:448
bool_t pause
Definition: main.c:58
static void playback_start(int seek_time, bool_t pause)
Definition: playback.c:435
static float b[EQ_BANDS][2]
Definition: equalizer.c:56
static bool_t playing
Definition: playback.c:56
char * playback_entry_get_filename(void)
char * str_printf(const char *format,...)
Definition: strpool.c:116
static pthread_cond_t ready_cond
Definition: playback.c:53
char * drct_get_title(void)
Definition: playback.c:559
EXPORT void hook_associate(const char *name, HookFunction func, void *user)
Definition: hook.c:36
static void * get_data(InputPlayback *p)
Definition: playback.c:509
static pthread_mutex_t ready_mutex
Definition: playback.c:52
void set_bool(const char *section, const char *name, bool_t value)
Definition: config.c:289
#define FALSE
Definition: core.h:35
#define CLAMP(a, min, max)
Definition: core.h:44
void(* set_replaygain_info)(const ReplayGainInfo *info)
Definition: plugin.h:267
void drct_get_info(int *bitrate, int *samplerate, int *channels)
Definition: playback.c:596
void drct_get_ab_repeat(int *a, int *b)
Definition: playback.c:656
Index Index bool_t
Definition: playlist-api.h:122
void output_abort_write(void)
Definition: output.c:381
static bool_t playback_error
Definition: playback.c:61
int(* get_volume)(int *l, int *r)
Definition: plugin.h:432
static void read_gain_from_tuple(const Tuple *tuple)
Definition: playback.c:84
#define hook_dissociate(n, f)
Definition: hook.h:34
int playback_entry_get_length(void)
void(* mseek)(InputPlayback *playback, int time)
Definition: plugin.h:421
static void do_stop(int playlist)
Definition: playback.c:307
bool_t drct_get_ready(void)
Definition: playback.c:131
char * str_ref(char *str)
Definition: strpool.c:74
static const struct OutputAPI output_api
Definition: playback.c:38
const void * plugin_get_header(PluginHandle *plugin)
static void update_cb(void *hook_data, void *user_data)
Definition: playback.c:175
int(* set_volume)(int l, int r)
Definition: plugin.h:433
static void playback_finish(void)
Definition: playback.c:223
static void wait_until_ready(void)
Definition: playback.c:163
static void * playback_thread(void *unused)
Definition: playback.c:370
Tuple * playback_entry_get_tuple(void)
static InputPlugin * current_decoder
Definition: playback.c:73
static bool_t stopped
Definition: playback.c:80
PluginHandle * playback_entry_get_decoder(void)
#define NULL
Definition: core.h:27
void output_set_volume(int left, int right)
Definition: output.c:559
static void * current_data
Definition: playback.c:64
const char *const * schemes
Definition: plugin.h:360
EXPORT void tuple_unref(Tuple *tuple)
Definition: tuple.c:284
static GError * error
Definition: audctrl.c:30
static int time_offset
Definition: playback.c:58
static void set_tuple(InputPlayback *p, Tuple *tuple)
Definition: playback.c:528
bool_t(* open_audio)(int format, int rate, int channels)
Definition: plugin.h:262
static void set_pb_ready(InputPlayback *p)
Definition: playback.c:145
static ReplayGainInfo gain_from_playlist
Definition: playback.c:75
static char * current_title
Definition: playback.c:70
void playback_stop(void)
Definition: playback.c:290
#define TRUE
Definition: core.h:37
bool_t get_bool(const char *section, const char *name)
Definition: config.c:294
int drct_get_length(void)
Definition: playback.c:588
static char * current_filename
Definition: playback.c:69
static int current_length
Definition: playback.c:71
static int current_samplerate
Definition: playback.c:65
float album_gain
Definition: types.h:57
static int failed_entries
Definition: playback.c:81
bool_t output_open_audio(int format, int rate, int channels)
Definition: output.c:313
int output_written_time(void)
Definition: output.c:411
void str_unref(char *str)
Definition: strpool.c:89
EXPORT VFSFile * vfs_fopen(const char *path, const char *mode)
Opens a stream from a VFS transport using one of the registered VFSConstructor handlers.
Definition: vfs.c:122
float track_gain
Definition: types.h:55
EXPORT void event_queue_cancel(const char *name, void *data)
Definition: eventqueue.c:71
void playlist_set_position(int playlist_num, int entry_num)
static bool_t update_from_playlist(void)
Definition: playback.c:111
bool_t drct_get_paused(void)
Definition: playback.c:477
static int current_bitrate
Definition: playback.c:65
#define event_queue(n, d)
Definition: hook.h:44
void drct_pause(void)
Definition: playback.c:204
int playback_entry_get_position(void)
static void playback_cleanup(void)
Definition: playback.c:257
int(* get_time)(InputPlayback *playback)
Definition: plugin.h:431
static pthread_t playback_thread_handle
Definition: playback.c:49
void playback_entry_set_tuple(Tuple *tuple)
static int current_channels
Definition: playback.c:65
static bool_t song_finished
Definition: playback.c:62
static int repeat_a
Definition: playback.c:77
EXPORT void hook_call(const char *name, void *data)
Definition: hook.c:98
EXPORT TupleValueType tuple_get_value_type(const Tuple *tuple, int nfield, const char *field)
Returns TupleValueType of given #Tuple field.
Definition: tuple.c:459
static int repeat_b
Definition: playback.c:77
void output_get_volume(int *left, int *right)
Definition: output.c:542
void output_write_audio(void *data, int size)
Definition: output.c:362
#define MAX(a, b)
Definition: core.h:42
bool_t output_is_open(void)
Definition: output.c:440
const struct OutputAPI * output
Definition: plugin.h:301
void playback_play(int seek_time, bool_t pause)
Definition: playback.c:459
void output_drain(void)
Definition: output.c:494
static VFSFile * current_file
Definition: playback.c:74
static bool_t restart_flag
Definition: playback.c:57
static int end_source
Definition: playback.c:50
EXPORT void vfs_rewind(VFSFile *file)
Rewinds a VFS stream.
Definition: vfs.c:297
static void set_params(InputPlayback *p, int bitrate, int samplerate, int channels)
Definition: playback.c:515
static int current_entry
Definition: playback.c:68
static void do_next(int playlist)
Definition: playback.c:313
void interface_show_error(const char *message)
Definition: interface.c:115
int playlist_get_position(int playlist_num)
static void set_data(InputPlayback *p, void *data)
Definition: playback.c:503
static int initial_seek
Definition: playback.c:58
void playlist_set_playing(int playlist_num)
Definition: playlist-new.c:868
struct @17::@18::@20 s
#define SPRINTF(s,...)
Definition: core.h:46
void drct_get_volume(int *l, int *r)
Definition: playback.c:606
void output_set_time(int time)
Definition: output.c:423
Index Index play entry
Definition: playlist-api.h:144
void drct_set_ab_repeat(int a, int b)
Definition: playback.c:627
void output_pause(bool_t pause)
Definition: output.c:396
bool_t drct_get_playing(void)
Definition: playback.c:472
void(* stop)(InputPlayback *playback)
Definition: plugin.h:427
EXPORT int tuple_get_int(const Tuple *tuple, int nfield, const char *field)
Returns integer associated to #Tuple field.
Definition: tuple.c:509