Audacious
$Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
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 }