Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
playback.c
Go to the documentation of this file.
00001 /*
00002  * playback.c
00003  * Copyright 2005-2011 Audacious Development Team
00004  *
00005  * This file is part of Audacious.
00006  *
00007  * Audacious is free software: you can redistribute it and/or modify it under
00008  * the terms of the GNU General Public License as published by the Free Software
00009  * Foundation, version 2 or version 3 of the License.
00010  *
00011  * Audacious is distributed in the hope that it will be useful, but WITHOUT ANY
00012  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
00013  * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License along with
00016  * Audacious. If not, see <http://www.gnu.org/licenses/>.
00017  *
00018  * The Audacious team does not consider modular code linking to Audacious or
00019  * using our public API to be a derived work.
00020  */
00021 
00022 #include <glib.h>
00023 #include <pthread.h>
00024 
00025 #include <libaudcore/audstrings.h>
00026 #include <libaudcore/eventqueue.h>
00027 #include <libaudcore/hook.h>
00028 
00029 #include "audconfig.h"
00030 #include "config.h"
00031 #include "i18n.h"
00032 #include "interface.h"
00033 #include "output.h"
00034 #include "playback.h"
00035 #include "playlist.h"
00036 
00037 static gboolean playback_start (gint playlist, gint entry, gint seek_time,
00038  gboolean pause);
00039 
00040 static InputPlayback playback_api;
00041 
00042 static gboolean playing = FALSE;
00043 static gboolean playback_error;
00044 static gint failed_entries;
00045 
00046 static gint current_entry;
00047 static gchar * current_filename;
00048 static InputPlugin * current_decoder;
00049 static void * current_data;
00050 static gint current_bitrate, current_samplerate, current_channels;
00051 static gchar * current_title;
00052 static gint current_length;
00053 
00054 static ReplayGainInfo gain_from_playlist;
00055 
00056 static gint time_offset, start_time, stop_time;
00057 static gboolean paused;
00058 
00059 static pthread_t playback_thread_handle;
00060 static gint end_source = 0;
00061 
00062 static pthread_mutex_t ready_mutex = PTHREAD_MUTEX_INITIALIZER;
00063 static pthread_cond_t ready_cond = PTHREAD_COND_INITIALIZER;
00064 static gboolean ready_flag;
00065 static gint ready_source = 0;
00066 
00067 static gint set_tuple_source = 0;
00068 static Tuple * tuple_to_be_set = NULL;
00069 
00070 static void cancel_set_tuple (void)
00071 {
00072     if (set_tuple_source != 0)
00073     {
00074         g_source_remove (set_tuple_source);
00075         set_tuple_source = 0;
00076     }
00077 
00078     if (tuple_to_be_set != NULL)
00079     {
00080         tuple_free (tuple_to_be_set);
00081         tuple_to_be_set = NULL;
00082     }
00083 }
00084 
00085 /* clears gain info if tuple == NULL */
00086 static void read_gain_from_tuple (const Tuple * tuple)
00087 {
00088     gint album_gain, album_peak, track_gain, track_peak, gain_unit, peak_unit;
00089 
00090     memset (& gain_from_playlist, 0, sizeof gain_from_playlist);
00091 
00092     if (tuple == NULL)
00093         return;
00094 
00095     album_gain = tuple_get_int (tuple, FIELD_GAIN_ALBUM_GAIN, NULL);
00096     album_peak = tuple_get_int (tuple, FIELD_GAIN_ALBUM_PEAK, NULL);
00097     track_gain = tuple_get_int (tuple, FIELD_GAIN_TRACK_GAIN, NULL);
00098     track_peak = tuple_get_int (tuple, FIELD_GAIN_TRACK_PEAK, NULL);
00099     gain_unit = tuple_get_int (tuple, FIELD_GAIN_GAIN_UNIT, NULL);
00100     peak_unit = tuple_get_int (tuple, FIELD_GAIN_PEAK_UNIT, NULL);
00101 
00102     if (gain_unit)
00103     {
00104         gain_from_playlist.album_gain = album_gain / (gfloat) gain_unit;
00105         gain_from_playlist.track_gain = track_gain / (gfloat) gain_unit;
00106     }
00107 
00108     if (peak_unit)
00109     {
00110         gain_from_playlist.album_peak = album_peak / (gfloat) peak_unit;
00111         gain_from_playlist.track_peak = track_peak / (gfloat) peak_unit;
00112     }
00113 }
00114 
00115 static gboolean ready_cb (void * unused)
00116 {
00117     g_return_val_if_fail (playing, FALSE);
00118 
00119     hook_call ("playback ready", NULL);
00120     hook_call ("title change", NULL);
00121     ready_source = 0;
00122     return FALSE;
00123 }
00124 
00125 gboolean playback_get_ready (void)
00126 {
00127     g_return_val_if_fail (playing, FALSE);
00128     pthread_mutex_lock (& ready_mutex);
00129     gboolean ready = ready_flag;
00130     pthread_mutex_unlock (& ready_mutex);
00131     return ready;
00132 }
00133 
00134 static void set_pb_ready (InputPlayback * p)
00135 {
00136     g_return_if_fail (playing);
00137 
00138     pthread_mutex_lock (& ready_mutex);
00139     ready_flag = TRUE;
00140     pthread_cond_signal (& ready_cond);
00141     pthread_mutex_unlock (& ready_mutex);
00142 
00143     ready_source = g_timeout_add (0, ready_cb, NULL);
00144 }
00145 
00146 static void wait_until_ready (void)
00147 {
00148     g_return_if_fail (playing);
00149     pthread_mutex_lock (& ready_mutex);
00150 
00151     while (! ready_flag)
00152         pthread_cond_wait (& ready_cond, & ready_mutex);
00153 
00154     pthread_mutex_unlock (& ready_mutex);
00155 }
00156 
00157 static void update_cb (void * hook_data, void * user_data)
00158 {
00159     g_return_if_fail (playing);
00160 
00161     if (GPOINTER_TO_INT (hook_data) < PLAYLIST_UPDATE_METADATA)
00162         return;
00163 
00164     gint playlist = playlist_get_playing ();
00165     gint entry = playlist_get_position (playlist);
00166 
00167     gchar * title = playlist_entry_get_title (playlist, entry, FALSE);
00168     if (! title)
00169         title = playlist_entry_get_filename (playlist, entry);
00170 
00171     gint length = playlist_entry_get_length (playlist, entry, FALSE);
00172 
00173     if (entry == current_entry && ! strcmp (title, current_title) && length ==
00174      current_length)
00175     {
00176         g_free (title);
00177         return;
00178     }
00179 
00180     current_entry = entry;
00181     g_free (current_title);
00182     current_title = title;
00183     current_length = length;
00184 
00185     if (playback_get_ready ())
00186         hook_call ("title change", NULL);
00187 }
00188 
00189 gint playback_get_time (void)
00190 {
00191     g_return_val_if_fail (playing, 0);
00192 
00193     if (! playback_get_ready ())
00194         return 0;
00195 
00196     gint time = -1;
00197 
00198     if (current_decoder->get_time != NULL)
00199         time = current_decoder->get_time (& playback_api);
00200 
00201     if (time < 0)
00202         time = get_output_time ();
00203 
00204     return time - time_offset;
00205 }
00206 
00207 void playback_play (gint seek_time, gboolean pause)
00208 {
00209     g_return_if_fail (! playing);
00210 
00211     gint playlist = playlist_get_playing ();
00212 
00213     if (playlist == -1)
00214     {
00215         playlist = playlist_get_active ();
00216         playlist_set_playing (playlist);
00217     }
00218 
00219     gint entry = playlist_get_position (playlist);
00220 
00221     if (entry == -1)
00222     {
00223         playlist_next_song (playlist, TRUE);
00224         entry = playlist_get_position (playlist);
00225 
00226         if (entry == -1)
00227             return;
00228     }
00229 
00230     failed_entries = 0;
00231     playback_start (playlist, entry, seek_time, pause);
00232 }
00233 
00234 void playback_pause (void)
00235 {
00236     g_return_if_fail (playing);
00237     wait_until_ready ();
00238 
00239     paused = ! paused;
00240 
00241     g_return_if_fail (current_decoder->pause != NULL);
00242     current_decoder->pause (& playback_api, paused);
00243 
00244     if (paused)
00245         hook_call ("playback pause", NULL);
00246     else
00247         hook_call ("playback unpause", NULL);
00248 }
00249 
00250 static void playback_cleanup (void)
00251 {
00252     g_return_if_fail (playing);
00253 
00254     pthread_join (playback_thread_handle, NULL);
00255     playing = FALSE;
00256     playback_error = FALSE;
00257 
00258     g_free (current_filename);
00259     current_filename = NULL;
00260     g_free (current_title);
00261     current_title = NULL;
00262 
00263     if (ready_source)
00264     {
00265         g_source_remove (ready_source);
00266         ready_source = 0;
00267     }
00268 
00269     cancel_set_tuple ();
00270     hook_dissociate ("playlist update", update_cb);
00271 }
00272 
00273 static void complete_stop (void)
00274 {
00275     output_drain ();
00276     hook_call ("playback stop", NULL);
00277 
00278     if (cfg.stopaftersong)
00279     {
00280         cfg.stopaftersong = FALSE;
00281         hook_call ("toggle stop after song", NULL);
00282     }
00283 }
00284 
00285 void playback_stop (void)
00286 {
00287     g_return_if_fail (playing);
00288     wait_until_ready ();
00289 
00290     current_decoder->stop (& playback_api);
00291     playback_cleanup ();
00292     complete_stop ();
00293 
00294     if (end_source)
00295     {
00296         g_source_remove (end_source);
00297         end_source = 0;
00298     }
00299 }
00300 
00301 static gboolean end_cb (void * unused)
00302 {
00303     g_return_val_if_fail (playing, FALSE);
00304 
00305     hook_call ("playback end", NULL);
00306 
00307     if (playback_error)
00308         failed_entries ++;
00309     else
00310         failed_entries = 0;
00311 
00312     playback_cleanup ();
00313 
00314     gint playlist = playlist_get_playing ();
00315 
00316     while (1)
00317     {
00318         gboolean play;
00319 
00320         if (cfg.no_playlist_advance)
00321             play = cfg.repeat && ! failed_entries;
00322         else if (! (play = playlist_next_song (playlist, cfg.repeat)))
00323             playlist_set_position (playlist, -1);
00324         else if (failed_entries >= 10)
00325             play = FALSE;
00326 
00327         if (cfg.stopaftersong)
00328             play = FALSE;
00329 
00330         if (! play)
00331         {
00332             complete_stop ();
00333             hook_call ("playlist end reached", NULL);
00334             break;
00335         }
00336 
00337         if (playback_start (playlist, playlist_get_position (playlist), 0, FALSE))
00338             break;
00339 
00340         failed_entries ++;
00341     }
00342 
00343     end_source = 0;
00344     return FALSE;
00345 }
00346 
00347 static void * playback_thread (void * unused)
00348 {
00349     gchar * real = filename_split_subtune (current_filename, NULL);
00350     VFSFile * file = vfs_fopen (real, "r");
00351     g_free (real);
00352 
00353     playback_error = ! current_decoder->play (& playback_api, current_filename,
00354      file, start_time, stop_time, paused);
00355 
00356     if (file != NULL)
00357         vfs_fclose (file);
00358 
00359     if (! ready_flag)
00360         set_pb_ready (& playback_api);
00361 
00362     end_source = g_timeout_add (0, end_cb, NULL);
00363     return NULL;
00364 }
00365 
00366 static gboolean playback_start (gint playlist, gint entry, gint seek_time,
00367  gboolean pause)
00368 {
00369     g_return_val_if_fail (! playing, FALSE);
00370 
00371     current_entry = entry;
00372 
00373     g_free (current_filename);
00374     current_filename = playlist_entry_get_filename (playlist, entry);
00375 
00376     PluginHandle * p = playlist_entry_get_decoder (playlist, entry, FALSE);
00377     current_decoder = p ? plugin_get_header (p) : NULL;
00378 
00379     if (current_decoder == NULL)
00380     {
00381         gchar * error = g_strdup_printf (_("No decoder found for %s."),
00382          current_filename);
00383         /* The interface may not be up yet at this point. --jlindgren */
00384         event_queue_with_data_free ("interface show error", error);
00385         return FALSE;
00386     }
00387 
00388     current_data = NULL;
00389     current_bitrate = 0;
00390     current_samplerate = 0;
00391     current_channels = 0;
00392 
00393     g_free (current_title);
00394     current_title = playlist_entry_get_title (playlist, entry, FALSE);
00395     if (! current_title)
00396         current_title = g_strdup (current_filename);
00397 
00398     current_length = playlist_entry_get_length (playlist, entry, FALSE);
00399 
00400     Tuple * tuple = playlist_entry_get_tuple (playlist, entry, FALSE);
00401     read_gain_from_tuple (tuple);
00402     if (tuple)
00403         tuple_free (tuple);
00404 
00405     if (current_length > 0 && playlist_entry_is_segmented (playlist, entry))
00406     {
00407         time_offset = playlist_entry_get_start_time (playlist, entry);
00408         stop_time = playlist_entry_get_end_time (playlist, entry);
00409     }
00410     else
00411     {
00412         time_offset = 0;
00413         stop_time = -1;
00414     }
00415 
00416     if (current_length > 0)
00417         start_time = time_offset + seek_time;
00418     else
00419         start_time = 0;
00420 
00421     playing = TRUE;
00422     playback_error = FALSE;
00423     paused = pause;
00424     ready_flag = FALSE;
00425 
00426     pthread_create (& playback_thread_handle, NULL, playback_thread, NULL);
00427 
00428     hook_associate ("playlist update", update_cb, NULL);
00429     hook_call ("playback begin", NULL);
00430     return TRUE;
00431 }
00432 
00433 gboolean playback_get_playing (void)
00434 {
00435     return playing;
00436 }
00437 
00438 gboolean playback_get_paused (void)
00439 {
00440     g_return_val_if_fail (playing, FALSE);
00441     return paused;
00442 }
00443 
00444 void playback_seek (gint time)
00445 {
00446     g_return_if_fail (playing);
00447     wait_until_ready ();
00448 
00449     if (current_decoder->mseek == NULL || playback_get_length () < 1)
00450         return;
00451 
00452     current_decoder->mseek (& playback_api, time_offset + CLAMP (time, 0,
00453      current_length));
00454 
00455     hook_call ("playback seek", NULL);
00456 }
00457 
00458 static void set_data (InputPlayback * p, void * data)
00459 {
00460     g_return_if_fail (playing);
00461     current_data = data;
00462 }
00463 
00464 static void * get_data (InputPlayback * p)
00465 {
00466     g_return_val_if_fail (playing, NULL);
00467     return current_data;
00468 }
00469 
00470 static void set_params (InputPlayback * p, gint bitrate, gint samplerate,
00471  gint channels)
00472 {
00473     g_return_if_fail (playing);
00474 
00475     current_bitrate = bitrate;
00476     current_samplerate = samplerate;
00477     current_channels = channels;
00478 
00479     event_queue ("info change", NULL);
00480 }
00481 
00482 static gboolean set_tuple_cb (void * unused)
00483 {
00484     g_return_val_if_fail (playing, FALSE);
00485     pthread_mutex_lock (& ready_mutex);
00486 
00487     gint playlist = playlist_get_playing ();
00488     playlist_entry_set_tuple (playlist, playlist_get_position (playlist),
00489      tuple_to_be_set);
00490     set_tuple_source = 0;
00491     tuple_to_be_set = NULL;
00492 
00493     pthread_mutex_unlock (& ready_mutex);
00494     return FALSE;
00495 }
00496 
00497 static void set_tuple (InputPlayback * p, Tuple * tuple)
00498 {
00499     g_return_if_fail (playing);
00500     pthread_mutex_lock (& ready_mutex);
00501 
00502     cancel_set_tuple ();
00503     set_tuple_source = g_timeout_add (0, set_tuple_cb, NULL);
00504     tuple_to_be_set = tuple;
00505 
00506     read_gain_from_tuple (tuple);
00507     pthread_mutex_unlock (& ready_mutex);
00508 }
00509 
00510 static void set_gain_from_playlist (InputPlayback * p)
00511 {
00512     g_return_if_fail (playing);
00513     p->output->set_replaygain_info (& gain_from_playlist);
00514 }
00515 
00516 static InputPlayback playback_api = {
00517     .output = & output_api,
00518     .set_data = set_data,
00519     .get_data = get_data,
00520     .set_pb_ready = set_pb_ready,
00521     .set_params = set_params,
00522     .set_tuple = set_tuple,
00523     .set_gain_from_playlist = set_gain_from_playlist,
00524 };
00525 
00526 gchar * playback_get_title (void)
00527 {
00528     g_return_val_if_fail (playing, NULL);
00529 
00530     if (! playback_get_ready ())
00531         return g_strdup (_("Buffering ..."));
00532 
00533     gchar s[128];
00534 
00535     if (current_length)
00536     {
00537         gint len = current_length / 1000;
00538 
00539         if (len < 3600)
00540             snprintf (s, sizeof s, cfg.leading_zero ? " (%02d:%02d)" :
00541              " (%d:%02d)", len / 60, len % 60);
00542         else
00543             snprintf (s, sizeof s, " (%d:%02d:%02d)", len / 3600, (len / 60) %
00544              60, len % 60);
00545     }
00546     else
00547         s[0] = 0;
00548 
00549     if (cfg.show_numbers_in_pl)
00550         return g_strdup_printf ("%d. %s%s", 1 + playlist_get_position
00551          (playlist_get_playing ()), current_title, s);
00552 
00553     return g_strdup_printf ("%s%s", current_title, s);
00554 }
00555 
00556 gint playback_get_length (void)
00557 {
00558     g_return_val_if_fail (playing, 0);
00559     return current_length;
00560 }
00561 
00562 void playback_get_info (gint * bitrate, gint * samplerate, gint * channels)
00563 {
00564     g_return_if_fail (playing);
00565     * bitrate = current_bitrate;
00566     * samplerate = current_samplerate;
00567     * channels = current_channels;
00568 }
00569 
00570 void playback_get_volume (gint * l, gint * r)
00571 {
00572     if (playing && current_decoder->get_volume != NULL &&
00573      current_decoder->get_volume (l, r))
00574         return;
00575 
00576     output_get_volume (l, r);
00577 }
00578 
00579 void playback_set_volume(gint l, gint r)
00580 {
00581     gint h_vol[2] = {l, r};
00582 
00583     hook_call ("volume set", h_vol);
00584 
00585     if (playing && current_decoder->set_volume != NULL &&
00586      current_decoder->set_volume (l, r))
00587         return;
00588 
00589     output_set_volume (l, r);
00590 }