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 "config.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_start (int playlist, int entry, 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 bool_t playing = FALSE;
51 static int failed_entries;
52 
53 static char * current_filename; /* pooled */
54 
55 static int current_entry;
56 static char * current_title; /* pooled */
57 static int current_length;
58 
59 static InputPlugin * current_decoder;
60 static void * current_data;
62 
64 
66 static bool_t paused;
67 
68 static pthread_t playback_thread_handle;
69 static int end_source = 0;
70 
71 static pthread_mutex_t ready_mutex = PTHREAD_MUTEX_INITIALIZER;
72 static pthread_cond_t ready_cond = PTHREAD_COND_INITIALIZER;
74 
75 /* clears gain info if tuple == NULL */
76 static void read_gain_from_tuple (const Tuple * tuple)
77 {
78  memset (& gain_from_playlist, 0, sizeof gain_from_playlist);
79 
80  if (tuple == NULL)
81  return;
82 
83  int album_gain = tuple_get_int (tuple, FIELD_GAIN_ALBUM_GAIN, NULL);
84  int album_peak = tuple_get_int (tuple, FIELD_GAIN_ALBUM_PEAK, NULL);
85  int track_gain = tuple_get_int (tuple, FIELD_GAIN_TRACK_GAIN, NULL);
86  int track_peak = tuple_get_int (tuple, FIELD_GAIN_TRACK_PEAK, NULL);
87  int gain_unit = tuple_get_int (tuple, FIELD_GAIN_GAIN_UNIT, NULL);
88  int peak_unit = tuple_get_int (tuple, FIELD_GAIN_PEAK_UNIT, NULL);
89 
90  if (gain_unit)
91  {
92  gain_from_playlist.album_gain = album_gain / (float) gain_unit;
93  gain_from_playlist.track_gain = track_gain / (float) gain_unit;
94  }
95 
96  if (peak_unit)
97  {
98  gain_from_playlist.album_peak = album_peak / (float) peak_unit;
99  gain_from_playlist.track_peak = track_peak / (float) peak_unit;
100  }
101 }
102 
104 {
106  char * title = playback_entry_get_title ();
107  int length = playback_entry_get_length ();
108 
109  if (entry == current_entry && ! g_strcmp0 (title, current_title) && length == current_length)
110  {
111  str_unref (title);
112  return FALSE;
113  }
114 
117  current_title = title;
118  current_length = length;
119  return TRUE;
120 }
121 
123 {
124  g_return_val_if_fail (playing, FALSE);
125  pthread_mutex_lock (& ready_mutex);
126  bool_t ready = ready_flag;
127  pthread_mutex_unlock (& ready_mutex);
128  return ready;
129 }
130 
131 static void set_pb_ready (InputPlayback * p)
132 {
133  g_return_if_fail (playing);
134  pthread_mutex_lock (& ready_mutex);
135 
137  ready_flag = TRUE;
138 
139  pthread_cond_signal (& ready_cond);
140  pthread_mutex_unlock (& ready_mutex);
141 
142  event_queue ("playback ready", NULL);
143 }
144 
145 static void wait_until_ready (void)
146 {
147  g_return_if_fail (playing);
148  pthread_mutex_lock (& ready_mutex);
149 
150  while (! ready_flag)
151  pthread_cond_wait (& ready_cond, & ready_mutex);
152 
153  pthread_mutex_unlock (& ready_mutex);
154 }
155 
156 static void update_cb (void * hook_data, void * user_data)
157 {
158  g_return_if_fail (playing);
159 
160  if (GPOINTER_TO_INT (hook_data) < PLAYLIST_UPDATE_METADATA || ! playback_get_ready ())
161  return;
162 
163  if (update_from_playlist ())
164  event_queue ("title change", NULL);
165 }
166 
168 {
169  g_return_val_if_fail (playing, 0);
170  wait_until_ready ();
171 
172  int time = -1;
173 
174  if (current_decoder && current_decoder->get_time)
175  time = current_decoder->get_time (& playback_api);
176 
177  if (time < 0)
178  time = output_get_time ();
179 
180  return time - time_offset;
181 }
182 
184 {
185  g_return_if_fail (! playing);
186 
188  if (playlist < 0)
189  return;
190 
191  int entry = playlist_get_position (playlist);
192  if (entry < 0)
193  return;
194 
195  failed_entries = 0;
196  playback_start (playlist, entry, seek_time, pause);
197 }
198 
199 void playback_pause (void)
200 {
201  g_return_if_fail (playing);
202  wait_until_ready ();
203 
204  if (! current_decoder || ! current_decoder->pause)
205  return;
206 
207  paused = ! paused;
208  current_decoder->pause (& playback_api, paused);
209 
210  if (paused)
211  hook_call ("playback pause", NULL);
212  else
213  hook_call ("playback unpause", NULL);
214 }
215 
216 static void playback_cleanup (void)
217 {
218  g_return_if_fail (playing);
219 
220  pthread_join (playback_thread_handle, NULL);
221  playing = FALSE;
222 
223  event_queue_cancel ("playback ready", NULL);
224  event_queue_cancel ("playback seek", NULL);
225  event_queue_cancel ("info change", NULL);
226  event_queue_cancel ("title change", NULL);
227 
228  if (end_source)
229  {
230  g_source_remove (end_source);
231  end_source = 0;
232  }
233 
238 
239  hook_dissociate ("playlist update", update_cb);
240 }
241 
242 static void complete_stop (void)
243 {
244  output_drain ();
245  hook_call ("playback stop", NULL);
246  set_bool (NULL, "stop_after_current_song", FALSE);
247 }
248 
249 void playback_stop (void)
250 {
251  g_return_if_fail (playing);
252  wait_until_ready ();
253 
254  if (current_decoder)
255  current_decoder->stop (& playback_api);
256 
257  playback_cleanup ();
258  complete_stop ();
259 }
260 
261 static bool_t end_cb (void * unused)
262 {
263  g_return_val_if_fail (playing, FALSE);
264 
265  hook_call ("playback end", NULL);
266 
267  if (playback_error)
268  failed_entries ++;
269  else
270  failed_entries = 0;
271 
272  playback_cleanup ();
273 
275  bool_t play;
276 
277  if (get_bool (NULL, "no_playlist_advance"))
278  play = get_bool (NULL, "repeat") && ! failed_entries;
279  else if (! (play = playlist_next_song (playlist, get_bool (NULL, "repeat"))))
280  playlist_set_position (playlist, -1);
281  else if (failed_entries >= 10)
282  play = FALSE;
283 
284  if (get_bool (NULL, "stop_after_current_song"))
285  play = FALSE;
286 
287  if (play)
288  playback_start (playlist, playlist_get_position (playlist), 0, FALSE);
289  else
290  {
291  complete_stop ();
292  hook_call ("playlist end reached", NULL);
293  }
294 
295  return FALSE;
296 }
297 
298 static void * playback_thread (void * unused)
299 {
302 
303  if (! current_decoder)
304  {
305  SPRINTF (error, _("No decoder found for %s."), current_filename);
308  goto DONE;
309  }
310 
311  current_data = NULL;
312  current_bitrate = 0;
313  current_samplerate = 0;
314  current_channels = 0;
315 
316  Tuple * tuple = playback_entry_get_tuple ();
317  read_gain_from_tuple (tuple);
318  if (tuple)
319  tuple_unref (tuple);
320 
321  bool_t seekable = (playback_entry_get_length () > 0);
322 
323  VFSFile * file = NULL;
324 
325  if (! current_decoder->schemes || ! current_decoder->schemes[0])
326  {
327  if (! (file = vfs_fopen (current_filename, "r")))
328  {
329  SPRINTF (error, _("%s could not be opened."), current_filename);
332  goto DONE;
333  }
334  }
335 
336  time_offset = seekable ? playback_entry_get_start_time () : 0;
338  file, seekable ? time_offset + initial_seek : 0,
339  seekable ? playback_entry_get_end_time () : -1, paused);
340 
342 
343  if (file)
344  vfs_fclose (file);
345 
346 DONE:
347  if (! ready_flag)
349 
350  end_source = g_timeout_add (0, end_cb, NULL);
351  return NULL;
352 }
353 
354 static void playback_start (int playlist, int entry, int seek_time, bool_t pause)
355 {
356  g_return_if_fail (! playing);
357 
358  current_filename = playlist_entry_get_filename (playlist, entry);
359  g_return_if_fail (current_filename);
360 
361  playing = TRUE;
363  ready_flag = FALSE;
364 
365  current_entry = -1;
367  current_length = 0;
368 
370  paused = pause;
371 
372  hook_associate ("playlist update", update_cb, NULL);
373  pthread_create (& playback_thread_handle, NULL, playback_thread, NULL);
374 
375  hook_call ("playback begin", NULL);
376 }
377 
379 {
380  return playing;
381 }
382 
384 {
385  g_return_val_if_fail (playing, FALSE);
386  return paused;
387 }
388 
389 void playback_seek (int time)
390 {
391  g_return_if_fail (playing);
392  wait_until_ready ();
393 
394  if (! current_decoder || ! current_decoder->mseek || current_length < 1)
395  return;
396 
397  current_decoder->mseek (& playback_api, time_offset + CLAMP (time, 0,
398  current_length));
399 
400  /* If the plugin is using our output system, don't call "playback seek"
401  * immediately but wait for output_set_time() to be called. This ensures
402  * that a "playback seek" handler can call playback_get_time() and get the
403  * new time. */
404  if (! output_is_open ())
405  hook_call ("playback seek", NULL);
406 }
407 
408 static void set_data (InputPlayback * p, void * data)
409 {
410  g_return_if_fail (playing);
411  current_data = data;
412 }
413 
414 static void * get_data (InputPlayback * p)
415 {
416  g_return_val_if_fail (playing, NULL);
417  return current_data;
418 }
419 
420 static void set_params (InputPlayback * p, int bitrate, int samplerate,
421  int channels)
422 {
423  g_return_if_fail (playing);
424 
425  current_bitrate = bitrate;
426  current_samplerate = samplerate;
428 
429  if (playback_get_ready ())
430  event_queue ("info change", NULL);
431 }
432 
433 static void set_tuple (InputPlayback * p, Tuple * tuple)
434 {
435  g_return_if_fail (playing);
436  read_gain_from_tuple (tuple);
437  playback_entry_set_tuple (tuple);
438 }
439 
440 static void set_gain_from_playlist (InputPlayback * p)
441 {
442  g_return_if_fail (playing);
443  p->output->set_replaygain_info (& gain_from_playlist);
444 }
445 
446 static InputPlayback playback_api = {
447  .output = & output_api,
448  .set_data = set_data,
449  .get_data = get_data,
450  .set_pb_ready = set_pb_ready,
451  .set_params = set_params,
452  .set_tuple = set_tuple,
453  .set_gain_from_playlist = set_gain_from_playlist,
454 };
455 
457 {
458  g_return_val_if_fail (playing, NULL);
459  return str_ref (current_filename);
460 }
461 
462 char * playback_get_title (void)
463 {
464  g_return_val_if_fail (playing, NULL);
465  wait_until_ready ();
466 
467  char s[32];
468 
469  if (current_length > 0)
470  {
471  int len = current_length / 1000;
472 
473  if (len < 3600)
474  snprintf (s, sizeof s, get_bool (NULL, "leading_zero") ?
475  " (%02d:%02d)" : " (%d:%02d)", len / 60, len % 60);
476  else
477  snprintf (s, sizeof s, " (%d:%02d:%02d)", len / 3600, (len / 60) %
478  60, len % 60);
479  }
480  else
481  s[0] = 0;
482 
483  if (get_bool (NULL, "show_numbers_in_pl"))
484  return str_printf ("%d. %s%s", 1 + playlist_get_position
486 
487  return str_printf ("%s%s", current_title, s);
488 }
489 
491 {
492  g_return_val_if_fail (playing, 0);
493  wait_until_ready ();
494 
495  return current_length;
496 }
497 
498 void playback_get_info (int * bitrate, int * samplerate, int * channels)
499 {
500  g_return_if_fail (playing);
501  wait_until_ready ();
502 
503  * bitrate = current_bitrate;
504  * samplerate = current_samplerate;
505  * channels = current_channels;
506 }
507 
508 void playback_get_volume (int * l, int * r)
509 {
511  current_decoder->get_volume && current_decoder->get_volume (l, r))
512  return;
513 
514  output_get_volume (l, r);
515 }
516 
517 void playback_set_volume (int l, int r)
518 {
520  current_decoder->set_volume && current_decoder->set_volume (l, r))
521  return;
522 
523  output_set_volume (l, r);
524 }