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 
47 static InputPlayback playback_api;
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 
73 static InputPlugin * current_decoder = NULL;
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 */
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 
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  {
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 
425  current_file, start_time, end_time, paused);
426 
427 DONE:
428  if (! ready_flag)
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 
535 static void set_gain_from_playlist (InputPlayback * p)
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 }