00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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;
00046
00047 static int current_entry;
00048 static char * current_title;
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
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 }