• Main Page
  • Related Pages
  • Classes
  • Files
  • File List
  • File Members

playback.c

Go to the documentation of this file.
00001 /*  Audacious - Cross-platform multimedia player
00002  *  Copyright (C) 2005-2009  Audacious development team
00003  *
00004  *  Based on BMP:
00005  *  Copyright (C) 2003-2004  BMP development team.
00006  *
00007  *  Based on XMMS:
00008  *  Copyright (C) 1998-2003  XMMS development team.
00009  *
00010  *  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; under version 3 of the License.
00013  *
00014  *  This program is distributed in the hope that it will be useful,
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *  GNU General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU General Public License
00020  *  along with this program.  If not, see <http://www.gnu.org/licenses>.
00021  *
00022  *  The Audacious team does not consider modular code linking to
00023  *  Audacious or using our public API to be a derived work.
00024  */
00025 
00026 #include <glib.h>
00027 
00028 #include <libaudcore/audstrings.h>
00029 #include <libaudcore/eventqueue.h>
00030 #include <libaudcore/hook.h>
00031 
00032 #include "audconfig.h"
00033 #include "config.h"
00034 #include "i18n.h"
00035 #include "interface.h"
00036 #include "main.h"
00037 #include "output.h"
00038 #include "playback.h"
00039 #include "playlist.h"
00040 
00041 static void set_params (InputPlayback * playback, const gchar * title, gint
00042  length, gint bitrate, gint samplerate, gint channels);
00043 static void set_tuple (InputPlayback * playback, Tuple * tuple);
00044 static void set_gain_from_playlist (InputPlayback * playback);
00045 
00046 static void playback_free (InputPlayback * playback);
00047 static gboolean playback_play_file (gint playlist, gint entry, gint seek_time,
00048  gboolean pause);
00049 
00050 InputPlayback * current_playback = NULL;
00051 
00052 static gint time_offset;
00053 static gboolean paused;
00054 static gboolean stopping;
00055 static gint ready_source;
00056 static gint failed_entries;
00057 static gint set_tuple_source = 0;
00058 static Tuple * tuple_to_be_set = NULL;
00059 static ReplayGainInfo gain_from_playlist;
00060 
00061 static void cancel_set_tuple (void)
00062 {
00063     if (set_tuple_source != 0)
00064     {
00065         g_source_remove (set_tuple_source);
00066         set_tuple_source = 0;
00067     }
00068 
00069     if (tuple_to_be_set != NULL)
00070     {
00071         tuple_free (tuple_to_be_set);
00072         tuple_to_be_set = NULL;
00073     }
00074 }
00075 
00076 /* clears gain info if tuple == NULL */
00077 static void read_gain_from_tuple (Tuple * tuple)
00078 {
00079     gint album_gain, album_peak, track_gain, track_peak, gain_unit, peak_unit;
00080 
00081     memset (& gain_from_playlist, 0, sizeof gain_from_playlist);
00082 
00083     if (tuple == NULL)
00084         return;
00085 
00086     album_gain = tuple_get_int (tuple, FIELD_GAIN_ALBUM_GAIN, NULL);
00087     album_peak = tuple_get_int (tuple, FIELD_GAIN_ALBUM_PEAK, NULL);
00088     track_gain = tuple_get_int (tuple, FIELD_GAIN_TRACK_GAIN, NULL);
00089     track_peak = tuple_get_int (tuple, FIELD_GAIN_TRACK_PEAK, NULL);
00090     gain_unit = tuple_get_int (tuple, FIELD_GAIN_GAIN_UNIT, NULL);
00091     peak_unit = tuple_get_int (tuple, FIELD_GAIN_PEAK_UNIT, NULL);
00092 
00093     if (gain_unit)
00094     {
00095         gain_from_playlist.album_gain = album_gain / (gfloat) gain_unit;
00096         gain_from_playlist.track_gain = track_gain / (gfloat) gain_unit;
00097     }
00098 
00099     if (peak_unit)
00100     {
00101         gain_from_playlist.album_peak = album_peak / (gfloat) peak_unit;
00102         gain_from_playlist.track_peak = track_peak / (gfloat) peak_unit;
00103     }
00104 }
00105 
00106 static gboolean ready_cb (void * unused)
00107 {
00108     g_return_val_if_fail (current_playback != NULL, FALSE);
00109 
00110     g_mutex_lock (current_playback->pb_ready_mutex);
00111     ready_source = 0;
00112     g_mutex_unlock (current_playback->pb_ready_mutex);
00113 
00114     hook_call ("title change", NULL);
00115     return FALSE;
00116 }
00117 
00118 static gboolean playback_is_ready (void)
00119 {
00120     gboolean ready;
00121 
00122     g_return_val_if_fail (current_playback != NULL, FALSE);
00123 
00124     g_mutex_lock (current_playback->pb_ready_mutex);
00125     ready = (current_playback->pb_ready_val && ! ready_source);
00126     g_mutex_unlock (current_playback->pb_ready_mutex);
00127     return ready;
00128 }
00129 
00130 static gint
00131 playback_set_pb_ready(InputPlayback *playback)
00132 {
00133     g_mutex_lock(playback->pb_ready_mutex);
00134     playback->pb_ready_val = 1;
00135     ready_source = g_timeout_add (0, ready_cb, NULL);
00136     g_cond_signal(playback->pb_ready_cond);
00137     g_mutex_unlock(playback->pb_ready_mutex);
00138     return 0;
00139 }
00140 
00141 static void update_cb (void * hook_data, void * user_data)
00142 {
00143     gint playlist, entry, length;
00144     const gchar * title;
00145 
00146     g_return_if_fail (current_playback != NULL);
00147 
00148     if (GPOINTER_TO_INT (hook_data) < PLAYLIST_UPDATE_METADATA)
00149         return;
00150 
00151     playlist = playlist_get_playing ();
00152     entry = playlist_get_position (playlist);
00153 
00154     if ((title = playlist_entry_get_title (playlist, entry, FALSE)) == NULL)
00155         title = playlist_entry_get_filename (playlist, entry);
00156 
00157     length = playlist_entry_get_length (playlist, entry, FALSE);
00158 
00159     if (! strcmp (title, current_playback->title) && length ==
00160      current_playback->length)
00161         return;
00162 
00163     g_free (current_playback->title);
00164     current_playback->title = g_strdup (title);
00165     current_playback->length = length;
00166 
00167     if (playback_is_ready ())
00168         hook_call ("title change", NULL);
00169 }
00170 
00171 gint playback_get_time (void)
00172 {
00173     if (! playback_is_ready ())
00174         return 0;
00175 
00176     gint time = -1;
00177 
00178     if (current_playback->plugin->get_time != NULL)
00179         time = current_playback->plugin->get_time (current_playback);
00180 
00181     if (time < 0)
00182         time = get_output_time ();
00183 
00184     return time - time_offset;
00185 }
00186 
00187 void playback_play (gint seek_time, gboolean pause)
00188 {
00189     gint playlist, entry;
00190 
00191     playlist = playlist_get_playing ();
00192 
00193     if (playlist == -1)
00194     {
00195         playlist = playlist_get_active ();
00196         playlist_set_playing (playlist);
00197     }
00198 
00199     entry = playlist_get_position (playlist);
00200 
00201     if (entry == -1)
00202     {
00203         playlist_next_song (playlist, TRUE);
00204         entry = playlist_get_position (playlist);
00205 
00206         if (entry == -1)
00207             return;
00208     }
00209 
00210     if (playback_get_playing())
00211         playback_stop();
00212 
00213     failed_entries = 0;
00214     playback_play_file (playlist, entry, seek_time, pause);
00215 }
00216 
00217 void playback_pause (void)
00218 {
00219     if (! playback_is_ready ())
00220         return;
00221 
00222     paused = ! paused;
00223 
00224     g_return_if_fail (current_playback->plugin->pause != NULL);
00225     current_playback->plugin->pause (current_playback, paused);
00226 
00227     if (paused)
00228         hook_call("playback pause", NULL);
00229     else
00230         hook_call("playback unpause", NULL);
00231 }
00232 
00233 static void playback_finalize (void)
00234 {
00235     hook_dissociate ("playlist update", update_cb);
00236 
00237     g_mutex_lock (current_playback->pb_ready_mutex);
00238 
00239     while (! current_playback->pb_ready_val)
00240         g_cond_wait (current_playback->pb_ready_cond,
00241          current_playback->pb_ready_mutex);
00242 
00243     if (ready_source)
00244     {
00245         g_source_remove (ready_source);
00246         ready_source = 0;
00247     }
00248 
00249     g_mutex_unlock (current_playback->pb_ready_mutex);
00250 
00251     current_playback->plugin->stop (current_playback);
00252 
00253     /* some plugins do this themselves */
00254     if (current_playback->thread != NULL)
00255         g_thread_join (current_playback->thread);
00256 
00257     cancel_set_tuple ();
00258     playback_free (current_playback);
00259     current_playback = NULL;
00260 }
00261 
00262 static void complete_stop (void)
00263 {
00264     output_drain ();
00265     hook_call ("playback stop", NULL);
00266 
00267     if (cfg.stopaftersong)
00268     {
00269         cfg.stopaftersong = FALSE;
00270         hook_call ("toggle stop after song", NULL);
00271     }
00272 }
00273 
00274 void playback_stop (void)
00275 {
00276     g_return_if_fail (current_playback != NULL);
00277 
00278     stopping = TRUE;
00279     playback_finalize ();
00280     stopping = FALSE;
00281 
00282     complete_stop ();
00283 }
00284 
00285 static gboolean playback_ended (void * unused)
00286 {
00287     gint playlist = playlist_get_playing ();
00288     gboolean play;
00289 
00290     g_return_val_if_fail (current_playback != NULL, FALSE);
00291 
00292     hook_call ("playback end", NULL);
00293 
00294     if (current_playback->error)
00295         failed_entries ++;
00296     else
00297         failed_entries = 0;
00298 
00299     playback_finalize ();
00300 
00301     while (1)
00302     {
00303         if (cfg.no_playlist_advance)
00304             play = cfg.repeat && ! failed_entries;
00305         else
00306         {
00307             if (! (play = playlist_next_song (playlist, cfg.repeat)))
00308                 playlist_set_position (playlist, -1);
00309 
00310             if (failed_entries >= 10)
00311                 play = FALSE;
00312         }
00313 
00314         if (cfg.stopaftersong)
00315             play = FALSE;
00316 
00317         if (! play)
00318         {
00319             complete_stop ();
00320             hook_call ("playlist end reached", NULL);
00321             break;
00322         }
00323 
00324         if (playback_play_file (playlist, playlist_get_position (playlist), 0,
00325          FALSE))
00326             break;
00327 
00328         failed_entries ++;
00329     }
00330 
00331     return FALSE;
00332 }
00333 
00334 typedef struct
00335 {
00336     gint start_time, stop_time;
00337     gboolean pause;
00338 }
00339 PlayParams;
00340 
00341 static void * playback_monitor_thread (void * data)
00342 {
00343     if (current_playback->plugin->play != NULL)
00344     {
00345         PlayParams * params = data;
00346         VFSFile * file = vfs_fopen (current_playback->filename, "r");
00347 
00348         current_playback->error = ! current_playback->plugin->play
00349          (current_playback, current_playback->filename, file,
00350          params->start_time, params->stop_time, params->pause);
00351 
00352         if (file != NULL)
00353             vfs_fclose (file);
00354     }
00355     else
00356     {
00357         fprintf (stderr, "%s should be updated to provide play().\n",
00358          current_playback->plugin->description);
00359         g_return_val_if_fail (current_playback->plugin->play_file != NULL, NULL);
00360         current_playback->plugin->play_file (current_playback);
00361     }
00362 
00363     g_mutex_lock (current_playback->pb_ready_mutex);
00364     current_playback->pb_ready_val = TRUE;
00365 
00366     if (ready_source != 0)
00367     {
00368         g_source_remove (ready_source);
00369         ready_source = 0;
00370     }
00371 
00372     g_cond_signal (current_playback->pb_ready_cond);
00373     g_mutex_unlock (current_playback->pb_ready_mutex);
00374 
00375     if (! stopping)
00376         g_timeout_add (0, playback_ended, NULL);
00377 
00378     return NULL;
00379 }
00380 
00381 /* compatibility */
00382 static void playback_set_replaygain_info (InputPlayback * playback,
00383  ReplayGainInfo * info)
00384 {
00385     fprintf (stderr, "Plugin %s should be updated to use OutputAPI::"
00386      "set_replaygain_info or (better) InputPlayback::set_gain_from_playlist.\n",
00387      playback->plugin->description);
00388 
00389     playback->output->set_replaygain_info (info);
00390 }
00391 
00392 /* compatibility */
00393 static void playback_pass_audio (InputPlayback * playback, gint format, gint
00394  channels, gint size, void * data, gint * going)
00395 {
00396     static gboolean warned = FALSE;
00397 
00398     if (! warned)
00399     {
00400         fprintf (stderr, "Plugin %s should be updated to use OutputAPI::"
00401          "write_audio.\n", playback->plugin->description);
00402         warned = TRUE;
00403     }
00404 
00405     playback->output->write_audio (data, size);
00406 }
00407 
00408 static InputPlayback * playback_new (void)
00409 {
00410     InputPlayback *playback = (InputPlayback *) g_slice_new0(InputPlayback);
00411 
00412     playback->pb_ready_mutex = g_mutex_new();
00413     playback->pb_ready_cond = g_cond_new();
00414     playback->pb_ready_val = 0;
00415 
00416     playback->output = & output_api;
00417 
00418     /* init vtable functors */
00419     playback->set_pb_ready = playback_set_pb_ready;
00420     playback->set_params = set_params;
00421     playback->set_tuple = set_tuple;
00422     playback->set_gain_from_playlist = set_gain_from_playlist;
00423 
00424     /* compatibility */
00425     playback->set_replaygain_info = playback_set_replaygain_info;
00426     playback->pass_audio = playback_pass_audio;
00427 
00428     return playback;
00429 }
00430 
00438 static void playback_free (InputPlayback * playback)
00439 {
00440     g_free(playback->filename);
00441     g_free(playback->title);
00442 
00443     g_mutex_free(playback->pb_ready_mutex);
00444     g_cond_free(playback->pb_ready_cond);
00445 
00446     g_slice_free(InputPlayback, playback);
00447 }
00448 
00449 static void playback_run (gint start_time, gint stop_time, gboolean pause)
00450 {
00451     current_playback->playing = FALSE;
00452     current_playback->eof = FALSE;
00453     current_playback->error = FALSE;
00454 
00455     paused = pause;
00456     stopping = FALSE;
00457     ready_source = 0;
00458 
00459     static PlayParams params;
00460     params.start_time = start_time;
00461     params.stop_time = stop_time;
00462     params.pause = pause;
00463 
00464     current_playback->thread = g_thread_create (playback_monitor_thread,
00465      & params, TRUE, NULL);
00466 }
00467 
00468 static gboolean playback_play_file (gint playlist, gint entry, gint seek_time,
00469  gboolean pause)
00470 {
00471     const gchar * filename = playlist_entry_get_filename (playlist, entry);
00472     const gchar * title = playlist_entry_get_title (playlist, entry, FALSE);
00473     InputPlugin * decoder = playlist_entry_get_decoder (playlist, entry);
00474     Tuple * tuple = (Tuple *) playlist_entry_get_tuple (playlist, entry, FALSE);
00475 
00476     g_return_val_if_fail (current_playback == NULL, FALSE);
00477 
00478     if (decoder == NULL)
00479     {
00480         gchar * error = g_strdup_printf (_("No decoder found for %s."), filename);
00481 
00482         interface_show_error_message (error);
00483         g_free (error);
00484         return FALSE;
00485     }
00486 
00487     read_gain_from_tuple (tuple); /* even if tuple == NULL */
00488 
00489     current_playback = playback_new ();
00490     current_playback->plugin = decoder;
00491     current_playback->filename = g_strdup (filename);
00492     current_playback->title = g_strdup ((title != NULL) ? title : filename);
00493     current_playback->length = playlist_entry_get_length (playlist, entry, FALSE);
00494 
00495     if (playlist_entry_is_segmented (playlist, entry))
00496     {
00497         time_offset = playlist_entry_get_start_time (playlist, entry);
00498         playback_run (time_offset + seek_time, playlist_entry_get_end_time
00499          (playlist, entry), pause);
00500     }
00501     else
00502     {
00503         time_offset = 0;
00504         playback_run (seek_time, -1, pause);
00505     }
00506 
00507 #ifdef USE_DBUS /* Fix me: Use a "playback begin" hook in dbus.c. */
00508     mpris_emit_track_change(mpris);
00509 #endif
00510 
00511     hook_associate ("playlist update", update_cb, NULL);
00512     hook_call ("playback begin", NULL);
00513     return TRUE;
00514 }
00515 
00516 gboolean playback_get_playing (void)
00517 {
00518     return (current_playback != NULL);
00519 }
00520 
00521 gboolean playback_get_paused (void)
00522 {
00523     g_return_val_if_fail (current_playback != NULL, FALSE);
00524 
00525     return paused;
00526 }
00527 
00528 void playback_seek (gint time)
00529 {
00530     g_return_if_fail (current_playback != NULL);
00531 
00532     if (! playback_is_ready ())
00533         return;
00534 
00535     time = CLAMP (time, 0, current_playback->length);
00536     time += time_offset;
00537 
00538     if (current_playback->plugin->mseek != NULL)
00539         current_playback->plugin->mseek (current_playback, time);
00540     else if (current_playback->plugin->seek != NULL)
00541     {
00542         fprintf (stderr, "%s should be updated to provide mseek().\n",
00543          current_playback->plugin->description);
00544         current_playback->plugin->seek (current_playback, time / 1000);
00545     }
00546 
00547     hook_call ("playback seek", NULL);
00548 }
00549 
00550 static void set_params (InputPlayback * playback, const gchar * title, gint
00551  length, gint bitrate, gint samplerate, gint channels)
00552 {
00553     playback->rate = bitrate;
00554     playback->freq = samplerate;
00555     playback->nch = channels;
00556 
00557     event_queue ("info change", NULL);
00558 }
00559 
00560 static gboolean set_tuple_cb (void * unused)
00561 {
00562     gint playlist = playlist_get_playing ();
00563 
00564     g_return_val_if_fail (current_playback != NULL, FALSE);
00565     g_mutex_lock (current_playback->pb_ready_mutex);
00566 
00567     playlist_entry_set_tuple (playlist, playlist_get_position (playlist),
00568      tuple_to_be_set);
00569     set_tuple_source = 0;
00570     tuple_to_be_set = NULL;
00571 
00572     g_mutex_unlock (current_playback->pb_ready_mutex);
00573 
00574     return FALSE;
00575 }
00576 
00577 static void set_tuple (InputPlayback * playback, Tuple * tuple)
00578 {
00579     g_mutex_lock (playback->pb_ready_mutex);
00580 
00581     /* playlist_entry_set_tuple must execute in main thread */
00582     cancel_set_tuple ();
00583     set_tuple_source = g_timeout_add (0, set_tuple_cb, NULL);
00584     tuple_to_be_set = tuple;
00585 
00586     read_gain_from_tuple (tuple);
00587 
00588     g_mutex_unlock (playback->pb_ready_mutex);
00589 }
00590 
00591 static void set_gain_from_playlist (InputPlayback * playback)
00592 {
00593     playback->output->set_replaygain_info (& gain_from_playlist);
00594 }
00595 
00596 gchar * playback_get_title (void)
00597 {
00598     gchar * suffix, * title;
00599 
00600     g_return_val_if_fail (current_playback != NULL, NULL);
00601 
00602     if (! playback_is_ready ())
00603         return g_strdup (_("Buffering ..."));
00604 
00605     suffix = (current_playback->length > 0) ? g_strdup_printf (" (%d:%02d)",
00606      current_playback->length / 60000, current_playback->length / 1000 % 60) :
00607      NULL;
00608 
00609     if (cfg.show_numbers_in_pl)
00610         title = g_strdup_printf ("%d. %s%s", 1 + playlist_get_position
00611          (playlist_get_playing ()), current_playback->title, (suffix != NULL) ?
00612          suffix : "");
00613     else
00614         title = g_strdup_printf ("%s%s", current_playback->title, (suffix !=
00615          NULL) ? suffix : "");
00616 
00617     g_free (suffix);
00618     return title;
00619 }
00620 
00621 gint playback_get_length (void)
00622 {
00623     g_return_val_if_fail (current_playback != NULL, 0);
00624 
00625     return current_playback->length;
00626 }
00627 
00628 void playback_get_info (gint * bitrate, gint * samplerate, gint * channels)
00629 {
00630     g_return_if_fail (current_playback != NULL);
00631 
00632     * bitrate = current_playback->rate;
00633     * samplerate = current_playback->freq;
00634     * channels = current_playback->nch;
00635 }
00636 
00637 void
00638 input_get_volume(gint * l, gint * r)
00639 {
00640     if (current_playback && current_playback->plugin->get_volume &&
00641      current_playback->plugin->get_volume (l, r))
00642         return;
00643 
00644     output_get_volume (l, r);
00645 }
00646 
00647 void
00648 input_set_volume(gint l, gint r)
00649 {
00650     gint h_vol[2] = {l, r};
00651 
00652     hook_call("volume set", h_vol);
00653 
00654     if (current_playback && current_playback->plugin->set_volume &&
00655      current_playback->plugin->set_volume (l, r))
00656         return;
00657 
00658     output_set_volume (l, r);
00659 }

Generated on Wed Apr 6 2011 for Audacious by  doxygen 1.7.1