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