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 const 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     const gchar * title = playlist_entry_get_title (playlist, entry, FALSE);
00167 
00168     if (title == NULL)
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         return;
00176 
00177     current_entry = entry;
00178     g_free (current_title);
00179     current_title = g_strdup (title);
00180     current_length = length;
00181 
00182     if (playback_get_ready ())
00183         hook_call ("title change", NULL);
00184 }
00185 
00186 gint playback_get_time (void)
00187 {
00188     g_return_val_if_fail (playing, 0);
00189 
00190     if (! playback_get_ready ())
00191         return 0;
00192 
00193     gint time = -1;
00194 
00195     if (current_decoder->get_time != NULL)
00196         time = current_decoder->get_time (& playback_api);
00197 
00198     if (time < 0)
00199         time = get_output_time ();
00200 
00201     return time - time_offset;
00202 }
00203 
00204 void playback_play (gint seek_time, gboolean pause)
00205 {
00206     g_return_if_fail (! playing);
00207 
00208     gint playlist = playlist_get_playing ();
00209 
00210     if (playlist == -1)
00211     {
00212         playlist = playlist_get_active ();
00213         playlist_set_playing (playlist);
00214     }
00215 
00216     gint entry = playlist_get_position (playlist);
00217 
00218     if (entry == -1)
00219     {
00220         playlist_next_song (playlist, TRUE);
00221         entry = playlist_get_position (playlist);
00222 
00223         if (entry == -1)
00224             return;
00225     }
00226 
00227     failed_entries = 0;
00228     playback_start (playlist, entry, seek_time, pause);
00229 }
00230 
00231 void playback_pause (void)
00232 {
00233     g_return_if_fail (playing);
00234     wait_until_ready ();
00235 
00236     paused = ! paused;
00237 
00238     g_return_if_fail (current_decoder->pause != NULL);
00239     current_decoder->pause (& playback_api, paused);
00240 
00241     if (paused)
00242         hook_call ("playback pause", NULL);
00243     else
00244         hook_call ("playback unpause", NULL);
00245 }
00246 
00247 static void playback_cleanup (void)
00248 {
00249     g_return_if_fail (playing);
00250 
00251     pthread_join (playback_thread_handle, NULL);
00252     playing = FALSE;
00253     playback_error = FALSE;
00254 
00255     g_free (current_title);
00256 
00257     if (ready_source)
00258     {
00259         g_source_remove (ready_source);
00260         ready_source = 0;
00261     }
00262 
00263     cancel_set_tuple ();
00264     hook_dissociate ("playlist update", update_cb);
00265 }
00266 
00267 static void complete_stop (void)
00268 {
00269     output_drain ();
00270     hook_call ("playback stop", NULL);
00271 
00272     if (cfg.stopaftersong)
00273     {
00274         cfg.stopaftersong = FALSE;
00275         hook_call ("toggle stop after song", NULL);
00276     }
00277 }
00278 
00279 void playback_stop (void)
00280 {
00281     g_return_if_fail (playing);
00282     wait_until_ready ();
00283 
00284     current_decoder->stop (& playback_api);
00285     playback_cleanup ();
00286     complete_stop ();
00287 
00288     if (end_source)
00289     {
00290         g_source_remove (end_source);
00291         end_source = 0;
00292     }
00293 }
00294 
00295 static gboolean end_cb (void * unused)
00296 {
00297     g_return_val_if_fail (playing, FALSE);
00298 
00299     hook_call ("playback end", NULL);
00300 
00301     if (playback_error)
00302         failed_entries ++;
00303     else
00304         failed_entries = 0;
00305 
00306     playback_cleanup ();
00307 
00308     gint playlist = playlist_get_playing ();
00309 
00310     while (1)
00311     {
00312         gboolean play;
00313 
00314         if (cfg.no_playlist_advance)
00315             play = cfg.repeat && ! failed_entries;
00316         else if (! (play = playlist_next_song (playlist, cfg.repeat)))
00317             playlist_set_position (playlist, -1);
00318         else if (failed_entries >= 10)
00319             play = FALSE;
00320 
00321         if (cfg.stopaftersong)
00322             play = FALSE;
00323 
00324         if (! play)
00325         {
00326             complete_stop ();
00327             hook_call ("playlist end reached", NULL);
00328             break;
00329         }
00330 
00331         if (playback_start (playlist, playlist_get_position (playlist), 0, FALSE))
00332             break;
00333 
00334         failed_entries ++;
00335     }
00336 
00337     end_source = 0;
00338     return FALSE;
00339 }
00340 
00341 static void * playback_thread (void * unused)
00342 {
00343     gchar * real = filename_split_subtune (current_filename, NULL);
00344     VFSFile * file = vfs_fopen (real, "r");
00345     g_free (real);
00346 
00347     playback_error = ! current_decoder->play (& playback_api, current_filename,
00348      file, start_time, stop_time, paused);
00349 
00350     if (file != NULL)
00351         vfs_fclose (file);
00352 
00353     if (! ready_flag)
00354         set_pb_ready (& playback_api);
00355 
00356     end_source = g_timeout_add (0, end_cb, NULL);
00357     return NULL;
00358 }
00359 
00360 static gboolean playback_start (gint playlist, gint entry, gint seek_time,
00361  gboolean pause)
00362 {
00363     g_return_val_if_fail (! playing, FALSE);
00364 
00365     current_entry = entry;
00366     current_filename = playlist_entry_get_filename (playlist, entry);
00367 
00368     vfs_prepare_filename (current_filename);
00369 
00370     PluginHandle * p = playlist_entry_get_decoder (playlist, entry, FALSE);
00371     current_decoder = p ? plugin_get_header (p) : NULL;
00372 
00373     if (current_decoder == NULL)
00374     {
00375         gchar * error = g_strdup_printf (_("No decoder found for %s."),
00376          current_filename);
00377         /* The interface may not be up yet at this point. --jlindgren */
00378         event_queue_with_data_free ("interface show error", error);
00379         return FALSE;
00380     }
00381 
00382     current_data = NULL;
00383     current_bitrate = 0;
00384     current_samplerate = 0;
00385     current_channels = 0;
00386 
00387     const gchar * title = playlist_entry_get_title (playlist, entry, FALSE);
00388     current_title = g_strdup ((title != NULL) ? title : current_filename);
00389 
00390     current_length = playlist_entry_get_length (playlist, entry, FALSE);
00391     read_gain_from_tuple (playlist_entry_get_tuple (playlist, entry, FALSE));
00392 
00393     if (current_length > 0 && playlist_entry_is_segmented (playlist, entry))
00394     {
00395         time_offset = playlist_entry_get_start_time (playlist, entry);
00396         stop_time = playlist_entry_get_end_time (playlist, entry);
00397     }
00398     else
00399     {
00400         time_offset = 0;
00401         stop_time = -1;
00402     }
00403 
00404     if (current_length > 0)
00405         start_time = time_offset + seek_time;
00406     else
00407         start_time = 0;
00408 
00409     playing = TRUE;
00410     playback_error = FALSE;
00411     paused = pause;
00412     ready_flag = FALSE;
00413 
00414     pthread_create (& playback_thread_handle, NULL, playback_thread, NULL);
00415 
00416     hook_associate ("playlist update", update_cb, NULL);
00417     hook_call ("playback begin", NULL);
00418     return TRUE;
00419 }
00420 
00421 gboolean playback_get_playing (void)
00422 {
00423     return playing;
00424 }
00425 
00426 gboolean playback_get_paused (void)
00427 {
00428     g_return_val_if_fail (playing, FALSE);
00429     return paused;
00430 }
00431 
00432 void playback_seek (gint time)
00433 {
00434     g_return_if_fail (playing);
00435     wait_until_ready ();
00436 
00437     if (current_decoder->mseek == NULL || playback_get_length () < 1)
00438         return;
00439 
00440     current_decoder->mseek (& playback_api, time_offset + CLAMP (time, 0,
00441      current_length));
00442 
00443     hook_call ("playback seek", NULL);
00444 }
00445 
00446 static void set_data (InputPlayback * p, void * data)
00447 {
00448     g_return_if_fail (playing);
00449     current_data = data;
00450 }
00451 
00452 static void * get_data (InputPlayback * p)
00453 {
00454     g_return_val_if_fail (playing, NULL);
00455     return current_data;
00456 }
00457 
00458 static void set_params (InputPlayback * p, gint bitrate, gint samplerate,
00459  gint channels)
00460 {
00461     g_return_if_fail (playing);
00462 
00463     current_bitrate = bitrate;
00464     current_samplerate = samplerate;
00465     current_channels = channels;
00466 
00467     event_queue ("info change", NULL);
00468 }
00469 
00470 static gboolean set_tuple_cb (void * unused)
00471 {
00472     g_return_val_if_fail (playing, FALSE);
00473     pthread_mutex_lock (& ready_mutex);
00474 
00475     gint playlist = playlist_get_playing ();
00476     playlist_entry_set_tuple (playlist, playlist_get_position (playlist),
00477      tuple_to_be_set);
00478     set_tuple_source = 0;
00479     tuple_to_be_set = NULL;
00480 
00481     pthread_mutex_unlock (& ready_mutex);
00482     return FALSE;
00483 }
00484 
00485 static void set_tuple (InputPlayback * p, Tuple * tuple)
00486 {
00487     g_return_if_fail (playing);
00488     pthread_mutex_lock (& ready_mutex);
00489 
00490     cancel_set_tuple ();
00491     set_tuple_source = g_timeout_add (0, set_tuple_cb, NULL);
00492     tuple_to_be_set = tuple;
00493 
00494     read_gain_from_tuple (tuple);
00495     pthread_mutex_unlock (& ready_mutex);
00496 }
00497 
00498 static void set_gain_from_playlist (InputPlayback * p)
00499 {
00500     g_return_if_fail (playing);
00501     p->output->set_replaygain_info (& gain_from_playlist);
00502 }
00503 
00504 static InputPlayback playback_api = {
00505     .output = & output_api,
00506     .set_data = set_data,
00507     .get_data = get_data,
00508     .set_pb_ready = set_pb_ready,
00509     .set_params = set_params,
00510     .set_tuple = set_tuple,
00511     .set_gain_from_playlist = set_gain_from_playlist,
00512 };
00513 
00514 gchar * playback_get_title (void)
00515 {
00516     g_return_val_if_fail (playing, NULL);
00517 
00518     if (! playback_get_ready ())
00519         return g_strdup (_("Buffering ..."));
00520 
00521     gchar s[128];
00522 
00523     if (current_length)
00524     {
00525         gint len = current_length / 1000;
00526 
00527         if (len < 3600)
00528             snprintf (s, sizeof s, cfg.leading_zero ? " (%02d:%02d)" :
00529              " (%d:%02d)", len / 60, len % 60);
00530         else
00531             snprintf (s, sizeof s, " (%d:%02d:%02d)", len / 3600, (len / 60) %
00532              60, len % 60);
00533     }
00534     else
00535         s[0] = 0;
00536 
00537     if (cfg.show_numbers_in_pl)
00538         return g_strdup_printf ("%d. %s%s", 1 + playlist_get_position
00539          (playlist_get_playing ()), current_title, s);
00540 
00541     return g_strdup_printf ("%s%s", current_title, s);
00542 }
00543 
00544 gint playback_get_length (void)
00545 {
00546     g_return_val_if_fail (playing, 0);
00547     return current_length;
00548 }
00549 
00550 void playback_get_info (gint * bitrate, gint * samplerate, gint * channels)
00551 {
00552     g_return_if_fail (playing);
00553     * bitrate = current_bitrate;
00554     * samplerate = current_samplerate;
00555     * channels = current_channels;
00556 }
00557 
00558 void playback_get_volume (gint * l, gint * r)
00559 {
00560     if (playing && current_decoder->get_volume != NULL &&
00561      current_decoder->get_volume (l, r))
00562         return;
00563 
00564     output_get_volume (l, r);
00565 }
00566 
00567 void playback_set_volume(gint l, gint r)
00568 {
00569     gint h_vol[2] = {l, r};
00570 
00571     hook_call ("volume set", h_vol);
00572 
00573     if (playing && current_decoder->set_volume != NULL &&
00574      current_decoder->set_volume (l, r))
00575         return;
00576 
00577     output_set_volume (l, r);
00578 }