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