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 event_queue_with_data_free ("interface show error", error);
00483 return FALSE;
00484 }
00485
00486 read_gain_from_tuple (tuple);
00487
00488 current_playback = playback_new ();
00489 current_playback->plugin = decoder;
00490 current_playback->filename = g_strdup (filename);
00491 current_playback->title = g_strdup ((title != NULL) ? title : filename);
00492 current_playback->length = playlist_entry_get_length (playlist, entry, FALSE);
00493
00494 if (playlist_entry_is_segmented (playlist, entry))
00495 {
00496 time_offset = playlist_entry_get_start_time (playlist, entry);
00497 playback_run (time_offset + seek_time, playlist_entry_get_end_time
00498 (playlist, entry), pause);
00499 }
00500 else
00501 {
00502 time_offset = 0;
00503 playback_run (seek_time, -1, pause);
00504 }
00505
00506 #ifdef USE_DBUS
00507 mpris_emit_track_change(mpris);
00508 #endif
00509
00510 hook_associate ("playlist update", update_cb, NULL);
00511 hook_call ("playback begin", NULL);
00512 return TRUE;
00513 }
00514
00515 gboolean playback_get_playing (void)
00516 {
00517 return (current_playback != NULL);
00518 }
00519
00520 gboolean playback_get_paused (void)
00521 {
00522 g_return_val_if_fail (current_playback != NULL, FALSE);
00523
00524 return paused;
00525 }
00526
00527 void playback_seek (gint time)
00528 {
00529 g_return_if_fail (current_playback != NULL);
00530
00531 if (! playback_is_ready ())
00532 return;
00533
00534 time = CLAMP (time, 0, current_playback->length);
00535 time += time_offset;
00536
00537 if (current_playback->plugin->mseek != NULL)
00538 current_playback->plugin->mseek (current_playback, time);
00539 else if (current_playback->plugin->seek != NULL)
00540 {
00541 fprintf (stderr, "%s should be updated to provide mseek().\n",
00542 current_playback->plugin->description);
00543 current_playback->plugin->seek (current_playback, time / 1000);
00544 }
00545
00546 hook_call ("playback seek", NULL);
00547 }
00548
00549 static void set_params (InputPlayback * playback, const gchar * title, gint
00550 length, gint bitrate, gint samplerate, gint channels)
00551 {
00552 playback->rate = bitrate;
00553 playback->freq = samplerate;
00554 playback->nch = channels;
00555
00556 event_queue ("info change", NULL);
00557 }
00558
00559 static gboolean set_tuple_cb (void * unused)
00560 {
00561 gint playlist = playlist_get_playing ();
00562
00563 g_return_val_if_fail (current_playback != NULL, FALSE);
00564 g_mutex_lock (current_playback->pb_ready_mutex);
00565
00566 playlist_entry_set_tuple (playlist, playlist_get_position (playlist),
00567 tuple_to_be_set);
00568 set_tuple_source = 0;
00569 tuple_to_be_set = NULL;
00570
00571 g_mutex_unlock (current_playback->pb_ready_mutex);
00572
00573 return FALSE;
00574 }
00575
00576 static void set_tuple (InputPlayback * playback, Tuple * tuple)
00577 {
00578 g_mutex_lock (playback->pb_ready_mutex);
00579
00580
00581 cancel_set_tuple ();
00582 set_tuple_source = g_timeout_add (0, set_tuple_cb, NULL);
00583 tuple_to_be_set = tuple;
00584
00585 read_gain_from_tuple (tuple);
00586
00587 g_mutex_unlock (playback->pb_ready_mutex);
00588 }
00589
00590 static void set_gain_from_playlist (InputPlayback * playback)
00591 {
00592 playback->output->set_replaygain_info (& gain_from_playlist);
00593 }
00594
00595 gchar * playback_get_title (void)
00596 {
00597 gchar * suffix, * title;
00598
00599 g_return_val_if_fail (current_playback != NULL, NULL);
00600
00601 if (! playback_is_ready ())
00602 return g_strdup (_("Buffering ..."));
00603
00604 suffix = (current_playback->length > 0) ? g_strdup_printf (" (%d:%02d)",
00605 current_playback->length / 60000, current_playback->length / 1000 % 60) :
00606 NULL;
00607
00608 if (cfg.show_numbers_in_pl)
00609 title = g_strdup_printf ("%d. %s%s", 1 + playlist_get_position
00610 (playlist_get_playing ()), current_playback->title, (suffix != NULL) ?
00611 suffix : "");
00612 else
00613 title = g_strdup_printf ("%s%s", current_playback->title, (suffix !=
00614 NULL) ? suffix : "");
00615
00616 g_free (suffix);
00617 return title;
00618 }
00619
00620 gint playback_get_length (void)
00621 {
00622 g_return_val_if_fail (current_playback != NULL, 0);
00623
00624 return current_playback->length;
00625 }
00626
00627 void playback_get_info (gint * bitrate, gint * samplerate, gint * channels)
00628 {
00629 g_return_if_fail (current_playback != NULL);
00630
00631 * bitrate = current_playback->rate;
00632 * samplerate = current_playback->freq;
00633 * channels = current_playback->nch;
00634 }
00635
00636 void
00637 input_get_volume(gint * l, gint * r)
00638 {
00639 if (current_playback && current_playback->plugin->get_volume &&
00640 current_playback->plugin->get_volume (l, r))
00641 return;
00642
00643 output_get_volume (l, r);
00644 }
00645
00646 void
00647 input_set_volume(gint l, gint r)
00648 {
00649 gint h_vol[2] = {l, r};
00650
00651 hook_call("volume set", h_vol);
00652
00653 if (current_playback && current_playback->plugin->set_volume &&
00654 current_playback->plugin->set_volume (l, r))
00655 return;
00656
00657 output_set_volume (l, r);
00658 }