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