Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
playback.c
Go to the documentation of this file.
1 /*
2  * playback.c
3  * Copyright 2005-2011 Audacious Development Team
4  *
5  * This file is part of Audacious.
6  *
7  * Audacious is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, version 2 or version 3 of the License.
10  *
11  * Audacious is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13  * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * Audacious. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * The Audacious team does not consider modular code linking to Audacious or
19  * using our public API to be a derived work.
20  */
21 
22 #include <glib.h>
23 #include <pthread.h>
24 #include <string.h>
25 
26 #include <libaudcore/audstrings.h>
27 #include <libaudcore/hook.h>
28 
29 #include "config.h"
30 #include "i18n.h"
31 #include "interface.h"
32 #include "misc.h"
33 #include "output.h"
34 #include "playback.h"
35 #include "playlist.h"
36 
37 static void playback_start (int playlist, int entry, int seek_time, bool_t pause);
38 
39 static InputPlayback playback_api;
40 
41 static bool_t playing = FALSE;
43 static int failed_entries;
44 
45 static char * current_filename; /* pooled */
46 
47 static int current_entry;
48 static char * current_title; /* pooled */
49 static int current_length;
50 
51 static InputPlugin * current_decoder;
52 static void * current_data;
54 
56 
58 static bool_t paused;
59 
60 static pthread_t playback_thread_handle;
61 static int end_source = 0;
62 
63 static pthread_mutex_t ready_mutex = PTHREAD_MUTEX_INITIALIZER;
64 static pthread_cond_t ready_cond = PTHREAD_COND_INITIALIZER;
66 
67 /* clears gain info if tuple == NULL */
68 static void read_gain_from_tuple (const Tuple * tuple)
69 {
70  memset (& gain_from_playlist, 0, sizeof gain_from_playlist);
71 
72  if (tuple == NULL)
73  return;
74 
75  int album_gain = tuple_get_int (tuple, FIELD_GAIN_ALBUM_GAIN, NULL);
76  int album_peak = tuple_get_int (tuple, FIELD_GAIN_ALBUM_PEAK, NULL);
77  int track_gain = tuple_get_int (tuple, FIELD_GAIN_TRACK_GAIN, NULL);
78  int track_peak = tuple_get_int (tuple, FIELD_GAIN_TRACK_PEAK, NULL);
79  int gain_unit = tuple_get_int (tuple, FIELD_GAIN_GAIN_UNIT, NULL);
80  int peak_unit = tuple_get_int (tuple, FIELD_GAIN_PEAK_UNIT, NULL);
81 
82  if (gain_unit)
83  {
84  gain_from_playlist.album_gain = album_gain / (float) gain_unit;
85  gain_from_playlist.track_gain = track_gain / (float) gain_unit;
86  }
87 
88  if (peak_unit)
89  {
90  gain_from_playlist.album_peak = album_peak / (float) peak_unit;
91  gain_from_playlist.track_peak = track_peak / (float) peak_unit;
92  }
93 }
94 
96 {
98  char * title = playback_entry_get_title ();
99  int length = playback_entry_get_length ();
100 
101  if (entry == current_entry && ! g_strcmp0 (title, current_title) && length == current_length)
102  {
103  str_unref (title);
104  return FALSE;
105  }
106 
109  current_title = title;
110  current_length = length;
111  return TRUE;
112 }
113 
115 {
116  g_return_val_if_fail (playing, FALSE);
117  pthread_mutex_lock (& ready_mutex);
118  bool_t ready = ready_flag;
119  pthread_mutex_unlock (& ready_mutex);
120  return ready;
121 }
122 
123 static void set_pb_ready (InputPlayback * p)
124 {
125  g_return_if_fail (playing);
126  pthread_mutex_lock (& ready_mutex);
127 
129  ready_flag = TRUE;
130 
131  pthread_cond_signal (& ready_cond);
132  pthread_mutex_unlock (& ready_mutex);
133 
134  event_queue ("playback ready", NULL);
135 }
136 
137 static void wait_until_ready (void)
138 {
139  g_return_if_fail (playing);
140  pthread_mutex_lock (& ready_mutex);
141 
142  while (! ready_flag)
143  pthread_cond_wait (& ready_cond, & ready_mutex);
144 
145  pthread_mutex_unlock (& ready_mutex);
146 }
147 
148 static void update_cb (void * hook_data, void * user_data)
149 {
150  g_return_if_fail (playing);
151 
152  if (GPOINTER_TO_INT (hook_data) < PLAYLIST_UPDATE_METADATA || ! playback_get_ready ())
153  return;
154 
155  if (update_from_playlist ())
156  event_queue ("title change", NULL);
157 }
158 
160 {
161  g_return_val_if_fail (playing, 0);
162  wait_until_ready ();
163 
164  int time = -1;
165 
166  if (current_decoder && current_decoder->get_time)
167  time = current_decoder->get_time (& playback_api);
168 
169  if (time < 0)
170  time = get_output_time ();
171 
172  return time - time_offset;
173 }
174 
175 void playback_play (int seek_time, bool_t pause)
176 {
177  g_return_if_fail (! playing);
178 
180 
181  if (playlist == -1)
182  {
183  playlist = playlist_get_active ();
184  playlist_set_playing (playlist);
185  }
186 
187  int entry = playlist_get_position (playlist);
188 
189  if (entry == -1)
190  {
191  playlist_next_song (playlist, TRUE);
192  entry = playlist_get_position (playlist);
193 
194  if (entry == -1)
195  return;
196  }
197 
198  failed_entries = 0;
199  playback_start (playlist, entry, seek_time, pause);
200 }
201 
202 void playback_pause (void)
203 {
204  g_return_if_fail (playing);
205  wait_until_ready ();
206 
207  if (! current_decoder || ! current_decoder->pause)
208  return;
209 
210  paused = ! paused;
211  current_decoder->pause (& playback_api, paused);
212 
213  if (paused)
214  hook_call ("playback pause", NULL);
215  else
216  hook_call ("playback unpause", NULL);
217 }
218 
219 static void playback_cleanup (void)
220 {
221  g_return_if_fail (playing);
222 
223  pthread_join (playback_thread_handle, NULL);
224  playing = FALSE;
225 
226  event_queue_cancel ("playback ready", NULL);
227  event_queue_cancel ("info change", NULL);
228  event_queue_cancel ("title change", NULL);
229 
230  if (end_source)
231  {
232  g_source_remove (end_source);
233  end_source = 0;
234  }
235 
240 
241  hook_dissociate ("playlist update", update_cb);
242 }
243 
244 static void complete_stop (void)
245 {
246  output_drain ();
247  hook_call ("playback stop", NULL);
248  set_bool (NULL, "stop_after_current_song", FALSE);
249 }
250 
251 void playback_stop (void)
252 {
253  g_return_if_fail (playing);
254  wait_until_ready ();
255 
256  if (current_decoder)
257  current_decoder->stop (& playback_api);
258 
259  playback_cleanup ();
260  complete_stop ();
261 }
262 
263 static bool_t end_cb (void * unused)
264 {
265  g_return_val_if_fail (playing, FALSE);
266 
267  hook_call ("playback end", NULL);
268 
269  if (playback_error)
270  failed_entries ++;
271  else
272  failed_entries = 0;
273 
274  playback_cleanup ();
275 
277  bool_t play;
278 
279  if (get_bool (NULL, "no_playlist_advance"))
280  play = get_bool (NULL, "repeat") && ! failed_entries;
281  else if (! (play = playlist_next_song (playlist, get_bool (NULL, "repeat"))))
282  playlist_set_position (playlist, -1);
283  else if (failed_entries >= 10)
284  play = FALSE;
285 
286  if (get_bool (NULL, "stop_after_current_song"))
287  play = FALSE;
288 
289  if (play)
290  playback_start (playlist, playlist_get_position (playlist), 0, FALSE);
291  else
292  {
293  complete_stop ();
294  hook_call ("playlist end reached", NULL);
295  }
296 
297  return FALSE;
298 }
299 
300 static void * playback_thread (void * unused)
301 {
304 
305  if (! current_decoder)
306  {
307  char * error = g_strdup_printf (_("No decoder found for %s."),
309  interface_show_error (error);
310  g_free (error);
312  goto DONE;
313  }
314 
315  current_data = NULL;
316  current_bitrate = 0;
317  current_samplerate = 0;
318  current_channels = 0;
319 
320  Tuple * tuple = playback_entry_get_tuple ();
321  read_gain_from_tuple (tuple);
322  if (tuple)
323  tuple_unref (tuple);
324 
325  bool_t seekable = (playback_entry_get_length () > 0);
326 
327  VFSFile * file = vfs_fopen (current_filename, "r");
328 
329  time_offset = seekable ? playback_entry_get_start_time () : 0;
331  file, seekable ? time_offset + initial_seek : 0,
332  seekable ? playback_entry_get_end_time () : -1, paused);
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 
360  initial_seek = seek_time;
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  hook_call ("playback seek", NULL);
392 }
393 
394 static void set_data (InputPlayback * p, void * data)
395 {
396  g_return_if_fail (playing);
397  current_data = data;
398 }
399 
400 static void * get_data (InputPlayback * p)
401 {
402  g_return_val_if_fail (playing, NULL);
403  return current_data;
404 }
405 
406 static void set_params (InputPlayback * p, int bitrate, int samplerate,
407  int channels)
408 {
409  g_return_if_fail (playing);
410 
411  current_bitrate = bitrate;
412  current_samplerate = samplerate;
414 
415  if (playback_get_ready ())
416  event_queue ("info change", NULL);
417 }
418 
419 static void set_tuple (InputPlayback * p, Tuple * tuple)
420 {
421  g_return_if_fail (playing);
422  read_gain_from_tuple (tuple);
423  playback_entry_set_tuple (tuple);
424 }
425 
426 static void set_gain_from_playlist (InputPlayback * p)
427 {
428  g_return_if_fail (playing);
429  p->output->set_replaygain_info (& gain_from_playlist);
430 }
431 
432 static InputPlayback playback_api = {
433  .output = & output_api,
434  .set_data = set_data,
435  .get_data = get_data,
436  .set_pb_ready = set_pb_ready,
437  .set_params = set_params,
438  .set_tuple = set_tuple,
439  .set_gain_from_playlist = set_gain_from_playlist,
440 };
441 
443 {
444  g_return_val_if_fail (playing, NULL);
445  return str_ref (current_filename);
446 }
447 
448 char * playback_get_title (void)
449 {
450  g_return_val_if_fail (playing, NULL);
451  wait_until_ready ();
452 
453  char s[32];
454 
455  if (current_length)
456  {
457  int len = current_length / 1000;
458 
459  if (len < 3600)
460  snprintf (s, sizeof s, get_bool (NULL, "leading_zero") ?
461  " (%02d:%02d)" : " (%d:%02d)", len / 60, len % 60);
462  else
463  snprintf (s, sizeof s, " (%d:%02d:%02d)", len / 3600, (len / 60) %
464  60, len % 60);
465  }
466  else
467  s[0] = 0;
468 
469  if (get_bool (NULL, "show_numbers_in_pl"))
470  return str_printf ("%d. %s%s", 1 + playlist_get_position
472 
473  return str_printf ("%s%s", current_title, s);
474 }
475 
477 {
478  g_return_val_if_fail (playing, 0);
479  wait_until_ready ();
480 
481  return current_length;
482 }
483 
484 void playback_get_info (int * bitrate, int * samplerate, int * channels)
485 {
486  g_return_if_fail (playing);
487  wait_until_ready ();
488 
489  * bitrate = current_bitrate;
490  * samplerate = current_samplerate;
491  * channels = current_channels;
492 }
493 
494 void playback_get_volume (int * l, int * r)
495 {
497  current_decoder->get_volume && current_decoder->get_volume (l, r))
498  return;
499 
500  output_get_volume (l, r);
501 }
502 
503 void playback_set_volume (int l, int r)
504 {
505  int h_vol[2] = {l, r};
506 
507  hook_call ("volume set", h_vol);
508 
510  current_decoder->set_volume && current_decoder->set_volume (l, r))
511  return;
512 
513  output_set_volume (l, r);
514 }