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 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 }