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 #include <string.h>
00025 
00026 #include <libaudcore/audstrings.h>
00027 #include <libaudcore/hook.h>
00028 
00029 #include "config.h"
00030 #include "i18n.h"
00031 #include "interface.h"
00032 #include "misc.h"
00033 #include "output.h"
00034 #include "playback.h"
00035 #include "playlist.h"
00036 
00037 static void playback_start (int playlist, int entry, int seek_time, bool_t pause);
00038 
00039 static InputPlayback playback_api;
00040 
00041 static bool_t playing = FALSE;
00042 static bool_t playback_error;
00043 static int failed_entries;
00044 
00045 static char * current_filename; /* pooled */
00046 
00047 static int current_entry;
00048 static char * current_title; /* pooled */
00049 static int current_length;
00050 
00051 static InputPlugin * current_decoder;
00052 static void * current_data;
00053 static int current_bitrate, current_samplerate, current_channels;
00054 
00055 static ReplayGainInfo gain_from_playlist;
00056 
00057 static int time_offset, initial_seek;
00058 static bool_t paused;
00059 
00060 static pthread_t playback_thread_handle;
00061 static int end_source = 0;
00062 
00063 static pthread_mutex_t ready_mutex = PTHREAD_MUTEX_INITIALIZER;
00064 static pthread_cond_t ready_cond = PTHREAD_COND_INITIALIZER;
00065 static bool_t ready_flag;
00066 
00067 /* clears gain info if tuple == NULL */
00068 static void read_gain_from_tuple (const Tuple * tuple)
00069 {
00070     memset (& gain_from_playlist, 0, sizeof gain_from_playlist);
00071 
00072     if (tuple == NULL)
00073         return;
00074 
00075     int album_gain = tuple_get_int (tuple, FIELD_GAIN_ALBUM_GAIN, NULL);
00076     int album_peak = tuple_get_int (tuple, FIELD_GAIN_ALBUM_PEAK, NULL);
00077     int track_gain = tuple_get_int (tuple, FIELD_GAIN_TRACK_GAIN, NULL);
00078     int track_peak = tuple_get_int (tuple, FIELD_GAIN_TRACK_PEAK, NULL);
00079     int gain_unit = tuple_get_int (tuple, FIELD_GAIN_GAIN_UNIT, NULL);
00080     int peak_unit = tuple_get_int (tuple, FIELD_GAIN_PEAK_UNIT, NULL);
00081 
00082     if (gain_unit)
00083     {
00084         gain_from_playlist.album_gain = album_gain / (float) gain_unit;
00085         gain_from_playlist.track_gain = track_gain / (float) gain_unit;
00086     }
00087 
00088     if (peak_unit)
00089     {
00090         gain_from_playlist.album_peak = album_peak / (float) peak_unit;
00091         gain_from_playlist.track_peak = track_peak / (float) peak_unit;
00092     }
00093 }
00094 
00095 static bool_t update_from_playlist (void)
00096 {
00097     int entry = playback_entry_get_position ();
00098     char * title = playback_entry_get_title ();
00099     int length = playback_entry_get_length ();
00100 
00101     if (entry == current_entry && ! g_strcmp0 (title, current_title) && length == current_length)
00102     {
00103         str_unref (title);
00104         return FALSE;
00105     }
00106 
00107     current_entry = entry;
00108     str_unref (current_title);
00109     current_title = title;
00110     current_length = length;
00111     return TRUE;
00112 }
00113 
00114 bool_t playback_get_ready (void)
00115 {
00116     g_return_val_if_fail (playing, FALSE);
00117     pthread_mutex_lock (& ready_mutex);
00118     bool_t ready = ready_flag;
00119     pthread_mutex_unlock (& ready_mutex);
00120     return ready;
00121 }
00122 
00123 static void set_pb_ready (InputPlayback * p)
00124 {
00125     g_return_if_fail (playing);
00126     pthread_mutex_lock (& ready_mutex);
00127 
00128     update_from_playlist ();
00129     ready_flag = TRUE;
00130 
00131     pthread_cond_signal (& ready_cond);
00132     pthread_mutex_unlock (& ready_mutex);
00133 
00134     event_queue ("playback ready", NULL);
00135 }
00136 
00137 static void wait_until_ready (void)
00138 {
00139     g_return_if_fail (playing);
00140     pthread_mutex_lock (& ready_mutex);
00141 
00142     while (! ready_flag)
00143         pthread_cond_wait (& ready_cond, & ready_mutex);
00144 
00145     pthread_mutex_unlock (& ready_mutex);
00146 }
00147 
00148 static void update_cb (void * hook_data, void * user_data)
00149 {
00150     g_return_if_fail (playing);
00151 
00152     if (GPOINTER_TO_INT (hook_data) < PLAYLIST_UPDATE_METADATA || ! playback_get_ready ())
00153         return;
00154 
00155     if (update_from_playlist ())
00156         event_queue ("title change", NULL);
00157 }
00158 
00159 int playback_get_time (void)
00160 {
00161     g_return_val_if_fail (playing, 0);
00162     wait_until_ready ();
00163 
00164     int time = -1;
00165 
00166     if (current_decoder && current_decoder->get_time)
00167         time = current_decoder->get_time (& playback_api);
00168 
00169     if (time < 0)
00170         time = get_output_time ();
00171 
00172     return time - time_offset;
00173 }
00174 
00175 void playback_play (int seek_time, bool_t pause)
00176 {
00177     g_return_if_fail (! playing);
00178 
00179     int playlist = playlist_get_playing ();
00180 
00181     if (playlist == -1)
00182     {
00183         playlist = playlist_get_active ();
00184         playlist_set_playing (playlist);
00185     }
00186 
00187     int entry = playlist_get_position (playlist);
00188 
00189     if (entry == -1)
00190     {
00191         playlist_next_song (playlist, TRUE);
00192         entry = playlist_get_position (playlist);
00193 
00194         if (entry == -1)
00195             return;
00196     }
00197 
00198     failed_entries = 0;
00199     playback_start (playlist, entry, seek_time, pause);
00200 }
00201 
00202 void playback_pause (void)
00203 {
00204     g_return_if_fail (playing);
00205     wait_until_ready ();
00206 
00207     if (! current_decoder || ! current_decoder->pause)
00208         return;
00209 
00210     paused = ! paused;
00211     current_decoder->pause (& playback_api, paused);
00212 
00213     if (paused)
00214         hook_call ("playback pause", NULL);
00215     else
00216         hook_call ("playback unpause", NULL);
00217 }
00218 
00219 static void playback_cleanup (void)
00220 {
00221     g_return_if_fail (playing);
00222 
00223     pthread_join (playback_thread_handle, NULL);
00224     playing = FALSE;
00225 
00226     event_queue_cancel ("playback ready", NULL);
00227     event_queue_cancel ("info change", NULL);
00228     event_queue_cancel ("title change", NULL);
00229 
00230     if (end_source)
00231     {
00232         g_source_remove (end_source);
00233         end_source = 0;
00234     }
00235 
00236     str_unref (current_filename);
00237     current_filename = NULL;
00238     str_unref (current_title);
00239     current_title = NULL;
00240 
00241     hook_dissociate ("playlist update", update_cb);
00242 }
00243 
00244 static void complete_stop (void)
00245 {
00246     output_drain ();
00247     hook_call ("playback stop", NULL);
00248     set_bool (NULL, "stop_after_current_song", FALSE);
00249 }
00250 
00251 void playback_stop (void)
00252 {
00253     g_return_if_fail (playing);
00254     wait_until_ready ();
00255 
00256     if (current_decoder)
00257         current_decoder->stop (& playback_api);
00258 
00259     playback_cleanup ();
00260     complete_stop ();
00261 }
00262 
00263 static bool_t end_cb (void * unused)
00264 {
00265     g_return_val_if_fail (playing, FALSE);
00266 
00267     hook_call ("playback end", NULL);
00268 
00269     if (playback_error)
00270         failed_entries ++;
00271     else
00272         failed_entries = 0;
00273 
00274     playback_cleanup ();
00275 
00276     int playlist = playlist_get_playing ();
00277     bool_t play;
00278 
00279     if (get_bool (NULL, "no_playlist_advance"))
00280         play = get_bool (NULL, "repeat") && ! failed_entries;
00281     else if (! (play = playlist_next_song (playlist, get_bool (NULL, "repeat"))))
00282         playlist_set_position (playlist, -1);
00283     else if (failed_entries >= 10)
00284         play = FALSE;
00285 
00286     if (get_bool (NULL, "stop_after_current_song"))
00287         play = FALSE;
00288 
00289     if (play)
00290         playback_start (playlist, playlist_get_position (playlist), 0, FALSE);
00291     else
00292     {
00293         complete_stop ();
00294         hook_call ("playlist end reached", NULL);
00295     }
00296 
00297     return FALSE;
00298 }
00299 
00300 static void * playback_thread (void * unused)
00301 {
00302     PluginHandle * p = playback_entry_get_decoder ();
00303     current_decoder = p ? plugin_get_header (p) : NULL;
00304 
00305     if (! current_decoder)
00306     {
00307         char * error = g_strdup_printf (_("No decoder found for %s."),
00308          current_filename);
00309         interface_show_error (error);
00310         g_free (error);
00311         playback_error = TRUE;
00312         goto DONE;
00313     }
00314 
00315     current_data = NULL;
00316     current_bitrate = 0;
00317     current_samplerate = 0;
00318     current_channels = 0;
00319 
00320     Tuple * tuple = playback_entry_get_tuple ();
00321     read_gain_from_tuple (tuple);
00322     if (tuple)
00323         tuple_unref (tuple);
00324 
00325     bool_t seekable = (playback_entry_get_length () > 0);
00326 
00327     VFSFile * file = vfs_fopen (current_filename, "r");
00328 
00329     time_offset = seekable ? playback_entry_get_start_time () : 0;
00330     playback_error = ! current_decoder->play (& playback_api, current_filename,
00331      file, seekable ? time_offset + initial_seek : 0,
00332      seekable ? playback_entry_get_end_time () : -1, paused);
00333 
00334     if (file)
00335         vfs_fclose (file);
00336 
00337 DONE:
00338     if (! ready_flag)
00339         set_pb_ready (& playback_api);
00340 
00341     end_source = g_timeout_add (0, end_cb, NULL);
00342     return NULL;
00343 }
00344 
00345 static void playback_start (int playlist, int entry, int seek_time, bool_t pause)
00346 {
00347     g_return_if_fail (! playing);
00348 
00349     current_filename = playlist_entry_get_filename (playlist, entry);
00350     g_return_if_fail (current_filename);
00351 
00352     playing = TRUE;
00353     playback_error = FALSE;
00354     ready_flag = FALSE;
00355 
00356     current_entry = -1;
00357     current_title = NULL;
00358     current_length = 0;
00359 
00360     initial_seek = seek_time;
00361     paused = pause;
00362 
00363     hook_associate ("playlist update", update_cb, NULL);
00364     pthread_create (& playback_thread_handle, NULL, playback_thread, NULL);
00365 
00366     hook_call ("playback begin", NULL);
00367 }
00368 
00369 bool_t playback_get_playing (void)
00370 {
00371     return playing;
00372 }
00373 
00374 bool_t playback_get_paused (void)
00375 {
00376     g_return_val_if_fail (playing, FALSE);
00377     return paused;
00378 }
00379 
00380 void playback_seek (int time)
00381 {
00382     g_return_if_fail (playing);
00383     wait_until_ready ();
00384 
00385     if (! current_decoder || ! current_decoder->mseek || current_length < 1)
00386         return;
00387 
00388     current_decoder->mseek (& playback_api, time_offset + CLAMP (time, 0,
00389      current_length));
00390 
00391     hook_call ("playback seek", NULL);
00392 }
00393 
00394 static void set_data (InputPlayback * p, void * data)
00395 {
00396     g_return_if_fail (playing);
00397     current_data = data;
00398 }
00399 
00400 static void * get_data (InputPlayback * p)
00401 {
00402     g_return_val_if_fail (playing, NULL);
00403     return current_data;
00404 }
00405 
00406 static void set_params (InputPlayback * p, int bitrate, int samplerate,
00407  int channels)
00408 {
00409     g_return_if_fail (playing);
00410 
00411     current_bitrate = bitrate;
00412     current_samplerate = samplerate;
00413     current_channels = channels;
00414 
00415     if (playback_get_ready ())
00416         event_queue ("info change", NULL);
00417 }
00418 
00419 static void set_tuple (InputPlayback * p, Tuple * tuple)
00420 {
00421     g_return_if_fail (playing);
00422     read_gain_from_tuple (tuple);
00423     playback_entry_set_tuple (tuple);
00424 }
00425 
00426 static void set_gain_from_playlist (InputPlayback * p)
00427 {
00428     g_return_if_fail (playing);
00429     p->output->set_replaygain_info (& gain_from_playlist);
00430 }
00431 
00432 static InputPlayback playback_api = {
00433     .output = & output_api,
00434     .set_data = set_data,
00435     .get_data = get_data,
00436     .set_pb_ready = set_pb_ready,
00437     .set_params = set_params,
00438     .set_tuple = set_tuple,
00439     .set_gain_from_playlist = set_gain_from_playlist,
00440 };
00441 
00442 char * playback_get_filename (void)
00443 {
00444     g_return_val_if_fail (playing, NULL);
00445     return str_ref (current_filename);
00446 }
00447 
00448 char * playback_get_title (void)
00449 {
00450     g_return_val_if_fail (playing, NULL);
00451     wait_until_ready ();
00452 
00453     char s[32];
00454 
00455     if (current_length)
00456     {
00457         int len = current_length / 1000;
00458 
00459         if (len < 3600)
00460             snprintf (s, sizeof s, get_bool (NULL, "leading_zero") ?
00461              " (%02d:%02d)" : " (%d:%02d)", len / 60, len % 60);
00462         else
00463             snprintf (s, sizeof s, " (%d:%02d:%02d)", len / 3600, (len / 60) %
00464              60, len % 60);
00465     }
00466     else
00467         s[0] = 0;
00468 
00469     if (get_bool (NULL, "show_numbers_in_pl"))
00470         return str_printf ("%d. %s%s", 1 + playlist_get_position
00471          (playlist_get_playing ()), current_title, s);
00472 
00473     return str_printf ("%s%s", current_title, s);
00474 }
00475 
00476 int playback_get_length (void)
00477 {
00478     g_return_val_if_fail (playing, 0);
00479     wait_until_ready ();
00480 
00481     return current_length;
00482 }
00483 
00484 void playback_get_info (int * bitrate, int * samplerate, int * channels)
00485 {
00486     g_return_if_fail (playing);
00487     wait_until_ready ();
00488 
00489     * bitrate = current_bitrate;
00490     * samplerate = current_samplerate;
00491     * channels = current_channels;
00492 }
00493 
00494 void playback_get_volume (int * l, int * r)
00495 {
00496     if (playing && playback_get_ready () && current_decoder &&
00497      current_decoder->get_volume && current_decoder->get_volume (l, r))
00498         return;
00499 
00500     output_get_volume (l, r);
00501 }
00502 
00503 void playback_set_volume (int l, int r)
00504 {
00505     int h_vol[2] = {l, r};
00506 
00507     hook_call ("volume set", h_vol);
00508 
00509     if (playing && playback_get_ready () && current_decoder &&
00510      current_decoder->set_volume && current_decoder->set_volume (l, r))
00511         return;
00512 
00513     output_set_volume (l, r);
00514 }