00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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
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
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
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
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
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
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);
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
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
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 }