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  char * error = g_strdup_printf (_("No decoder found for %s."),
307  interface_show_error (error);
308  g_free (error);
310  goto DONE;
311  }
312 
313  current_data = NULL;
314  current_bitrate = 0;
315  current_samplerate = 0;
316  current_channels = 0;
317 
318  Tuple * tuple = playback_entry_get_tuple ();
319  read_gain_from_tuple (tuple);
320  if (tuple)
321  tuple_unref (tuple);
322 
323  bool_t seekable = (playback_entry_get_length () > 0);
324 
325  VFSFile * file = vfs_fopen (current_filename, "r");
326 
327  time_offset = seekable ? playback_entry_get_start_time () : 0;
329  file, seekable ? time_offset + initial_seek : 0,
330  seekable ? playback_entry_get_end_time () : -1, paused);
331 
333 
334  if (file)
335  vfs_fclose (file);
336 
337 DONE:
338  if (! ready_flag)
340 
341  end_source = g_timeout_add (0, end_cb, NULL);
342  return NULL;
343 }
344 
345 static void playback_start (int playlist, int entry, int seek_time, bool_t pause)
346 {
347  g_return_if_fail (! playing);
348 
349  current_filename = playlist_entry_get_filename (playlist, entry);
350  g_return_if_fail (current_filename);
351 
352  playing = TRUE;
354  ready_flag = FALSE;
355 
356  current_entry = -1;
358  current_length = 0;
359 
361  paused = pause;
362 
363  hook_associate ("playlist update", update_cb, NULL);
364  pthread_create (& playback_thread_handle, NULL, playback_thread, NULL);
365 
366  hook_call ("playback begin", NULL);
367 }
368 
370 {
371  return playing;
372 }
373 
375 {
376  g_return_val_if_fail (playing, FALSE);
377  return paused;
378 }
379 
380 void playback_seek (int time)
381 {
382  g_return_if_fail (playing);
383  wait_until_ready ();
384 
385  if (! current_decoder || ! current_decoder->mseek || current_length < 1)
386  return;
387 
388  current_decoder->mseek (& playback_api, time_offset + CLAMP (time, 0,
389  current_length));
390 
391  /* If the plugin is using our output system, don't call "playback seek"
392  * immediately but wait for output_set_time() to be called. This ensures
393  * that a "playback seek" handler can call playback_get_time() and get the
394  * new time. */
395  if (! output_is_open ())
396  hook_call ("playback seek", NULL);
397 }
398 
399 static void set_data (InputPlayback * p, void * data)
400 {
401  g_return_if_fail (playing);
402  current_data = data;
403 }
404 
405 static void * get_data (InputPlayback * p)
406 {
407  g_return_val_if_fail (playing, NULL);
408  return current_data;
409 }
410 
411 static void set_params (InputPlayback * p, int bitrate, int samplerate,
412  int channels)
413 {
414  g_return_if_fail (playing);
415 
416  current_bitrate = bitrate;
417  current_samplerate = samplerate;
419 
420  if (playback_get_ready ())
421  event_queue ("info change", NULL);
422 }
423 
424 static void set_tuple (InputPlayback * p, Tuple * tuple)
425 {
426  g_return_if_fail (playing);
427  read_gain_from_tuple (tuple);
428  playback_entry_set_tuple (tuple);
429 }
430 
431 static void set_gain_from_playlist (InputPlayback * p)
432 {
433  g_return_if_fail (playing);
434  p->output->set_replaygain_info (& gain_from_playlist);
435 }
436 
437 static InputPlayback playback_api = {
438  .output = & output_api,
439  .set_data = set_data,
440  .get_data = get_data,
441  .set_pb_ready = set_pb_ready,
442  .set_params = set_params,
443  .set_tuple = set_tuple,
444  .set_gain_from_playlist = set_gain_from_playlist,
445 };
446 
448 {
449  g_return_val_if_fail (playing, NULL);
450  return str_ref (current_filename);
451 }
452 
453 char * playback_get_title (void)
454 {
455  g_return_val_if_fail (playing, NULL);
456  wait_until_ready ();
457 
458  char s[32];
459 
460  if (current_length)
461  {
462  int len = current_length / 1000;
463 
464  if (len < 3600)
465  snprintf (s, sizeof s, get_bool (NULL, "leading_zero") ?
466  " (%02d:%02d)" : " (%d:%02d)", len / 60, len % 60);
467  else
468  snprintf (s, sizeof s, " (%d:%02d:%02d)", len / 3600, (len / 60) %
469  60, len % 60);
470  }
471  else
472  s[0] = 0;
473 
474  if (get_bool (NULL, "show_numbers_in_pl"))
475  return str_printf ("%d. %s%s", 1 + playlist_get_position
477 
478  return str_printf ("%s%s", current_title, s);
479 }
480 
482 {
483  g_return_val_if_fail (playing, 0);
484  wait_until_ready ();
485 
486  return current_length;
487 }
488 
489 void playback_get_info (int * bitrate, int * samplerate, int * channels)
490 {
491  g_return_if_fail (playing);
492  wait_until_ready ();
493 
494  * bitrate = current_bitrate;
495  * samplerate = current_samplerate;
496  * channels = current_channels;
497 }
498 
499 void playback_get_volume (int * l, int * r)
500 {
502  current_decoder->get_volume && current_decoder->get_volume (l, r))
503  return;
504 
505  output_get_volume (l, r);
506 }
507 
508 void playback_set_volume (int l, int r)
509 {
511  current_decoder->set_volume && current_decoder->set_volume (l, r))
512  return;
513 
514  output_set_volume (l, r);
515 }