player_control: removed the global variable "pc"

Allocate a player_control object where needed, and pass it around.
Each "client" object is associated with a "player_control" instance.

This prepares multi-player support.
This commit is contained in:
Max Kellermann 2009-11-03 21:08:48 +01:00
parent 715844fd08
commit b6995ca011
42 changed files with 753 additions and 583 deletions

View File

@ -27,11 +27,13 @@
struct client; struct client;
struct sockaddr; struct sockaddr;
struct player_control;
void client_manager_init(void); void client_manager_init(void);
void client_manager_deinit(void); void client_manager_deinit(void);
void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid); void client_new(struct player_control *player_control,
int fd, const struct sockaddr *sa, size_t sa_length, int uid);
bool client_is_expired(const struct client *client); bool client_is_expired(const struct client *client);

View File

@ -32,6 +32,8 @@ struct deferred_buffer {
}; };
struct client { struct client {
struct player_control *player_control;
GIOChannel *channel; GIOChannel *channel;
guint source_id; guint source_id;

View File

@ -41,12 +41,15 @@
static const char GREETING[] = "OK MPD " PROTOCOL_VERSION "\n"; static const char GREETING[] = "OK MPD " PROTOCOL_VERSION "\n";
void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid) void
client_new(struct player_control *player_control,
int fd, const struct sockaddr *sa, size_t sa_length, int uid)
{ {
static unsigned int next_client_num; static unsigned int next_client_num;
struct client *client; struct client *client;
char *remote; char *remote;
assert(player_control != NULL);
assert(fd >= 0); assert(fd >= 0);
#ifdef HAVE_LIBWRAP #ifdef HAVE_LIBWRAP
@ -81,6 +84,7 @@ void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid)
} }
client = g_new0(struct client, 1); client = g_new0(struct client, 1);
client->player_control = player_control;
#ifndef G_OS_WIN32 #ifndef G_OS_WIN32
client->channel = g_io_channel_unix_new(fd); client->channel = g_io_channel_unix_new(fd);

View File

@ -44,6 +44,7 @@
#include "dbUtils.h" #include "dbUtils.h"
#include "tag.h" #include "tag.h"
#include "client.h" #include "client.h"
#include "client_internal.h"
#include "tag_print.h" #include "tag_print.h"
#include "path.h" #include "path.h"
#include "replay_gain_config.h" #include "replay_gain_config.h"
@ -434,7 +435,7 @@ handle_play(struct client *client, int argc, char *argv[])
if (argc == 2 && !check_int(client, &song, argv[1], need_positive)) if (argc == 2 && !check_int(client, &song, argv[1], need_positive))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
result = playlist_play(&g_playlist, song); result = playlist_play(&g_playlist, client->player_control, song);
return print_playlist_result(client, result); return print_playlist_result(client, result);
} }
@ -447,7 +448,7 @@ handle_playid(struct client *client, int argc, char *argv[])
if (argc == 2 && !check_int(client, &id, argv[1], need_positive)) if (argc == 2 && !check_int(client, &id, argv[1], need_positive))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
result = playlist_play_id(&g_playlist, id); result = playlist_play_id(&g_playlist, client->player_control, id);
return print_playlist_result(client, result); return print_playlist_result(client, result);
} }
@ -455,7 +456,7 @@ static enum command_return
handle_stop(G_GNUC_UNUSED struct client *client, handle_stop(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]) G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{ {
playlist_stop(&g_playlist); playlist_stop(&g_playlist, client->player_control);
return COMMAND_RETURN_OK; return COMMAND_RETURN_OK;
} }
@ -476,9 +477,9 @@ handle_pause(struct client *client,
if (!check_bool(client, &pause_flag, argv[1])) if (!check_bool(client, &pause_flag, argv[1]))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
pc_set_pause(pause_flag); pc_set_pause(client->player_control, pause_flag);
} else } else
pc_pause(); pc_pause(client->player_control);
return COMMAND_RETURN_OK; return COMMAND_RETURN_OK;
} }
@ -493,7 +494,7 @@ handle_status(struct client *client,
char *error; char *error;
int song; int song;
pc_get_status(&player_status); pc_get_status(client->player_control, &player_status);
switch (player_status.state) { switch (player_status.state) {
case PLAYER_STATE_STOP: case PLAYER_STATE_STOP:
@ -526,9 +527,9 @@ handle_status(struct client *client,
playlist_get_consume(&g_playlist), playlist_get_consume(&g_playlist),
playlist_get_version(&g_playlist), playlist_get_version(&g_playlist),
playlist_get_length(&g_playlist), playlist_get_length(&g_playlist),
(int)(pc_get_cross_fade() + 0.5), (int)(pc_get_cross_fade(client->player_control) + 0.5),
pc_get_mixramp_db(), pc_get_mixramp_db(client->player_control),
pc_get_mixramp_delay(), pc_get_mixramp_delay(client->player_control),
state); state);
song = playlist_get_current_song(&g_playlist); song = playlist_get_current_song(&g_playlist);
@ -561,7 +562,7 @@ handle_status(struct client *client,
updateJobId); updateJobId);
} }
error = pc_get_error_message(); error = pc_get_error_message(client->player_control);
if (error != NULL) { if (error != NULL) {
client_printf(client, client_printf(client,
COMMAND_STATUS_ERROR ": %s\n", COMMAND_STATUS_ERROR ": %s\n",
@ -605,6 +606,7 @@ handle_add(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
result = PLAYLIST_RESULT_DENIED; result = PLAYLIST_RESULT_DENIED;
#else #else
result = playlist_append_file(&g_playlist, result = playlist_append_file(&g_playlist,
client->player_control,
uri + 7, client_get_uid(client), uri + 7, client_get_uid(client),
NULL); NULL);
#endif #endif
@ -618,11 +620,13 @@ handle_add(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
} }
result = playlist_append_uri(&g_playlist, uri, NULL); result = playlist_append_uri(&g_playlist,
client->player_control,
uri, NULL);
return print_playlist_result(client, result); return print_playlist_result(client, result);
} }
result = addAllIn(uri); result = addAllIn(client->player_control, uri);
if (result == (enum playlist_result)-1) { if (result == (enum playlist_result)-1) {
command_error(client, ACK_ERROR_NO_EXIST, command_error(client, ACK_ERROR_NO_EXIST,
"directory or file not found"); "directory or file not found");
@ -643,7 +647,9 @@ handle_addid(struct client *client, int argc, char *argv[])
#ifdef WIN32 #ifdef WIN32
result = PLAYLIST_RESULT_DENIED; result = PLAYLIST_RESULT_DENIED;
#else #else
result = playlist_append_file(&g_playlist, uri + 7, result = playlist_append_file(&g_playlist,
client->player_control,
uri + 7,
client_get_uid(client), client_get_uid(client),
&added_id); &added_id);
#endif #endif
@ -654,7 +660,9 @@ handle_addid(struct client *client, int argc, char *argv[])
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
} }
result = playlist_append_uri(&g_playlist, uri, &added_id); result = playlist_append_uri(&g_playlist,
client->player_control,
uri, &added_id);
} }
if (result != PLAYLIST_RESULT_SUCCESS) if (result != PLAYLIST_RESULT_SUCCESS)
@ -664,11 +672,13 @@ handle_addid(struct client *client, int argc, char *argv[])
int to; int to;
if (!check_int(client, &to, argv[2], check_integer, argv[2])) if (!check_int(client, &to, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
result = playlist_move_id(&g_playlist, added_id, to); result = playlist_move_id(&g_playlist, client->player_control,
added_id, to);
if (result != PLAYLIST_RESULT_SUCCESS) { if (result != PLAYLIST_RESULT_SUCCESS) {
enum command_return ret = enum command_return ret =
print_playlist_result(client, result); print_playlist_result(client, result);
playlist_delete_id(&g_playlist, added_id); playlist_delete_id(&g_playlist, client->player_control,
added_id);
return ret; return ret;
} }
} }
@ -686,7 +696,8 @@ handle_delete(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_range(client, &start, &end, argv[1], need_range)) if (!check_range(client, &start, &end, argv[1], need_range))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
result = playlist_delete_range(&g_playlist, start, end); result = playlist_delete_range(&g_playlist, client->player_control,
start, end);
return print_playlist_result(client, result); return print_playlist_result(client, result);
} }
@ -699,7 +710,7 @@ handle_deleteid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_int(client, &id, argv[1], need_positive)) if (!check_int(client, &id, argv[1], need_positive))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
result = playlist_delete_id(&g_playlist, id); result = playlist_delete_id(&g_playlist, client->player_control, id);
return print_playlist_result(client, result); return print_playlist_result(client, result);
} }
@ -720,7 +731,7 @@ handle_shuffle(G_GNUC_UNUSED struct client *client,
argv[1], need_range)) argv[1], need_range))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
playlist_shuffle(&g_playlist, start, end); playlist_shuffle(&g_playlist, client->player_control, start, end);
return COMMAND_RETURN_OK; return COMMAND_RETURN_OK;
} }
@ -728,7 +739,7 @@ static enum command_return
handle_clear(G_GNUC_UNUSED struct client *client, handle_clear(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]) G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{ {
playlist_clear(&g_playlist); playlist_clear(&g_playlist, client->player_control);
return COMMAND_RETURN_OK; return COMMAND_RETURN_OK;
} }
@ -747,11 +758,13 @@ handle_load(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{ {
enum playlist_result result; enum playlist_result result;
result = playlist_open_into_queue(argv[1], &g_playlist, true); result = playlist_open_into_queue(argv[1], &g_playlist,
client->player_control, true);
if (result != PLAYLIST_RESULT_NO_SUCH_LIST) if (result != PLAYLIST_RESULT_NO_SUCH_LIST)
return result; return result;
result = playlist_load_spl(&g_playlist, argv[1]); result = playlist_load_spl(&g_playlist, client->player_control,
argv[1]);
return print_playlist_result(client, result); return print_playlist_result(client, result);
} }
@ -1141,7 +1154,7 @@ handle_next(G_GNUC_UNUSED struct client *client,
int single = g_playlist.queue.single; int single = g_playlist.queue.single;
g_playlist.queue.single = false; g_playlist.queue.single = false;
playlist_next(&g_playlist); playlist_next(&g_playlist, client->player_control);
g_playlist.queue.single = single; g_playlist.queue.single = single;
return COMMAND_RETURN_OK; return COMMAND_RETURN_OK;
@ -1151,7 +1164,7 @@ static enum command_return
handle_previous(G_GNUC_UNUSED struct client *client, handle_previous(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]) G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{ {
playlist_previous(&g_playlist); playlist_previous(&g_playlist, client->player_control);
return COMMAND_RETURN_OK; return COMMAND_RETURN_OK;
} }
@ -1210,7 +1223,7 @@ handle_repeat(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
} }
playlist_set_repeat(&g_playlist, status); playlist_set_repeat(&g_playlist, client->player_control, status);
return COMMAND_RETURN_OK; return COMMAND_RETURN_OK;
} }
@ -1228,7 +1241,7 @@ handle_single(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
} }
playlist_set_single(&g_playlist, status); playlist_set_single(&g_playlist, client->player_control, status);
return COMMAND_RETURN_OK; return COMMAND_RETURN_OK;
} }
@ -1264,7 +1277,7 @@ handle_random(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
} }
playlist_set_random(&g_playlist, status); playlist_set_random(&g_playlist, client->player_control, status);
return COMMAND_RETURN_OK; return COMMAND_RETURN_OK;
} }
@ -1279,7 +1292,7 @@ static enum command_return
handle_clearerror(G_GNUC_UNUSED struct client *client, handle_clearerror(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]) G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{ {
pc_clear_error(); pc_clear_error(client->player_control);
return COMMAND_RETURN_OK; return COMMAND_RETURN_OK;
} }
@ -1348,7 +1361,8 @@ handle_move(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
if (!check_int(client, &to, argv[2], check_integer, argv[2])) if (!check_int(client, &to, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
result = playlist_move_range(&g_playlist, start, end, to); result = playlist_move_range(&g_playlist, client->player_control,
start, end, to);
return print_playlist_result(client, result); return print_playlist_result(client, result);
} }
@ -1362,7 +1376,8 @@ handle_moveid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
if (!check_int(client, &to, argv[2], check_integer, argv[2])) if (!check_int(client, &to, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
result = playlist_move_id(&g_playlist, id, to); result = playlist_move_id(&g_playlist, client->player_control,
id, to);
return print_playlist_result(client, result); return print_playlist_result(client, result);
} }
@ -1376,7 +1391,8 @@ handle_swap(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
if (!check_int(client, &song2, argv[2], check_integer, argv[2])) if (!check_int(client, &song2, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
result = playlist_swap_songs(&g_playlist, song1, song2); result = playlist_swap_songs(&g_playlist, client->player_control,
song1, song2);
return print_playlist_result(client, result); return print_playlist_result(client, result);
} }
@ -1390,7 +1406,8 @@ handle_swapid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
if (!check_int(client, &id2, argv[2], check_integer, argv[2])) if (!check_int(client, &id2, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
result = playlist_swap_songs_id(&g_playlist, id1, id2); result = playlist_swap_songs_id(&g_playlist, client->player_control,
id1, id2);
return print_playlist_result(client, result); return print_playlist_result(client, result);
} }
@ -1405,7 +1422,8 @@ handle_seek(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_int(client, &seek_time, argv[2], check_integer, argv[2])) if (!check_int(client, &seek_time, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
result = playlist_seek_song(&g_playlist, song, seek_time); result = playlist_seek_song(&g_playlist, client->player_control,
song, seek_time);
return print_playlist_result(client, result); return print_playlist_result(client, result);
} }
@ -1420,7 +1438,8 @@ handle_seekid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_int(client, &seek_time, argv[2], check_integer, argv[2])) if (!check_int(client, &seek_time, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
result = playlist_seek_song_id(&g_playlist, id, seek_time); result = playlist_seek_song_id(&g_playlist, client->player_control,
id, seek_time);
return print_playlist_result(client, result); return print_playlist_result(client, result);
} }
@ -1470,7 +1489,7 @@ handle_crossfade(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_unsigned(client, &xfade_time, argv[1])) if (!check_unsigned(client, &xfade_time, argv[1]))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
pc_set_cross_fade(xfade_time); pc_set_cross_fade(client->player_control, xfade_time);
return COMMAND_RETURN_OK; return COMMAND_RETURN_OK;
} }
@ -1482,7 +1501,7 @@ handle_mixrampdb(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_float(client, &db, argv[1])) if (!check_float(client, &db, argv[1]))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
pc_set_mixramp_db(db); pc_set_mixramp_db(client->player_control, db);
return COMMAND_RETURN_OK; return COMMAND_RETURN_OK;
} }
@ -1494,7 +1513,7 @@ handle_mixrampdelay(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_float(client, &delay_secs, argv[1])) if (!check_float(client, &delay_secs, argv[1]))
return COMMAND_RETURN_ERROR; return COMMAND_RETURN_ERROR;
pc_set_mixramp_delay(delay_secs); pc_set_mixramp_delay(client->player_control, delay_secs);
return COMMAND_RETURN_OK; return COMMAND_RETURN_OK;
} }

View File

@ -29,6 +29,7 @@
#include "tag.h" #include "tag.h"
#include "strset.h" #include "strset.h"
#include "stored_playlist.h" #include "stored_playlist.h"
#include "client_internal.h"
#include <glib.h> #include <glib.h>
@ -166,9 +167,11 @@ int printAllIn(struct client *client, const char *name)
} }
static int static int
directoryAddSongToPlaylist(struct song *song, G_GNUC_UNUSED void *data) directoryAddSongToPlaylist(struct song *song, void *data)
{ {
return playlist_append_song(&g_playlist, song, NULL); struct player_control *pc = data;
return playlist_append_song(&g_playlist, pc, song, NULL);
} }
struct add_data { struct add_data {
@ -185,9 +188,10 @@ directoryAddSongToStoredPlaylist(struct song *song, void *_data)
return 0; return 0;
} }
int addAllIn(const char *name) int
addAllIn(struct player_control *pc, const char *name)
{ {
return db_walk(name, directoryAddSongToPlaylist, NULL, NULL); return db_walk(name, directoryAddSongToPlaylist, NULL, pc);
} }
int addAllInToStoredPlaylist(const char *name, const char *utf8file) int addAllInToStoredPlaylist(const char *name, const char *utf8file)
@ -205,7 +209,9 @@ findAddInDirectory(struct song *song, void *_data)
struct search_data *data = _data; struct search_data *data = _data;
if (locate_song_match(song, data->criteria)) if (locate_song_match(song, data->criteria))
return playlist_append_song(&g_playlist, song, NULL); return playlist_append_song(&g_playlist,
data->client->player_control,
song, NULL);
return 0; return 0;
} }

View File

@ -22,10 +22,12 @@
struct client; struct client;
struct locate_item_list; struct locate_item_list;
struct player_control;
int printAllIn(struct client *client, const char *name); int printAllIn(struct client *client, const char *name);
int addAllIn(const char *name); int
addAllIn(struct player_control *pc, const char *name);
int addAllInToStoredPlaylist(const char *name, const char *utf8file); int addAllInToStoredPlaylist(const char *name, const char *utf8file);

View File

@ -65,7 +65,7 @@ decoder_initialized(struct decoder *decoder,
dc->state = DECODE_STATE_DECODE; dc->state = DECODE_STATE_DECODE;
decoder_unlock(dc); decoder_unlock(dc);
player_lock_signal(); player_lock_signal(dc->player_control);
g_debug("audio_format=%s, seekable=%s", g_debug("audio_format=%s, seekable=%s",
audio_format_to_string(&dc->in_audio_format, &af_string), audio_format_to_string(&dc->in_audio_format, &af_string),
@ -117,7 +117,7 @@ decoder_command_finished(struct decoder *decoder)
dc->command = DECODE_COMMAND_NONE; dc->command = DECODE_COMMAND_NONE;
decoder_unlock(dc); decoder_unlock(dc);
player_lock_signal(); player_lock_signal(dc->player_control);
} }
double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder) double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder)
@ -214,7 +214,7 @@ do_send_tag(struct decoder *decoder, struct input_stream *is,
/* there is a partial chunk - flush it, we want the /* there is a partial chunk - flush it, we want the
tag in a new chunk */ tag in a new chunk */
decoder_flush_chunk(decoder); decoder_flush_chunk(decoder);
player_lock_signal(); player_lock_signal(decoder->dc->player_control);
} }
assert(decoder->chunk == NULL); assert(decoder->chunk == NULL);
@ -329,7 +329,7 @@ decoder_data(struct decoder *decoder,
if (dest == NULL) { if (dest == NULL) {
/* the chunk is full, flush it */ /* the chunk is full, flush it */
decoder_flush_chunk(decoder); decoder_flush_chunk(decoder);
player_lock_signal(); player_lock_signal(dc->player_control);
continue; continue;
} }
@ -348,7 +348,7 @@ decoder_data(struct decoder *decoder,
if (full) { if (full) {
/* the chunk is full, flush it */ /* the chunk is full, flush it */
decoder_flush_chunk(decoder); decoder_flush_chunk(decoder);
player_lock_signal(); player_lock_signal(dc->player_control);
} }
data += nbytes; data += nbytes;
@ -432,7 +432,7 @@ decoder_replay_gain(struct decoder *decoder,
replay gain values affect the following replay gain values affect the following
samples */ samples */
decoder_flush_chunk(decoder); decoder_flush_chunk(decoder);
player_lock_signal(); player_lock_signal(decoder->dc->player_control);
} }
} else } else
decoder->replay_gain_serial = 0; decoder->replay_gain_serial = 0;

View File

@ -28,8 +28,9 @@
#define G_LOG_DOMAIN "decoder_control" #define G_LOG_DOMAIN "decoder_control"
void void
dc_init(struct decoder_control *dc) dc_init(struct decoder_control *dc, struct player_control *pc)
{ {
dc->player_control = pc;
dc->thread = NULL; dc->thread = NULL;
dc->mutex = g_mutex_new(); dc->mutex = g_mutex_new();
@ -62,7 +63,7 @@ static void
dc_command_wait_locked(struct decoder_control *dc) dc_command_wait_locked(struct decoder_control *dc)
{ {
while (dc->command != DECODE_COMMAND_NONE) while (dc->command != DECODE_COMMAND_NONE)
player_wait_decoder(dc); player_wait_decoder(dc->player_control, dc);
} }
void void

View File

@ -27,6 +27,8 @@
#include <assert.h> #include <assert.h>
struct player_control;
enum decoder_state { enum decoder_state {
DECODE_STATE_STOP = 0, DECODE_STATE_STOP = 0,
DECODE_STATE_START, DECODE_STATE_START,
@ -42,6 +44,12 @@ enum decoder_state {
}; };
struct decoder_control { struct decoder_control {
/**
* The player thread which calls us. This pointer is used to
* signal command completion.
*/
struct player_control *player_control;
/** the handle of the decoder thread, or NULL if the decoder /** the handle of the decoder thread, or NULL if the decoder
thread isn't running */ thread isn't running */
GThread *thread; GThread *thread;
@ -98,7 +106,7 @@ struct decoder_control {
}; };
void void
dc_init(struct decoder_control *dc); dc_init(struct decoder_control *dc, struct player_control *pc);
void void
dc_deinit(struct decoder_control *dc); dc_deinit(struct decoder_control *dc);

View File

@ -65,7 +65,7 @@ need_chunks(struct decoder_control *dc, struct input_stream *is, bool do_wait)
if ((is == NULL || !decoder_input_buffer(dc, is)) && do_wait) { if ((is == NULL || !decoder_input_buffer(dc, is)) && do_wait) {
decoder_wait(dc); decoder_wait(dc);
player_signal(); player_signal(dc->player_control);
return dc->command; return dc->command;
} }

View File

@ -383,7 +383,7 @@ decoder_run_song(struct decoder_control *dc,
dc->state = DECODE_STATE_START; dc->state = DECODE_STATE_START;
dc->command = DECODE_COMMAND_NONE; dc->command = DECODE_COMMAND_NONE;
player_signal(); player_signal(dc->player_control);
pcm_convert_init(&decoder.conv_state); pcm_convert_init(&decoder.conv_state);
@ -464,13 +464,13 @@ decoder_task(gpointer arg)
dc->command = DECODE_COMMAND_NONE; dc->command = DECODE_COMMAND_NONE;
player_signal(); player_signal(dc->player_control);
break; break;
case DECODE_COMMAND_STOP: case DECODE_COMMAND_STOP:
dc->command = DECODE_COMMAND_NONE; dc->command = DECODE_COMMAND_NONE;
player_signal(); player_signal(dc->player_control);
break; break;
case DECODE_COMMAND_NONE: case DECODE_COMMAND_NONE:

View File

@ -23,6 +23,7 @@
#include "client.h" #include "client.h"
#include "conf.h" #include "conf.h"
#include "glib_compat.h" #include "glib_compat.h"
#include "main.h"
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
@ -39,7 +40,7 @@ static void
listen_callback(int fd, const struct sockaddr *address, listen_callback(int fd, const struct sockaddr *address,
size_t address_length, int uid, G_GNUC_UNUSED void *ctx) size_t address_length, int uid, G_GNUC_UNUSED void *ctx)
{ {
client_new(fd, address, address_length, uid); client_new(global_player_control, fd, address, address_length, uid);
} }
static bool static bool

View File

@ -94,6 +94,8 @@ GMainLoop *main_loop;
GCond *main_cond; GCond *main_cond;
struct player_control *global_player_control;
static void static void
glue_daemonize_init(const struct options *options) glue_daemonize_init(const struct options *options)
{ {
@ -183,7 +185,8 @@ glue_sticker_init(void)
static void static void
glue_state_file_init(void) glue_state_file_init(void)
{ {
state_file_init(config_get_path(CONF_STATE_FILE)); state_file_init(config_get_path(CONF_STATE_FILE),
global_player_control);
} }
/** /**
@ -254,7 +257,7 @@ initialize_decoder_and_player(void)
if (buffered_before_play > buffered_chunks) if (buffered_before_play > buffered_chunks)
buffered_before_play = buffered_chunks; buffered_before_play = buffered_chunks;
pc_init(buffered_chunks, buffered_before_play); global_player_control = pc_new(buffered_chunks, buffered_before_play);
} }
/** /**
@ -364,7 +367,7 @@ int mpd_main(int argc, char *argv[])
initialize_decoder_and_player(); initialize_decoder_and_player();
volume_init(); volume_init();
initAudioConfig(); initAudioConfig();
audio_output_all_init(); audio_output_all_init(global_player_control);
client_manager_init(); client_manager_init();
replay_gain_global_init(); replay_gain_global_init();
@ -384,7 +387,7 @@ int mpd_main(int argc, char *argv[])
initZeroconf(); initZeroconf();
player_create(); player_create(global_player_control);
if (create_db) { if (create_db) {
/* the database failed to load: recreate the /* the database failed to load: recreate the
@ -410,7 +413,7 @@ int mpd_main(int argc, char *argv[])
/* enable all audio outputs (if not already done by /* enable all audio outputs (if not already done by
playlist_state_restore() */ playlist_state_restore() */
pc_update_audio(); pc_update_audio(global_player_control);
#ifdef WIN32 #ifdef WIN32
win32_app_started(); win32_app_started();
@ -431,8 +434,8 @@ int mpd_main(int argc, char *argv[])
mpd_inotify_finish(); mpd_inotify_finish();
#endif #endif
state_file_finish(); state_file_finish(global_player_control);
pc_kill(); pc_kill(global_player_control);
finishZeroconf(); finishZeroconf();
client_manager_deinit(); client_manager_deinit();
listen_global_finish(); listen_global_finish();
@ -457,7 +460,7 @@ int mpd_main(int argc, char *argv[])
mapper_finish(); mapper_finish();
path_global_finish(); path_global_finish();
finishPermissions(); finishPermissions();
pc_deinit(); pc_free(global_player_control);
command_finish(); command_finish();
update_global_finish(); update_global_finish();
decoder_plugin_deinit_all(); decoder_plugin_deinit_all();

View File

@ -28,6 +28,8 @@ extern GMainLoop *main_loop;
extern GCond *main_cond; extern GCond *main_cond;
extern struct player_control *global_player_control;
/** /**
* A entry point for application. * A entry point for application.
* On non-Windows platforms this is called directly from main() * On non-Windows platforms this is called directly from main()

View File

@ -100,7 +100,7 @@ audio_output_config_count(void)
} }
void void
audio_output_all_init(void) audio_output_all_init(struct player_control *pc)
{ {
const struct config_param *param = NULL; const struct config_param *param = NULL;
unsigned int i; unsigned int i;
@ -121,7 +121,7 @@ audio_output_all_init(void)
/* only allow param to be NULL if there just one audioOutput */ /* only allow param to be NULL if there just one audioOutput */
assert(param || (num_audio_outputs == 1)); assert(param || (num_audio_outputs == 1));
if (!audio_output_init(output, param, &error)) { if (!audio_output_init(output, param, pc, &error)) {
if (param != NULL) if (param != NULL)
MPD_ERROR("line %i: %s", MPD_ERROR("line %i: %s",
param->line, error->message); param->line, error->message);
@ -473,17 +473,17 @@ audio_output_all_check(void)
} }
bool bool
audio_output_all_wait(unsigned threshold) audio_output_all_wait(struct player_control *pc, unsigned threshold)
{ {
player_lock(); player_lock(pc);
if (audio_output_all_check() < threshold) { if (audio_output_all_check() < threshold) {
player_unlock(); player_unlock(pc);
return true; return true;
} }
player_wait(); player_wait(pc);
player_unlock(); player_unlock(pc);
return audio_output_all_check() < threshold; return audio_output_all_check() < threshold;
} }

View File

@ -32,13 +32,14 @@
struct audio_format; struct audio_format;
struct music_buffer; struct music_buffer;
struct music_chunk; struct music_chunk;
struct player_control;
/** /**
* Global initialization: load audio outputs from the configuration * Global initialization: load audio outputs from the configuration
* file and initialize them. * file and initialize them.
*/ */
void void
audio_output_all_init(void); audio_output_all_init(struct player_control *pc);
/** /**
* Global finalization: free memory occupied by audio outputs. All * Global finalization: free memory occupied by audio outputs. All
@ -127,7 +128,7 @@ audio_output_all_check(void);
* @return true if there are less than #threshold chunks in the pipe * @return true if there are less than #threshold chunks in the pipe
*/ */
bool bool
audio_output_all_wait(unsigned threshold); audio_output_all_wait(struct player_control *pc, unsigned threshold);
/** /**
* Puts all audio outputs into pause mode. Most implementations will * Puts all audio outputs into pause mode. Most implementations will

View File

@ -50,7 +50,7 @@ audio_output_enable_index(unsigned idx)
ao->enabled = true; ao->enabled = true;
idle_add(IDLE_OUTPUT); idle_add(IDLE_OUTPUT);
pc_update_audio(); pc_update_audio(ao->player_control);
++audio_output_state_version; ++audio_output_state_version;
@ -79,7 +79,7 @@ audio_output_disable_index(unsigned idx)
idle_add(IDLE_MIXER); idle_add(IDLE_MIXER);
} }
pc_update_audio(); pc_update_audio(ao->player_control);
++audio_output_state_version; ++audio_output_state_version;

View File

@ -29,6 +29,7 @@ struct audio_output;
struct audio_format; struct audio_format;
struct config_param; struct config_param;
struct music_pipe; struct music_pipe;
struct player_control;
static inline GQuark static inline GQuark
audio_output_quark(void) audio_output_quark(void)
@ -38,6 +39,7 @@ audio_output_quark(void)
bool bool
audio_output_init(struct audio_output *ao, const struct config_param *param, audio_output_init(struct audio_output *ao, const struct config_param *param,
struct player_control *pc,
GError **error_r); GError **error_r);
/** /**

View File

@ -127,8 +127,12 @@ audio_output_load_mixer(void *ao, const struct config_param *param,
bool bool
audio_output_init(struct audio_output *ao, const struct config_param *param, audio_output_init(struct audio_output *ao, const struct config_param *param,
struct player_control *pc,
GError **error_r) GError **error_r)
{ {
assert(ao != NULL);
assert(pc != NULL);
const struct audio_output_plugin *plugin = NULL; const struct audio_output_plugin *plugin = NULL;
GError *error = NULL; GError *error = NULL;
@ -249,6 +253,7 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
ao->command = AO_COMMAND_NONE; ao->command = AO_COMMAND_NONE;
ao->mutex = g_mutex_new(); ao->mutex = g_mutex_new();
ao->cond = g_cond_new(); ao->cond = g_cond_new();
ao->player_control = pc;
ao->data = ao_plugin_init(plugin, ao->data = ao_plugin_init(plugin,
&ao->config_audio_format, &ao->config_audio_format,

View File

@ -207,6 +207,12 @@ struct audio_output {
*/ */
GCond *cond; GCond *cond;
/**
* The player_control object which "owns" this output. This
* object is needed to signal command completion.
*/
struct player_control *player_control;
/** /**
* The #music_chunk which is currently being played. All * The #music_chunk which is currently being played. All
* chunks before this one may be returned to the * chunks before this one may be returned to the

View File

@ -530,7 +530,7 @@ ao_play(struct audio_output *ao)
ao->chunk_finished = true; ao->chunk_finished = true;
g_mutex_unlock(ao->mutex); g_mutex_unlock(ao->mutex);
player_lock_signal(); player_lock_signal(ao->player_control);
g_mutex_lock(ao->mutex); g_mutex_lock(ao->mutex);
return true; return true;

View File

@ -32,237 +32,247 @@
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
struct player_control pc;
static void static void
pc_enqueue_song_locked(struct song *song); pc_enqueue_song_locked(struct player_control *pc, struct song *song);
void pc_init(unsigned buffer_chunks, unsigned int buffered_before_play) struct player_control *
pc_new(unsigned buffer_chunks, unsigned int buffered_before_play)
{ {
pc.buffer_chunks = buffer_chunks; struct player_control *pc = g_new0(struct player_control, 1);
pc.buffered_before_play = buffered_before_play;
pc.mutex = g_mutex_new(); pc->buffer_chunks = buffer_chunks;
pc.cond = g_cond_new(); pc->buffered_before_play = buffered_before_play;
pc.command = PLAYER_COMMAND_NONE; pc->mutex = g_mutex_new();
pc.error = PLAYER_ERROR_NOERROR; pc->cond = g_cond_new();
pc.state = PLAYER_STATE_STOP;
pc.cross_fade_seconds = 0;
pc.mixramp_db = 0;
pc.mixramp_delay_seconds = nanf("");
}
void pc_deinit(void) pc->command = PLAYER_COMMAND_NONE;
{ pc->error = PLAYER_ERROR_NOERROR;
g_cond_free(pc.cond); pc->state = PLAYER_STATE_STOP;
g_mutex_free(pc.mutex); pc->cross_fade_seconds = 0;
pc->mixramp_db = 0;
pc->mixramp_delay_seconds = nanf("");
return pc;
} }
void void
player_wait_decoder(struct decoder_control *dc) pc_free(struct player_control *pc)
{ {
g_cond_free(pc->cond);
g_mutex_free(pc->mutex);
g_free(pc);
}
void
player_wait_decoder(struct player_control *pc, struct decoder_control *dc)
{
assert(pc != NULL);
assert(dc != NULL);
assert(dc->player_control == pc);
/* during this function, the decoder lock is held, because /* during this function, the decoder lock is held, because
we're waiting for the decoder thread */ we're waiting for the decoder thread */
g_cond_wait(pc.cond, dc->mutex); g_cond_wait(pc->cond, dc->mutex);
} }
void void
pc_song_deleted(const struct song *song) pc_song_deleted(struct player_control *pc, const struct song *song)
{ {
if (pc.errored_song == song) { if (pc->errored_song == song) {
pc.error = PLAYER_ERROR_NOERROR; pc->error = PLAYER_ERROR_NOERROR;
pc.errored_song = NULL; pc->errored_song = NULL;
} }
} }
static void static void
player_command_wait_locked(void) player_command_wait_locked(struct player_control *pc)
{ {
while (pc.command != PLAYER_COMMAND_NONE) while (pc->command != PLAYER_COMMAND_NONE)
g_cond_wait(main_cond, pc.mutex); g_cond_wait(main_cond, pc->mutex);
} }
static void static void
player_command_locked(enum player_command cmd) player_command_locked(struct player_control *pc, enum player_command cmd)
{ {
assert(pc.command == PLAYER_COMMAND_NONE); assert(pc->command == PLAYER_COMMAND_NONE);
pc.command = cmd; pc->command = cmd;
player_signal(); player_signal(pc);
player_command_wait_locked(); player_command_wait_locked(pc);
} }
static void static void
player_command(enum player_command cmd) player_command(struct player_control *pc, enum player_command cmd)
{ {
player_lock(); player_lock(pc);
player_command_locked(cmd); player_command_locked(pc, cmd);
player_unlock(); player_unlock(pc);
} }
void void
pc_play(struct song *song) pc_play(struct player_control *pc, struct song *song)
{ {
assert(song != NULL); assert(song != NULL);
player_lock(); player_lock(pc);
if (pc.state != PLAYER_STATE_STOP) if (pc->state != PLAYER_STATE_STOP)
player_command_locked(PLAYER_COMMAND_STOP); player_command_locked(pc, PLAYER_COMMAND_STOP);
assert(pc.next_song == NULL); assert(pc->next_song == NULL);
pc_enqueue_song_locked(song); pc_enqueue_song_locked(pc, song);
assert(pc.next_song == NULL); assert(pc->next_song == NULL);
player_unlock(); player_unlock(pc);
idle_add(IDLE_PLAYER);
}
void pc_cancel(void)
{
player_command(PLAYER_COMMAND_CANCEL);
assert(pc.next_song == NULL);
}
void
pc_stop(void)
{
player_command(PLAYER_COMMAND_CLOSE_AUDIO);
assert(pc.next_song == NULL);
idle_add(IDLE_PLAYER); idle_add(IDLE_PLAYER);
} }
void void
pc_update_audio(void) pc_cancel(struct player_control *pc)
{ {
player_command(PLAYER_COMMAND_UPDATE_AUDIO); player_command(pc, PLAYER_COMMAND_CANCEL);
assert(pc->next_song == NULL);
} }
void void
pc_kill(void) pc_stop(struct player_control *pc)
{ {
assert(pc.thread != NULL); player_command(pc, PLAYER_COMMAND_CLOSE_AUDIO);
assert(pc->next_song == NULL);
player_command(PLAYER_COMMAND_EXIT);
g_thread_join(pc.thread);
pc.thread = NULL;
idle_add(IDLE_PLAYER); idle_add(IDLE_PLAYER);
} }
void void
pc_pause(void) pc_update_audio(struct player_control *pc)
{ {
player_lock(); player_command(pc, PLAYER_COMMAND_UPDATE_AUDIO);
}
if (pc.state != PLAYER_STATE_STOP) { void
player_command_locked(PLAYER_COMMAND_PAUSE); pc_kill(struct player_control *pc)
{
assert(pc->thread != NULL);
player_command(pc, PLAYER_COMMAND_EXIT);
g_thread_join(pc->thread);
pc->thread = NULL;
idle_add(IDLE_PLAYER);
}
void
pc_pause(struct player_control *pc)
{
player_lock(pc);
if (pc->state != PLAYER_STATE_STOP) {
player_command_locked(pc, PLAYER_COMMAND_PAUSE);
idle_add(IDLE_PLAYER); idle_add(IDLE_PLAYER);
} }
player_unlock(); player_unlock(pc);
} }
static void static void
pc_pause_locked(void) pc_pause_locked(struct player_control *pc)
{ {
if (pc.state != PLAYER_STATE_STOP) { if (pc->state != PLAYER_STATE_STOP) {
player_command_locked(PLAYER_COMMAND_PAUSE); player_command_locked(pc, PLAYER_COMMAND_PAUSE);
idle_add(IDLE_PLAYER); idle_add(IDLE_PLAYER);
} }
} }
void void
pc_set_pause(bool pause_flag) pc_set_pause(struct player_control *pc, bool pause_flag)
{ {
player_lock(); player_lock(pc);
switch (pc.state) { switch (pc->state) {
case PLAYER_STATE_STOP: case PLAYER_STATE_STOP:
break; break;
case PLAYER_STATE_PLAY: case PLAYER_STATE_PLAY:
if (pause_flag) if (pause_flag)
pc_pause_locked(); pc_pause_locked(pc);
break; break;
case PLAYER_STATE_PAUSE: case PLAYER_STATE_PAUSE:
if (!pause_flag) if (!pause_flag)
pc_pause_locked(); pc_pause_locked(pc);
break; break;
} }
player_unlock(); player_unlock(pc);
} }
void void
pc_get_status(struct player_status *status) pc_get_status(struct player_control *pc, struct player_status *status)
{ {
player_lock(); player_lock(pc);
player_command_locked(PLAYER_COMMAND_REFRESH); player_command_locked(pc, PLAYER_COMMAND_REFRESH);
status->state = pc.state; status->state = pc->state;
if (pc.state != PLAYER_STATE_STOP) { if (pc->state != PLAYER_STATE_STOP) {
status->bit_rate = pc.bit_rate; status->bit_rate = pc->bit_rate;
status->audio_format = pc.audio_format; status->audio_format = pc->audio_format;
status->total_time = pc.total_time; status->total_time = pc->total_time;
status->elapsed_time = pc.elapsed_time; status->elapsed_time = pc->elapsed_time;
} }
player_unlock(); player_unlock(pc);
} }
enum player_state enum player_state
pc_get_state(void) pc_get_state(struct player_control *pc)
{ {
return pc.state; return pc->state;
} }
void void
pc_clear_error(void) pc_clear_error(struct player_control *pc)
{ {
player_lock(); player_lock(pc);
pc.error = PLAYER_ERROR_NOERROR; pc->error = PLAYER_ERROR_NOERROR;
pc.errored_song = NULL; pc->errored_song = NULL;
player_unlock(); player_unlock(pc);
} }
enum player_error enum player_error
pc_get_error(void) pc_get_error(struct player_control *pc)
{ {
return pc.error; return pc->error;
} }
static char * static char *
pc_errored_song_uri(void) pc_errored_song_uri(struct player_control *pc)
{ {
return song_get_uri(pc.errored_song); return song_get_uri(pc->errored_song);
} }
char * char *
pc_get_error_message(void) pc_get_error_message(struct player_control *pc)
{ {
char *error; char *error;
char *uri; char *uri;
switch (pc.error) { switch (pc->error) {
case PLAYER_ERROR_NOERROR: case PLAYER_ERROR_NOERROR:
return NULL; return NULL;
case PLAYER_ERROR_FILENOTFOUND: case PLAYER_ERROR_FILENOTFOUND:
uri = pc_errored_song_uri(); uri = pc_errored_song_uri(pc);
error = g_strdup_printf("file \"%s\" does not exist or is inaccessible", uri); error = g_strdup_printf("file \"%s\" does not exist or is inaccessible", uri);
g_free(uri); g_free(uri);
return error; return error;
case PLAYER_ERROR_FILE: case PLAYER_ERROR_FILE:
uri = pc_errored_song_uri(); uri = pc_errored_song_uri(pc);
error = g_strdup_printf("problems decoding \"%s\"", uri); error = g_strdup_printf("problems decoding \"%s\"", uri);
g_free(uri); g_free(uri);
return error; return error;
@ -274,7 +284,7 @@ pc_get_error_message(void)
return g_strdup("system error occured"); return g_strdup("system error occured");
case PLAYER_ERROR_UNKTYPE: case PLAYER_ERROR_UNKTYPE:
uri = pc_errored_song_uri(); uri = pc_errored_song_uri(pc);
error = g_strdup_printf("file type of \"%s\" is unknown", uri); error = g_strdup_printf("file type of \"%s\" is unknown", uri);
g_free(uri); g_free(uri);
return error; return error;
@ -285,40 +295,40 @@ pc_get_error_message(void)
} }
static void static void
pc_enqueue_song_locked(struct song *song) pc_enqueue_song_locked(struct player_control *pc, struct song *song)
{ {
assert(song != NULL); assert(song != NULL);
assert(pc.next_song == NULL); assert(pc->next_song == NULL);
pc.next_song = song; pc->next_song = song;
player_command_locked(PLAYER_COMMAND_QUEUE); player_command_locked(pc, PLAYER_COMMAND_QUEUE);
} }
void void
pc_enqueue_song(struct song *song) pc_enqueue_song(struct player_control *pc, struct song *song)
{ {
assert(song != NULL); assert(song != NULL);
player_lock(); player_lock(pc);
pc_enqueue_song_locked(song); pc_enqueue_song_locked(pc, song);
player_unlock(); player_unlock(pc);
} }
bool bool
pc_seek(struct song *song, float seek_time) pc_seek(struct player_control *pc, struct song *song, float seek_time)
{ {
assert(song != NULL); assert(song != NULL);
if (pc.state == PLAYER_STATE_STOP) if (pc->state == PLAYER_STATE_STOP)
return false; return false;
player_lock(); player_lock(pc);
pc.next_song = song; pc->next_song = song;
pc.seek_where = seek_time; pc->seek_where = seek_time;
player_command_locked(PLAYER_COMMAND_SEEK); player_command_locked(pc, PLAYER_COMMAND_SEEK);
player_unlock(); player_unlock(pc);
assert(pc.next_song == NULL); assert(pc->next_song == NULL);
idle_add(IDLE_PLAYER); idle_add(IDLE_PLAYER);
@ -326,51 +336,51 @@ pc_seek(struct song *song, float seek_time)
} }
float float
pc_get_cross_fade(void) pc_get_cross_fade(const struct player_control *pc)
{ {
return pc.cross_fade_seconds; return pc->cross_fade_seconds;
} }
void void
pc_set_cross_fade(float cross_fade_seconds) pc_set_cross_fade(struct player_control *pc, float cross_fade_seconds)
{ {
if (cross_fade_seconds < 0) if (cross_fade_seconds < 0)
cross_fade_seconds = 0; cross_fade_seconds = 0;
pc.cross_fade_seconds = cross_fade_seconds; pc->cross_fade_seconds = cross_fade_seconds;
idle_add(IDLE_OPTIONS); idle_add(IDLE_OPTIONS);
} }
float float
pc_get_mixramp_db(void) pc_get_mixramp_db(const struct player_control *pc)
{ {
return pc.mixramp_db; return pc->mixramp_db;
} }
void void
pc_set_mixramp_db(float mixramp_db) pc_set_mixramp_db(struct player_control *pc, float mixramp_db)
{ {
pc.mixramp_db = mixramp_db; pc->mixramp_db = mixramp_db;
idle_add(IDLE_OPTIONS); idle_add(IDLE_OPTIONS);
} }
float float
pc_get_mixramp_delay(void) pc_get_mixramp_delay(const struct player_control *pc)
{ {
return pc.mixramp_delay_seconds; return pc->mixramp_delay_seconds;
} }
void void
pc_set_mixramp_delay(float mixramp_delay_seconds) pc_set_mixramp_delay(struct player_control *pc, float mixramp_delay_seconds)
{ {
pc.mixramp_delay_seconds = mixramp_delay_seconds; pc->mixramp_delay_seconds = mixramp_delay_seconds;
idle_add(IDLE_OPTIONS); idle_add(IDLE_OPTIONS);
} }
double double
pc_get_total_play_time(void) pc_get_total_play_time(const struct player_control *pc)
{ {
return pc.total_play_time; return pc->total_play_time;
} }

View File

@ -116,28 +116,28 @@ struct player_control {
double total_play_time; double total_play_time;
}; };
extern struct player_control pc; struct player_control *
pc_new(unsigned buffer_chunks, unsigned buffered_before_play);
void pc_init(unsigned buffer_chunks, unsigned buffered_before_play); void
pc_free(struct player_control *pc);
void pc_deinit(void);
/** /**
* Locks the #player_control object. * Locks the #player_control object.
*/ */
static inline void static inline void
player_lock(void) player_lock(struct player_control *pc)
{ {
g_mutex_lock(pc.mutex); g_mutex_lock(pc->mutex);
} }
/** /**
* Unlocks the #player_control object. * Unlocks the #player_control object.
*/ */
static inline void static inline void
player_unlock(void) player_unlock(struct player_control *pc)
{ {
g_mutex_unlock(pc.mutex); g_mutex_unlock(pc->mutex);
} }
/** /**
@ -146,9 +146,9 @@ player_unlock(void)
* to calling this function. * to calling this function.
*/ */
static inline void static inline void
player_wait(void) player_wait(struct player_control *pc)
{ {
g_cond_wait(pc.cond, pc.mutex); g_cond_wait(pc->cond, pc->mutex);
} }
/** /**
@ -159,16 +159,16 @@ player_wait(void)
* Note the small difference to the player_wait() function! * Note the small difference to the player_wait() function!
*/ */
void void
player_wait_decoder(struct decoder_control *dc); player_wait_decoder(struct player_control *pc, struct decoder_control *dc);
/** /**
* Signals the #player_control object. The object should be locked * Signals the #player_control object. The object should be locked
* prior to calling this function. * prior to calling this function.
*/ */
static inline void static inline void
player_signal(void) player_signal(struct player_control *pc)
{ {
g_cond_signal(pc.cond); g_cond_signal(pc->cond);
} }
/** /**
@ -176,11 +176,11 @@ player_signal(void)
* locked by this function. * locked by this function.
*/ */
static inline void static inline void
player_lock_signal(void) player_lock_signal(struct player_control *pc)
{ {
player_lock(); player_lock(pc);
player_signal(); player_signal(pc);
player_unlock(); player_unlock(pc);
} }
/** /**
@ -189,33 +189,34 @@ player_lock_signal(void)
* not point to an invalid pointer. * not point to an invalid pointer.
*/ */
void void
pc_song_deleted(const struct song *song); pc_song_deleted(struct player_control *pc, const struct song *song);
void void
pc_play(struct song *song); pc_play(struct player_control *pc, struct song *song);
/** /**
* see PLAYER_COMMAND_CANCEL * see PLAYER_COMMAND_CANCEL
*/ */
void pc_cancel(void); void
pc_cancel(struct player_control *pc);
void void
pc_set_pause(bool pause_flag); pc_set_pause(struct player_control *pc, bool pause_flag);
void void
pc_pause(void); pc_pause(struct player_control *pc);
void void
pc_kill(void); pc_kill(struct player_control *pc);
void void
pc_get_status(struct player_status *status); pc_get_status(struct player_control *pc, struct player_status *status);
enum player_state enum player_state
pc_get_state(void); pc_get_state(struct player_control *pc);
void void
pc_clear_error(void); pc_clear_error(struct player_control *pc);
/** /**
* Returns the human-readable message describing the last error during * Returns the human-readable message describing the last error during
@ -223,19 +224,19 @@ pc_clear_error(void);
* returned string. * returned string.
*/ */
char * char *
pc_get_error_message(void); pc_get_error_message(struct player_control *pc);
enum player_error enum player_error
pc_get_error(void); pc_get_error(struct player_control *pc);
void void
pc_stop(void); pc_stop(struct player_control *pc);
void void
pc_update_audio(void); pc_update_audio(struct player_control *pc);
void void
pc_enqueue_song(struct song *song); pc_enqueue_song(struct player_control *pc, struct song *song);
/** /**
* Makes the player thread seek the specified song to a position. * Makes the player thread seek the specified song to a position.
@ -244,27 +245,27 @@ pc_enqueue_song(struct song *song);
* playing currently) * playing currently)
*/ */
bool bool
pc_seek(struct song *song, float seek_time); pc_seek(struct player_control *pc, struct song *song, float seek_time);
void void
pc_set_cross_fade(float cross_fade_seconds); pc_set_cross_fade(struct player_control *pc, float cross_fade_seconds);
float float
pc_get_cross_fade(void); pc_get_cross_fade(const struct player_control *pc);
void void
pc_set_mixramp_db(float mixramp_db); pc_set_mixramp_db(struct player_control *pc, float mixramp_db);
float float
pc_get_mixramp_db(void); pc_get_mixramp_db(const struct player_control *pc);
void void
pc_set_mixramp_delay(float mixramp_delay_seconds); pc_set_mixramp_delay(struct player_control *pc, float mixramp_delay_seconds);
float float
pc_get_mixramp_delay(void); pc_get_mixramp_delay(const struct player_control *pc);
double double
pc_get_total_play_time(void); pc_get_total_play_time(const struct player_control *pc);
#endif #endif

View File

@ -48,6 +48,8 @@ enum xfade_state {
}; };
struct player { struct player {
struct player_control *pc;
struct decoder_control *dc; struct decoder_control *dc;
struct music_pipe *pipe; struct music_pipe *pipe;
@ -117,19 +119,21 @@ struct player {
static struct music_buffer *player_buffer; static struct music_buffer *player_buffer;
static void player_command_finished_locked(void) static void
player_command_finished_locked(struct player_control *pc)
{ {
assert(pc.command != PLAYER_COMMAND_NONE); assert(pc->command != PLAYER_COMMAND_NONE);
pc.command = PLAYER_COMMAND_NONE; pc->command = PLAYER_COMMAND_NONE;
g_cond_signal(main_cond); g_cond_signal(main_cond);
} }
static void player_command_finished(void) static void
player_command_finished(struct player_control *pc)
{ {
player_lock(); player_lock(pc);
player_command_finished_locked(); player_command_finished_locked(pc);
player_unlock(); player_unlock(pc);
} }
/** /**
@ -140,12 +144,13 @@ static void player_command_finished(void)
static void static void
player_dc_start(struct player *player, struct music_pipe *pipe) player_dc_start(struct player *player, struct music_pipe *pipe)
{ {
struct player_control *pc = player->pc;
struct decoder_control *dc = player->dc; struct decoder_control *dc = player->dc;
assert(player->queued || pc.command == PLAYER_COMMAND_SEEK); assert(player->queued || pc->command == PLAYER_COMMAND_SEEK);
assert(pc.next_song != NULL); assert(pc->next_song != NULL);
dc_start(dc, pc.next_song, player_buffer, pipe); dc_start(dc, pc->next_song, player_buffer, pipe);
} }
/** /**
@ -208,41 +213,42 @@ player_dc_stop(struct player *player)
static bool static bool
player_wait_for_decoder(struct player *player) player_wait_for_decoder(struct player *player)
{ {
struct player_control *pc = player->pc;
struct decoder_control *dc = player->dc; struct decoder_control *dc = player->dc;
assert(player->queued || pc.command == PLAYER_COMMAND_SEEK); assert(player->queued || pc->command == PLAYER_COMMAND_SEEK);
assert(pc.next_song != NULL); assert(pc->next_song != NULL);
player->queued = false; player->queued = false;
if (decoder_lock_has_failed(dc)) { if (decoder_lock_has_failed(dc)) {
player_lock(); player_lock(pc);
pc.errored_song = dc->song; pc->errored_song = dc->song;
pc.error = PLAYER_ERROR_FILE; pc->error = PLAYER_ERROR_FILE;
pc.next_song = NULL; pc->next_song = NULL;
player_unlock(); player_unlock(pc);
return false; return false;
} }
player->song = pc.next_song; player->song = pc->next_song;
player->elapsed_time = 0.0; player->elapsed_time = 0.0;
/* set the "starting" flag, which will be cleared by /* set the "starting" flag, which will be cleared by
player_check_decoder_startup() */ player_check_decoder_startup() */
player->decoder_starting = true; player->decoder_starting = true;
player_lock(); player_lock(pc);
/* update player_control's song information */ /* update player_control's song information */
pc.total_time = song_get_duration(pc.next_song); pc->total_time = song_get_duration(pc->next_song);
pc.bit_rate = 0; pc->bit_rate = 0;
audio_format_clear(&pc.audio_format); audio_format_clear(&pc->audio_format);
/* clear the queued song */ /* clear the queued song */
pc.next_song = NULL; pc->next_song = NULL;
player_unlock(); player_unlock(pc);
/* call syncPlaylistWithQueue() in the main thread */ /* call syncPlaylistWithQueue() in the main thread */
event_pipe_emit(PIPE_EVENT_PLAYLIST); event_pipe_emit(PIPE_EVENT_PLAYLIST);
@ -280,6 +286,7 @@ real_song_duration(const struct song *song, double decoder_duration)
static bool static bool
player_check_decoder_startup(struct player *player) player_check_decoder_startup(struct player *player)
{ {
struct player_control *pc = player->pc;
struct decoder_control *dc = player->dc; struct decoder_control *dc = player->dc;
assert(player->decoder_starting); assert(player->decoder_starting);
@ -290,10 +297,10 @@ player_check_decoder_startup(struct player *player)
/* the decoder failed */ /* the decoder failed */
decoder_unlock(dc); decoder_unlock(dc);
player_lock(); player_lock(pc);
pc.errored_song = dc->song; pc->errored_song = dc->song;
pc.error = PLAYER_ERROR_FILE; pc->error = PLAYER_ERROR_FILE;
player_unlock(); player_unlock(pc);
return false; return false;
} else if (!decoder_is_starting(dc)) { } else if (!decoder_is_starting(dc)) {
@ -302,15 +309,15 @@ player_check_decoder_startup(struct player *player)
decoder_unlock(dc); decoder_unlock(dc);
if (audio_format_defined(&player->play_audio_format) && if (audio_format_defined(&player->play_audio_format) &&
!audio_output_all_wait(1)) !audio_output_all_wait(pc, 1))
/* the output devices havn't finished playing /* the output devices havn't finished playing
all chunks yet - wait for that */ all chunks yet - wait for that */
return true; return true;
player_lock(); player_lock(pc);
pc.total_time = real_song_duration(dc->song, dc->total_time); pc->total_time = real_song_duration(dc->song, dc->total_time);
pc.audio_format = dc->in_audio_format; pc->audio_format = dc->in_audio_format;
player_unlock(); player_unlock(pc);
player->play_audio_format = dc->out_audio_format; player->play_audio_format = dc->out_audio_format;
player->decoder_starting = false; player->decoder_starting = false;
@ -323,13 +330,13 @@ player_check_decoder_startup(struct player *player)
"while playing \"%s\"", uri); "while playing \"%s\"", uri);
g_free(uri); g_free(uri);
player_lock(); player_lock(pc);
pc.error = PLAYER_ERROR_AUDIO; pc->error = PLAYER_ERROR_AUDIO;
/* pause: the user may resume playback as soon /* pause: the user may resume playback as soon
as an audio output becomes available */ as an audio output becomes available */
pc.state = PLAYER_STATE_PAUSE; pc->state = PLAYER_STATE_PAUSE;
player_unlock(); player_unlock(pc);
player->paused = true; player->paused = true;
return true; return true;
@ -339,7 +346,7 @@ player_check_decoder_startup(struct player *player)
} else { } else {
/* the decoder is not yet ready; wait /* the decoder is not yet ready; wait
some more */ some more */
player_wait_decoder(dc); player_wait_decoder(pc, dc);
decoder_unlock(dc); decoder_unlock(dc);
return true; return true;
@ -393,10 +400,11 @@ player_send_silence(struct player *player)
*/ */
static bool player_seek_decoder(struct player *player) static bool player_seek_decoder(struct player *player)
{ {
struct song *song = pc.next_song; struct player_control *pc = player->pc;
struct song *song = pc->next_song;
struct decoder_control *dc = player->dc; struct decoder_control *dc = player->dc;
assert(pc.next_song != NULL); assert(pc->next_song != NULL);
if (decoder_current_song(dc) != song) { if (decoder_current_song(dc) != song) {
/* the decoder is already decoding the "next" song - /* the decoder is already decoding the "next" song -
@ -412,7 +420,7 @@ static bool player_seek_decoder(struct player *player)
player_dc_start(player, player->pipe); player_dc_start(player, player->pipe);
if (!player_wait_for_decoder(player)) { if (!player_wait_for_decoder(player)) {
/* decoder failure */ /* decoder failure */
player_command_finished(); player_command_finished(pc);
return false; return false;
} }
} else { } else {
@ -424,7 +432,7 @@ static bool player_seek_decoder(struct player *player)
player->pipe = dc->pipe; player->pipe = dc->pipe;
} }
pc.next_song = NULL; pc->next_song = NULL;
player->queued = false; player->queued = false;
} }
@ -433,28 +441,28 @@ static bool player_seek_decoder(struct player *player)
while (player->decoder_starting) { while (player->decoder_starting) {
if (!player_check_decoder_startup(player)) { if (!player_check_decoder_startup(player)) {
/* decoder failure */ /* decoder failure */
player_command_finished(); player_command_finished(pc);
return false; return false;
} }
} }
/* send the SEEK command */ /* send the SEEK command */
double where = pc.seek_where; double where = pc->seek_where;
if (where > pc.total_time) if (where > pc->total_time)
where = pc.total_time - 0.1; where = pc->total_time - 0.1;
if (where < 0.0) if (where < 0.0)
where = 0.0; where = 0.0;
if (!dc_seek(dc, where + song->start_ms / 1000.0)) { if (!dc_seek(dc, where + song->start_ms / 1000.0)) {
/* decoder failure */ /* decoder failure */
player_command_finished(); player_command_finished(pc);
return false; return false;
} }
player->elapsed_time = where; player->elapsed_time = where;
player_command_finished(); player_command_finished(pc);
player->xfade = XFADE_UNKNOWN; player->xfade = XFADE_UNKNOWN;
@ -471,9 +479,10 @@ static bool player_seek_decoder(struct player *player)
*/ */
static void player_process_command(struct player *player) static void player_process_command(struct player *player)
{ {
struct player_control *pc = player->pc;
G_GNUC_UNUSED struct decoder_control *dc = player->dc; G_GNUC_UNUSED struct decoder_control *dc = player->dc;
switch (pc.command) { switch (pc->command) {
case PLAYER_COMMAND_NONE: case PLAYER_COMMAND_NONE:
case PLAYER_COMMAND_STOP: case PLAYER_COMMAND_STOP:
case PLAYER_COMMAND_EXIT: case PLAYER_COMMAND_EXIT:
@ -481,95 +490,95 @@ static void player_process_command(struct player *player)
break; break;
case PLAYER_COMMAND_UPDATE_AUDIO: case PLAYER_COMMAND_UPDATE_AUDIO:
player_unlock(); player_unlock(pc);
audio_output_all_enable_disable(); audio_output_all_enable_disable();
player_lock(); player_lock(pc);
player_command_finished_locked(); player_command_finished_locked(pc);
break; break;
case PLAYER_COMMAND_QUEUE: case PLAYER_COMMAND_QUEUE:
assert(pc.next_song != NULL); assert(pc->next_song != NULL);
assert(!player->queued); assert(!player->queued);
assert(!player_dc_at_next_song(player)); assert(!player_dc_at_next_song(player));
player->queued = true; player->queued = true;
player_command_finished_locked(); player_command_finished_locked(pc);
break; break;
case PLAYER_COMMAND_PAUSE: case PLAYER_COMMAND_PAUSE:
player_unlock(); player_unlock(pc);
player->paused = !player->paused; player->paused = !player->paused;
if (player->paused) { if (player->paused) {
audio_output_all_pause(); audio_output_all_pause();
player_lock(); player_lock(pc);
pc.state = PLAYER_STATE_PAUSE; pc->state = PLAYER_STATE_PAUSE;
} else if (!audio_format_defined(&player->play_audio_format)) { } else if (!audio_format_defined(&player->play_audio_format)) {
/* the decoder hasn't provided an audio format /* the decoder hasn't provided an audio format
yet - don't open the audio device yet */ yet - don't open the audio device yet */
player_lock(); player_lock(pc);
pc.state = PLAYER_STATE_PLAY; pc->state = PLAYER_STATE_PLAY;
} else if (audio_output_all_open(&player->play_audio_format, player_buffer)) { } else if (audio_output_all_open(&player->play_audio_format, player_buffer)) {
/* unpaused, continue playing */ /* unpaused, continue playing */
player_lock(); player_lock(pc);
pc.state = PLAYER_STATE_PLAY; pc->state = PLAYER_STATE_PLAY;
} else { } else {
/* the audio device has failed - rollback to /* the audio device has failed - rollback to
pause mode */ pause mode */
pc.error = PLAYER_ERROR_AUDIO; pc->error = PLAYER_ERROR_AUDIO;
player->paused = true; player->paused = true;
player_lock(); player_lock(pc);
} }
player_command_finished_locked(); player_command_finished_locked(pc);
break; break;
case PLAYER_COMMAND_SEEK: case PLAYER_COMMAND_SEEK:
player_unlock(); player_unlock(pc);
player_seek_decoder(player); player_seek_decoder(player);
player_lock(); player_lock(pc);
break; break;
case PLAYER_COMMAND_CANCEL: case PLAYER_COMMAND_CANCEL:
if (pc.next_song == NULL) { if (pc->next_song == NULL) {
/* the cancel request arrived too late, we're /* the cancel request arrived too late, we're
already playing the queued song... stop already playing the queued song... stop
everything now */ everything now */
pc.command = PLAYER_COMMAND_STOP; pc->command = PLAYER_COMMAND_STOP;
return; return;
} }
if (player_dc_at_next_song(player)) { if (player_dc_at_next_song(player)) {
/* the decoder is already decoding the song - /* the decoder is already decoding the song -
stop it and reset the position */ stop it and reset the position */
player_unlock(); player_unlock(pc);
player_dc_stop(player); player_dc_stop(player);
player_lock(); player_lock(pc);
} }
pc.next_song = NULL; pc->next_song = NULL;
player->queued = false; player->queued = false;
player_command_finished_locked(); player_command_finished_locked(pc);
break; break;
case PLAYER_COMMAND_REFRESH: case PLAYER_COMMAND_REFRESH:
if (audio_format_defined(&player->play_audio_format) && if (audio_format_defined(&player->play_audio_format) &&
!player->paused) { !player->paused) {
player_unlock(); player_unlock(pc);
audio_output_all_check(); audio_output_all_check();
player_lock(); player_lock(pc);
} }
pc.elapsed_time = audio_output_all_get_elapsed_time(); pc->elapsed_time = audio_output_all_get_elapsed_time();
if (pc.elapsed_time < 0.0) if (pc->elapsed_time < 0.0)
pc.elapsed_time = player->elapsed_time; pc->elapsed_time = player->elapsed_time;
player_command_finished_locked(); player_command_finished_locked(pc);
break; break;
} }
} }
@ -605,7 +614,8 @@ update_song_tag(struct song *song, const struct tag *new_tag)
* Player lock is not held. * Player lock is not held.
*/ */
static bool static bool
play_chunk(struct song *song, struct music_chunk *chunk, play_chunk(struct player_control *pc,
struct song *song, struct music_chunk *chunk,
const struct audio_format *format) const struct audio_format *format)
{ {
assert(music_chunk_check_format(chunk, format)); assert(music_chunk_check_format(chunk, format));
@ -618,14 +628,14 @@ play_chunk(struct song *song, struct music_chunk *chunk,
return true; return true;
} }
pc.bit_rate = chunk->bit_rate; pc->bit_rate = chunk->bit_rate;
/* send the chunk to the audio outputs */ /* send the chunk to the audio outputs */
if (!audio_output_all_play(chunk)) if (!audio_output_all_play(chunk))
return false; return false;
pc.total_play_time += (double)chunk->length / pc->total_play_time += (double)chunk->length /
audio_format_time_to_size(format); audio_format_time_to_size(format);
return true; return true;
} }
@ -639,9 +649,10 @@ play_chunk(struct song *song, struct music_chunk *chunk,
static bool static bool
play_next_chunk(struct player *player) play_next_chunk(struct player *player)
{ {
struct player_control *pc = player->pc;
struct decoder_control *dc = player->dc; struct decoder_control *dc = player->dc;
if (!audio_output_all_wait(64)) if (!audio_output_all_wait(pc, 64))
/* the output pipe is still large enough, don't send /* the output pipe is still large enough, don't send
another chunk */ another chunk */
return true; return true;
@ -678,7 +689,7 @@ play_next_chunk(struct player *player)
other_chunk->tag); other_chunk->tag);
other_chunk->tag = NULL; other_chunk->tag = NULL;
if (isnan(pc.mixramp_delay_seconds)) { if (isnan(pc->mixramp_delay_seconds)) {
chunk->mix_ratio = ((float)cross_fade_position) chunk->mix_ratio = ((float)cross_fade_position)
/ player->cross_fade_chunks; / player->cross_fade_chunks;
} else { } else {
@ -713,7 +724,7 @@ play_next_chunk(struct player *player)
} else { } else {
/* wait for the decoder */ /* wait for the decoder */
decoder_signal(dc); decoder_signal(dc);
player_wait_decoder(dc); player_wait_decoder(pc, dc);
decoder_unlock(dc); decoder_unlock(dc);
return true; return true;
@ -736,19 +747,20 @@ play_next_chunk(struct player *player)
/* play the current chunk */ /* play the current chunk */
if (!play_chunk(player->song, chunk, &player->play_audio_format)) { if (!play_chunk(player->pc, player->song, chunk,
&player->play_audio_format)) {
music_buffer_return(player_buffer, chunk); music_buffer_return(player_buffer, chunk);
player_lock(); player_lock(pc);
pc.error = PLAYER_ERROR_AUDIO; pc->error = PLAYER_ERROR_AUDIO;
/* pause: the user may resume playback as soon as an /* pause: the user may resume playback as soon as an
audio output becomes available */ audio output becomes available */
pc.state = PLAYER_STATE_PAUSE; pc->state = PLAYER_STATE_PAUSE;
player->paused = true; player->paused = true;
player_unlock(); player_unlock(pc);
return false; return false;
} }
@ -758,7 +770,7 @@ play_next_chunk(struct player *player)
larger block at a time */ larger block at a time */
decoder_lock(dc); decoder_lock(dc);
if (!decoder_is_idle(dc) && if (!decoder_is_idle(dc) &&
music_pipe_size(dc->pipe) <= (pc.buffered_before_play + music_pipe_size(dc->pipe) <= (pc->buffered_before_play +
music_buffer_size(player_buffer) * 3) / 4) music_buffer_size(player_buffer) * 3) / 4)
decoder_signal(dc); decoder_signal(dc);
decoder_unlock(dc); decoder_unlock(dc);
@ -800,9 +812,10 @@ player_song_border(struct player *player)
* basically a state machine, which multiplexes data between the * basically a state machine, which multiplexes data between the
* decoder thread and the output threads. * decoder thread and the output threads.
*/ */
static void do_play(struct decoder_control *dc) static void do_play(struct player_control *pc, struct decoder_control *dc)
{ {
struct player player = { struct player player = {
.pc = pc,
.dc = dc, .dc = dc,
.buffering = true, .buffering = true,
.decoder_starting = false, .decoder_starting = false,
@ -816,42 +829,42 @@ static void do_play(struct decoder_control *dc)
.elapsed_time = 0.0, .elapsed_time = 0.0,
}; };
player_unlock(); player_unlock(pc);
player.pipe = music_pipe_new(); player.pipe = music_pipe_new();
player_dc_start(&player, player.pipe); player_dc_start(&player, player.pipe);
if (!player_wait_for_decoder(&player)) { if (!player_wait_for_decoder(&player)) {
player_dc_stop(&player); player_dc_stop(&player);
player_command_finished(); player_command_finished(pc);
music_pipe_free(player.pipe); music_pipe_free(player.pipe);
event_pipe_emit(PIPE_EVENT_PLAYLIST); event_pipe_emit(PIPE_EVENT_PLAYLIST);
player_lock(); player_lock(pc);
return; return;
} }
player_lock(); player_lock(pc);
pc.state = PLAYER_STATE_PLAY; pc->state = PLAYER_STATE_PLAY;
player_command_finished_locked(); player_command_finished_locked(pc);
while (true) { while (true) {
player_process_command(&player); player_process_command(&player);
if (pc.command == PLAYER_COMMAND_STOP || if (pc->command == PLAYER_COMMAND_STOP ||
pc.command == PLAYER_COMMAND_EXIT || pc->command == PLAYER_COMMAND_EXIT ||
pc.command == PLAYER_COMMAND_CLOSE_AUDIO) { pc->command == PLAYER_COMMAND_CLOSE_AUDIO) {
player_unlock(); player_unlock(pc);
audio_output_all_cancel(); audio_output_all_cancel();
break; break;
} }
player_unlock(); player_unlock(pc);
if (player.buffering) { if (player.buffering) {
/* buffering at the start of the song - wait /* buffering at the start of the song - wait
until the buffer is large enough, to until the buffer is large enough, to
prevent stuttering on slow machines */ prevent stuttering on slow machines */
if (music_pipe_size(player.pipe) < pc.buffered_before_play && if (music_pipe_size(player.pipe) < pc->buffered_before_play &&
!decoder_lock_is_idle(dc)) { !decoder_lock_is_idle(dc)) {
/* not enough decoded buffer space yet */ /* not enough decoded buffer space yet */
@ -863,9 +876,9 @@ static void do_play(struct decoder_control *dc)
decoder_lock(dc); decoder_lock(dc);
/* XXX race condition: check decoder again */ /* XXX race condition: check decoder again */
player_wait_decoder(dc); player_wait_decoder(pc, dc);
decoder_unlock(dc); decoder_unlock(dc);
player_lock(); player_lock(pc);
continue; continue;
} else { } else {
/* buffering is complete */ /* buffering is complete */
@ -889,7 +902,7 @@ static void do_play(struct decoder_control *dc)
!dc_seek(dc, song->start_ms / 1000.0)) !dc_seek(dc, song->start_ms / 1000.0))
player_dc_stop(&player); player_dc_stop(&player);
player_lock(); player_lock(pc);
continue; continue;
} }
@ -918,9 +931,9 @@ static void do_play(struct decoder_control *dc)
calculate how many chunks will be required calculate how many chunks will be required
for it */ for it */
player.cross_fade_chunks = player.cross_fade_chunks =
cross_fade_calc(pc.cross_fade_seconds, dc->total_time, cross_fade_calc(pc->cross_fade_seconds, dc->total_time,
pc.mixramp_db, pc->mixramp_db,
pc.mixramp_delay_seconds, pc->mixramp_delay_seconds,
dc->replay_gain_db, dc->replay_gain_db,
dc->replay_gain_prev_db, dc->replay_gain_prev_db,
dc->mixramp_start, dc->mixramp_start,
@ -928,7 +941,7 @@ static void do_play(struct decoder_control *dc)
&dc->out_audio_format, &dc->out_audio_format,
&player.play_audio_format, &player.play_audio_format,
music_buffer_size(player_buffer) - music_buffer_size(player_buffer) -
pc.buffered_before_play); pc->buffered_before_play);
if (player.cross_fade_chunks > 0) { if (player.cross_fade_chunks > 0) {
player.xfade = XFADE_ENABLED; player.xfade = XFADE_ENABLED;
player.cross_fading = false; player.cross_fading = false;
@ -939,10 +952,10 @@ static void do_play(struct decoder_control *dc)
} }
if (player.paused) { if (player.paused) {
player_lock(); player_lock(pc);
if (pc.command == PLAYER_COMMAND_NONE) if (pc->command == PLAYER_COMMAND_NONE)
player_wait(); player_wait(pc);
continue; continue;
} else if (!music_pipe_empty(player.pipe)) { } else if (!music_pipe_empty(player.pipe)) {
/* at least one music chunk is ready - send it /* at least one music chunk is ready - send it
@ -979,7 +992,7 @@ static void do_play(struct decoder_control *dc)
break; break;
} }
player_lock(); player_lock(pc);
} }
player_dc_stop(&player); player_dc_stop(&player);
@ -990,113 +1003,116 @@ static void do_play(struct decoder_control *dc)
if (player.cross_fade_tag != NULL) if (player.cross_fade_tag != NULL)
tag_free(player.cross_fade_tag); tag_free(player.cross_fade_tag);
player_lock(); player_lock(pc);
if (player.queued) { if (player.queued) {
assert(pc.next_song != NULL); assert(pc->next_song != NULL);
pc.next_song = NULL; pc->next_song = NULL;
} }
pc.state = PLAYER_STATE_STOP; pc->state = PLAYER_STATE_STOP;
player_unlock(); player_unlock(pc);
event_pipe_emit(PIPE_EVENT_PLAYLIST); event_pipe_emit(PIPE_EVENT_PLAYLIST);
player_lock(); player_lock(pc);
} }
static gpointer player_task(G_GNUC_UNUSED gpointer arg) static gpointer
player_task(gpointer arg)
{ {
struct player_control *pc = arg;
struct decoder_control dc; struct decoder_control dc;
dc_init(&dc); dc_init(&dc, pc);
decoder_thread_start(&dc); decoder_thread_start(&dc);
player_buffer = music_buffer_new(pc.buffer_chunks); player_buffer = music_buffer_new(pc->buffer_chunks);
player_lock(); player_lock(pc);
while (1) { while (1) {
switch (pc.command) { switch (pc->command) {
case PLAYER_COMMAND_QUEUE: case PLAYER_COMMAND_QUEUE:
assert(pc.next_song != NULL); assert(pc->next_song != NULL);
do_play(&dc); do_play(pc, &dc);
break; break;
case PLAYER_COMMAND_STOP: case PLAYER_COMMAND_STOP:
player_unlock(); player_unlock(pc);
audio_output_all_cancel(); audio_output_all_cancel();
player_lock(); player_lock(pc);
/* fall through */ /* fall through */
case PLAYER_COMMAND_SEEK: case PLAYER_COMMAND_SEEK:
case PLAYER_COMMAND_PAUSE: case PLAYER_COMMAND_PAUSE:
pc.next_song = NULL; pc->next_song = NULL;
player_command_finished_locked(); player_command_finished_locked(pc);
break; break;
case PLAYER_COMMAND_CLOSE_AUDIO: case PLAYER_COMMAND_CLOSE_AUDIO:
player_unlock(); player_unlock(pc);
audio_output_all_release(); audio_output_all_release();
player_lock(); player_lock(pc);
player_command_finished_locked(); player_command_finished_locked(pc);
#ifndef NDEBUG #ifndef NDEBUG
/* in the DEBUG build, check for leaked /* in the DEBUG build, check for leaked
music_chunk objects by freeing the music_chunk objects by freeing the
music_buffer */ music_buffer */
music_buffer_free(player_buffer); music_buffer_free(player_buffer);
player_buffer = music_buffer_new(pc.buffer_chunks); player_buffer = music_buffer_new(pc->buffer_chunks);
#endif #endif
break; break;
case PLAYER_COMMAND_UPDATE_AUDIO: case PLAYER_COMMAND_UPDATE_AUDIO:
player_unlock(); player_unlock(pc);
audio_output_all_enable_disable(); audio_output_all_enable_disable();
player_lock(); player_lock(pc);
player_command_finished_locked(); player_command_finished_locked(pc);
break; break;
case PLAYER_COMMAND_EXIT: case PLAYER_COMMAND_EXIT:
player_unlock(); player_unlock(pc);
dc_quit(&dc); dc_quit(&dc);
dc_deinit(&dc); dc_deinit(&dc);
audio_output_all_close(); audio_output_all_close();
music_buffer_free(player_buffer); music_buffer_free(player_buffer);
player_command_finished(); player_command_finished(pc);
return NULL; return NULL;
case PLAYER_COMMAND_CANCEL: case PLAYER_COMMAND_CANCEL:
pc.next_song = NULL; pc->next_song = NULL;
player_command_finished_locked(); player_command_finished_locked(pc);
break; break;
case PLAYER_COMMAND_REFRESH: case PLAYER_COMMAND_REFRESH:
/* no-op when not playing */ /* no-op when not playing */
player_command_finished_locked(); player_command_finished_locked(pc);
break; break;
case PLAYER_COMMAND_NONE: case PLAYER_COMMAND_NONE:
player_wait(); player_wait(pc);
break; break;
} }
} }
} }
void player_create(void) void
player_create(struct player_control *pc)
{ {
assert(pc.thread == NULL); assert(pc->thread == NULL);
GError *e = NULL; GError *e = NULL;
pc.thread = g_thread_create(player_task, NULL, true, &e); pc->thread = g_thread_create(player_task, pc, true, &e);
if (pc.thread == NULL) if (pc->thread == NULL)
MPD_ERROR("Failed to spawn player task: %s", e->message); MPD_ERROR("Failed to spawn player task: %s", e->message);
} }

View File

@ -37,6 +37,9 @@
#ifndef MPD_PLAYER_THREAD_H #ifndef MPD_PLAYER_THREAD_H
#define MPD_PLAYER_THREAD_H #define MPD_PLAYER_THREAD_H
void player_create(void); struct player_control;
void
player_create(struct player_control *pc);
#endif #endif

View File

@ -75,7 +75,8 @@ playlist_finish(struct playlist *playlist)
* Queue a song, addressed by its order number. * Queue a song, addressed by its order number.
*/ */
static void static void
playlist_queue_song_order(struct playlist *playlist, unsigned order) playlist_queue_song_order(struct playlist *playlist, struct player_control *pc,
unsigned order)
{ {
struct song *song; struct song *song;
char *uri; char *uri;
@ -89,16 +90,16 @@ playlist_queue_song_order(struct playlist *playlist, unsigned order)
g_debug("queue song %i:\"%s\"", playlist->queued, uri); g_debug("queue song %i:\"%s\"", playlist->queued, uri);
g_free(uri); g_free(uri);
pc_enqueue_song(song); pc_enqueue_song(pc, song);
} }
/** /**
* Called if the player thread has started playing the "queued" song. * Called if the player thread has started playing the "queued" song.
*/ */
static void static void
playlist_song_started(struct playlist *playlist) playlist_song_started(struct playlist *playlist, struct player_control *pc)
{ {
assert(pc.next_song == NULL); assert(pc->next_song == NULL);
assert(playlist->queued >= -1); assert(playlist->queued >= -1);
/* queued song has started: copy queued to current, /* queued song has started: copy queued to current,
@ -110,11 +111,13 @@ playlist_song_started(struct playlist *playlist)
/* Pause if we are in single mode. */ /* Pause if we are in single mode. */
if(playlist->queue.single && !playlist->queue.repeat) { if(playlist->queue.single && !playlist->queue.repeat) {
pc_set_pause(true); pc_set_pause(pc, true);
} }
if(playlist->queue.consume) if(playlist->queue.consume)
playlist_delete(playlist, queue_order_to_position(&playlist->queue, current)); playlist_delete(playlist, pc,
queue_order_to_position(&playlist->queue,
current));
idle_add(IDLE_PLAYER); idle_add(IDLE_PLAYER);
} }
@ -129,7 +132,9 @@ playlist_get_queued_song(struct playlist *playlist)
} }
void void
playlist_update_queued_song(struct playlist *playlist, const struct song *prev) playlist_update_queued_song(struct playlist *playlist,
struct player_control *pc,
const struct song *prev)
{ {
int next_order; int next_order;
const struct song *next_song; const struct song *next_song;
@ -170,20 +175,21 @@ playlist_update_queued_song(struct playlist *playlist, const struct song *prev)
if (prev != NULL && next_song != prev) { if (prev != NULL && next_song != prev) {
/* clear the currently queued song */ /* clear the currently queued song */
pc_cancel(); pc_cancel(pc);
playlist->queued = -1; playlist->queued = -1;
} }
if (next_order >= 0) { if (next_order >= 0) {
if (next_song != prev) if (next_song != prev)
playlist_queue_song_order(playlist, next_order); playlist_queue_song_order(playlist, pc, next_order);
else else
playlist->queued = next_order; playlist->queued = next_order;
} }
} }
void void
playlist_play_order(struct playlist *playlist, int orderNum) playlist_play_order(struct playlist *playlist, struct player_control *pc,
int orderNum)
{ {
struct song *song; struct song *song;
char *uri; char *uri;
@ -197,46 +203,46 @@ playlist_play_order(struct playlist *playlist, int orderNum)
g_debug("play %i:\"%s\"", orderNum, uri); g_debug("play %i:\"%s\"", orderNum, uri);
g_free(uri); g_free(uri);
pc_play(song); pc_play(pc, song);
playlist->current = orderNum; playlist->current = orderNum;
} }
static void static void
playlist_resume_playback(struct playlist *playlist); playlist_resume_playback(struct playlist *playlist, struct player_control *pc);
/** /**
* This is the "PLAYLIST" event handler. It is invoked by the player * This is the "PLAYLIST" event handler. It is invoked by the player
* thread whenever it requests a new queued song, or when it exits. * thread whenever it requests a new queued song, or when it exits.
*/ */
void void
playlist_sync(struct playlist *playlist) playlist_sync(struct playlist *playlist, struct player_control *pc)
{ {
if (!playlist->playing) if (!playlist->playing)
/* this event has reached us out of sync: we aren't /* this event has reached us out of sync: we aren't
playing anymore; ignore the event */ playing anymore; ignore the event */
return; return;
player_lock(); player_lock(pc);
enum player_state pc_state = pc_get_state(); enum player_state pc_state = pc_get_state(pc);
const struct song *pc_next_song = pc.next_song; const struct song *pc_next_song = pc->next_song;
player_unlock(); player_unlock(pc);
if (pc_state == PLAYER_STATE_STOP) if (pc_state == PLAYER_STATE_STOP)
/* the player thread has stopped: check if playback /* the player thread has stopped: check if playback
should be restarted with the next song. That can should be restarted with the next song. That can
happen if the playlist isn't filling the queue fast happen if the playlist isn't filling the queue fast
enough */ enough */
playlist_resume_playback(playlist); playlist_resume_playback(playlist, pc);
else { else {
/* check if the player thread has already started /* check if the player thread has already started
playing the queued song */ playing the queued song */
if (pc_next_song == NULL && playlist->queued != -1) if (pc_next_song == NULL && playlist->queued != -1)
playlist_song_started(playlist); playlist_song_started(playlist, pc);
/* make sure the queued song is always set (if /* make sure the queued song is always set (if
possible) */ possible) */
if (pc.next_song == NULL && playlist->queued < 0) if (pc->next_song == NULL && playlist->queued < 0)
playlist_update_queued_song(playlist, NULL); playlist_update_queued_song(playlist, pc, NULL);
} }
} }
@ -245,14 +251,14 @@ playlist_sync(struct playlist *playlist)
* decide whether to re-start playback * decide whether to re-start playback
*/ */
static void static void
playlist_resume_playback(struct playlist *playlist) playlist_resume_playback(struct playlist *playlist, struct player_control *pc)
{ {
enum player_error error; enum player_error error;
assert(playlist->playing); assert(playlist->playing);
assert(pc_get_state() == PLAYER_STATE_STOP); assert(pc_get_state(pc) == PLAYER_STATE_STOP);
error = pc_get_error(); error = pc_get_error(pc);
if (error == PLAYER_ERROR_NOERROR) if (error == PLAYER_ERROR_NOERROR)
playlist->error_count = 0; playlist->error_count = 0;
else else
@ -263,10 +269,10 @@ playlist_resume_playback(struct playlist *playlist)
playlist->error_count >= queue_length(&playlist->queue)) playlist->error_count >= queue_length(&playlist->queue))
/* too many errors, or critical error: stop /* too many errors, or critical error: stop
playback */ playback */
playlist_stop(playlist); playlist_stop(playlist, pc);
else else
/* continue playback at the next song */ /* continue playback at the next song */
playlist_next(playlist); playlist_next(playlist, pc);
} }
bool bool
@ -294,7 +300,8 @@ playlist_get_consume(const struct playlist *playlist)
} }
void void
playlist_set_repeat(struct playlist *playlist, bool status) playlist_set_repeat(struct playlist *playlist, struct player_control *pc,
bool status)
{ {
if (status == playlist->queue.repeat) if (status == playlist->queue.repeat)
return; return;
@ -303,7 +310,7 @@ playlist_set_repeat(struct playlist *playlist, bool status)
/* if the last song is currently being played, the "next song" /* if the last song is currently being played, the "next song"
might change when repeat mode is toggled */ might change when repeat mode is toggled */
playlist_update_queued_song(playlist, playlist_update_queued_song(playlist, pc,
playlist_get_queued_song(playlist)); playlist_get_queued_song(playlist));
idle_add(IDLE_OPTIONS); idle_add(IDLE_OPTIONS);
@ -321,7 +328,8 @@ playlist_order(struct playlist *playlist)
} }
void void
playlist_set_single(struct playlist *playlist, bool status) playlist_set_single(struct playlist *playlist, struct player_control *pc,
bool status)
{ {
if (status == playlist->queue.single) if (status == playlist->queue.single)
return; return;
@ -330,7 +338,7 @@ playlist_set_single(struct playlist *playlist, bool status)
/* if the last song is currently being played, the "next song" /* if the last song is currently being played, the "next song"
might change when single mode is toggled */ might change when single mode is toggled */
playlist_update_queued_song(playlist, playlist_update_queued_song(playlist, pc,
playlist_get_queued_song(playlist)); playlist_get_queued_song(playlist));
idle_add(IDLE_OPTIONS); idle_add(IDLE_OPTIONS);
@ -347,7 +355,8 @@ playlist_set_consume(struct playlist *playlist, bool status)
} }
void void
playlist_set_random(struct playlist *playlist, bool status) playlist_set_random(struct playlist *playlist, struct player_control *pc,
bool status)
{ {
const struct song *queued; const struct song *queued;
@ -384,7 +393,7 @@ playlist_set_random(struct playlist *playlist, bool status)
} else } else
playlist_order(playlist); playlist_order(playlist);
playlist_update_queued_song(playlist, queued); playlist_update_queued_song(playlist, pc, queued);
idle_add(IDLE_OPTIONS); idle_add(IDLE_OPTIONS);
} }

View File

@ -26,6 +26,8 @@
#define PLAYLIST_COMMENT '#' #define PLAYLIST_COMMENT '#'
struct player_control;
enum playlist_result { enum playlist_result {
PLAYLIST_RESULT_SUCCESS, PLAYLIST_RESULT_SUCCESS,
PLAYLIST_RESULT_ERRNO, PLAYLIST_RESULT_ERRNO,
@ -111,7 +113,7 @@ playlist_get_queue(const struct playlist *playlist)
} }
void void
playlist_clear(struct playlist *playlist); playlist_clear(struct playlist *playlist, struct player_control *pc);
#ifndef WIN32 #ifndef WIN32
/** /**
@ -119,20 +121,21 @@ playlist_clear(struct playlist *playlist);
* but only if the file's owner is equal to the specified uid. * but only if the file's owner is equal to the specified uid.
*/ */
enum playlist_result enum playlist_result
playlist_append_file(struct playlist *playlist, const char *path, int uid, playlist_append_file(struct playlist *playlist, struct player_control *pc,
unsigned *added_id); const char *path, int uid, unsigned *added_id);
#endif #endif
enum playlist_result enum playlist_result
playlist_append_uri(struct playlist *playlist, const char *file, playlist_append_uri(struct playlist *playlist, struct player_control *pc,
unsigned *added_id); const char *file, unsigned *added_id);
enum playlist_result enum playlist_result
playlist_append_song(struct playlist *playlist, playlist_append_song(struct playlist *playlist, struct player_control *pc,
struct song *song, unsigned *added_id); struct song *song, unsigned *added_id);
enum playlist_result enum playlist_result
playlist_delete(struct playlist *playlist, unsigned song); playlist_delete(struct playlist *playlist, struct player_control *pc,
unsigned song);
/** /**
* Deletes a range of songs from the playlist. * Deletes a range of songs from the playlist.
@ -141,64 +144,77 @@ playlist_delete(struct playlist *playlist, unsigned song);
* @param end the position after the last song to delete * @param end the position after the last song to delete
*/ */
enum playlist_result enum playlist_result
playlist_delete_range(struct playlist *playlist, unsigned start, unsigned end); playlist_delete_range(struct playlist *playlist, struct player_control *pc,
unsigned start, unsigned end);
enum playlist_result enum playlist_result
playlist_delete_id(struct playlist *playlist, unsigned song); playlist_delete_id(struct playlist *playlist, struct player_control *pc,
unsigned song);
void void
playlist_stop(struct playlist *playlist); playlist_stop(struct playlist *playlist, struct player_control *pc);
enum playlist_result enum playlist_result
playlist_play(struct playlist *playlist, int song); playlist_play(struct playlist *playlist, struct player_control *pc,
int song);
enum playlist_result enum playlist_result
playlist_play_id(struct playlist *playlist, int song); playlist_play_id(struct playlist *playlist, struct player_control *pc,
int song);
void void
playlist_next(struct playlist *playlist); playlist_next(struct playlist *playlist, struct player_control *pc);
void void
playlist_sync(struct playlist *playlist); playlist_sync(struct playlist *playlist, struct player_control *pc);
void void
playlist_previous(struct playlist *playlist); playlist_previous(struct playlist *playlist, struct player_control *pc);
void void
playlist_shuffle(struct playlist *playlist, unsigned start, unsigned end); playlist_shuffle(struct playlist *playlist, struct player_control *pc,
unsigned start, unsigned end);
void void
playlist_delete_song(struct playlist *playlist, const struct song *song); playlist_delete_song(struct playlist *playlist, struct player_control *pc,
const struct song *song);
enum playlist_result enum playlist_result
playlist_move_range(struct playlist *playlist, unsigned start, unsigned end, int to); playlist_move_range(struct playlist *playlist, struct player_control *pc,
unsigned start, unsigned end, int to);
enum playlist_result enum playlist_result
playlist_move_id(struct playlist *playlist, unsigned id, int to); playlist_move_id(struct playlist *playlist, struct player_control *pc,
unsigned id, int to);
enum playlist_result enum playlist_result
playlist_swap_songs(struct playlist *playlist, unsigned song1, unsigned song2); playlist_swap_songs(struct playlist *playlist, struct player_control *pc,
unsigned song1, unsigned song2);
enum playlist_result enum playlist_result
playlist_swap_songs_id(struct playlist *playlist, unsigned id1, unsigned id2); playlist_swap_songs_id(struct playlist *playlist, struct player_control *pc,
unsigned id1, unsigned id2);
bool bool
playlist_get_repeat(const struct playlist *playlist); playlist_get_repeat(const struct playlist *playlist);
void void
playlist_set_repeat(struct playlist *playlist, bool status); playlist_set_repeat(struct playlist *playlist, struct player_control *pc,
bool status);
bool bool
playlist_get_random(const struct playlist *playlist); playlist_get_random(const struct playlist *playlist);
void void
playlist_set_random(struct playlist *playlist, bool status); playlist_set_random(struct playlist *playlist, struct player_control *pc,
bool status);
bool bool
playlist_get_single(const struct playlist *playlist); playlist_get_single(const struct playlist *playlist);
void void
playlist_set_single(struct playlist *playlist, bool status); playlist_set_single(struct playlist *playlist, struct player_control *pc,
bool status);
bool bool
playlist_get_consume(const struct playlist *playlist); playlist_get_consume(const struct playlist *playlist);
@ -222,10 +238,11 @@ unsigned long
playlist_get_version(const struct playlist *playlist); playlist_get_version(const struct playlist *playlist);
enum playlist_result enum playlist_result
playlist_seek_song(struct playlist *playlist, unsigned song, float seek_time); playlist_seek_song(struct playlist *playlist, struct player_control *pc,
unsigned song, float seek_time);
enum playlist_result enum playlist_result
playlist_seek_song_id(struct playlist *playlist, playlist_seek_song_id(struct playlist *playlist, struct player_control *pc,
unsigned id, float seek_time); unsigned id, float seek_time);
void void

View File

@ -32,7 +32,8 @@
#undef G_LOG_DOMAIN #undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "playlist" #define G_LOG_DOMAIN "playlist"
void playlist_stop(struct playlist *playlist) void
playlist_stop(struct playlist *playlist, struct player_control *pc)
{ {
if (!playlist->playing) if (!playlist->playing)
return; return;
@ -40,7 +41,7 @@ void playlist_stop(struct playlist *playlist)
assert(playlist->current >= 0); assert(playlist->current >= 0);
g_debug("stop"); g_debug("stop");
pc_stop(); pc_stop(pc);
playlist->queued = -1; playlist->queued = -1;
playlist->playing = false; playlist->playing = false;
@ -62,11 +63,13 @@ void playlist_stop(struct playlist *playlist)
} }
} }
enum playlist_result playlist_play(struct playlist *playlist, int song) enum playlist_result
playlist_play(struct playlist *playlist, struct player_control *pc,
int song)
{ {
unsigned i = song; unsigned i = song;
pc_clear_error(); pc_clear_error(pc);
if (song == -1) { if (song == -1) {
/* play any song ("current" song, or the first song */ /* play any song ("current" song, or the first song */
@ -77,7 +80,7 @@ enum playlist_result playlist_play(struct playlist *playlist, int song)
if (playlist->playing) { if (playlist->playing) {
/* already playing: unpause playback, just in /* already playing: unpause playback, just in
case it was paused, and return */ case it was paused, and return */
pc_set_pause(false); pc_set_pause(pc, false);
return PLAYLIST_RESULT_SUCCESS; return PLAYLIST_RESULT_SUCCESS;
} }
@ -109,28 +112,29 @@ enum playlist_result playlist_play(struct playlist *playlist, int song)
playlist->stop_on_error = false; playlist->stop_on_error = false;
playlist->error_count = 0; playlist->error_count = 0;
playlist_play_order(playlist, i); playlist_play_order(playlist, pc, i);
return PLAYLIST_RESULT_SUCCESS; return PLAYLIST_RESULT_SUCCESS;
} }
enum playlist_result enum playlist_result
playlist_play_id(struct playlist *playlist, int id) playlist_play_id(struct playlist *playlist, struct player_control *pc,
int id)
{ {
int song; int song;
if (id == -1) { if (id == -1) {
return playlist_play(playlist, id); return playlist_play(playlist, pc, id);
} }
song = queue_id_to_position(&playlist->queue, id); song = queue_id_to_position(&playlist->queue, id);
if (song < 0) if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG; return PLAYLIST_RESULT_NO_SUCH_SONG;
return playlist_play(playlist, song); return playlist_play(playlist, pc, song);
} }
void void
playlist_next(struct playlist *playlist) playlist_next(struct playlist *playlist, struct player_control *pc)
{ {
int next_order; int next_order;
int current; int current;
@ -149,7 +153,7 @@ playlist_next(struct playlist *playlist)
next_order = queue_next_order(&playlist->queue, playlist->current); next_order = queue_next_order(&playlist->queue, playlist->current);
if (next_order < 0) { if (next_order < 0) {
/* no song after this one: stop playback */ /* no song after this one: stop playback */
playlist_stop(playlist); playlist_stop(playlist, pc);
/* reset "current song" */ /* reset "current song" */
playlist->current = -1; playlist->current = -1;
@ -170,15 +174,18 @@ playlist_next(struct playlist *playlist)
discard them anyway */ discard them anyway */
} }
playlist_play_order(playlist, next_order); playlist_play_order(playlist, pc, next_order);
} }
/* Consume mode removes each played songs. */ /* Consume mode removes each played songs. */
if(playlist->queue.consume) if(playlist->queue.consume)
playlist_delete(playlist, queue_order_to_position(&playlist->queue, current)); playlist_delete(playlist, pc,
queue_order_to_position(&playlist->queue,
current));
} }
void playlist_previous(struct playlist *playlist) void
playlist_previous(struct playlist *playlist, struct player_control *pc)
{ {
if (!playlist->playing) if (!playlist->playing)
return; return;
@ -187,21 +194,22 @@ void playlist_previous(struct playlist *playlist)
if (playlist->current > 0) { if (playlist->current > 0) {
/* play the preceding song */ /* play the preceding song */
playlist_play_order(playlist, playlist_play_order(playlist, pc,
playlist->current - 1); playlist->current - 1);
} else if (playlist->queue.repeat) { } else if (playlist->queue.repeat) {
/* play the last song in "repeat" mode */ /* play the last song in "repeat" mode */
playlist_play_order(playlist, playlist_play_order(playlist, pc,
queue_length(&playlist->queue) - 1); queue_length(&playlist->queue) - 1);
} else { } else {
/* re-start playing the current song if it's /* re-start playing the current song if it's
the first one */ the first one */
playlist_play_order(playlist, playlist->current); playlist_play_order(playlist, pc, playlist->current);
} }
} }
enum playlist_result enum playlist_result
playlist_seek_song(struct playlist *playlist, unsigned song, float seek_time) playlist_seek_song(struct playlist *playlist, struct player_control *pc,
unsigned song, float seek_time)
{ {
const struct song *queued; const struct song *queued;
unsigned i; unsigned i;
@ -217,7 +225,7 @@ playlist_seek_song(struct playlist *playlist, unsigned song, float seek_time)
else else
i = song; i = song;
pc_clear_error(); pc_clear_error(pc);
playlist->stop_on_error = true; playlist->stop_on_error = true;
playlist->error_count = 0; playlist->error_count = 0;
@ -225,29 +233,30 @@ playlist_seek_song(struct playlist *playlist, unsigned song, float seek_time)
/* seeking is not within the current song - first /* seeking is not within the current song - first
start playing the new song */ start playing the new song */
playlist_play_order(playlist, i); playlist_play_order(playlist, pc, i);
queued = NULL; queued = NULL;
} }
success = pc_seek(queue_get_order(&playlist->queue, i), seek_time); success = pc_seek(pc, queue_get_order(&playlist->queue, i), seek_time);
if (!success) { if (!success) {
playlist_update_queued_song(playlist, queued); playlist_update_queued_song(playlist, pc, queued);
return PLAYLIST_RESULT_NOT_PLAYING; return PLAYLIST_RESULT_NOT_PLAYING;
} }
playlist->queued = -1; playlist->queued = -1;
playlist_update_queued_song(playlist, NULL); playlist_update_queued_song(playlist, pc, NULL);
return PLAYLIST_RESULT_SUCCESS; return PLAYLIST_RESULT_SUCCESS;
} }
enum playlist_result enum playlist_result
playlist_seek_song_id(struct playlist *playlist, unsigned id, float seek_time) playlist_seek_song_id(struct playlist *playlist, struct player_control *pc,
unsigned id, float seek_time)
{ {
int song = queue_id_to_position(&playlist->queue, id); int song = queue_id_to_position(&playlist->queue, id);
if (song < 0) if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG; return PLAYLIST_RESULT_NO_SUCH_SONG;
return playlist_seek_song(playlist, song, seek_time); return playlist_seek_song(playlist, pc, song, seek_time);
} }

View File

@ -43,16 +43,17 @@ static void playlist_increment_version(struct playlist *playlist)
idle_add(IDLE_PLAYLIST); idle_add(IDLE_PLAYLIST);
} }
void playlist_clear(struct playlist *playlist) void
playlist_clear(struct playlist *playlist, struct player_control *pc)
{ {
playlist_stop(playlist); playlist_stop(playlist, pc);
/* make sure there are no references to allocated songs /* make sure there are no references to allocated songs
anymore */ anymore */
for (unsigned i = 0; i < queue_length(&playlist->queue); i++) { for (unsigned i = 0; i < queue_length(&playlist->queue); i++) {
const struct song *song = queue_get(&playlist->queue, i); const struct song *song = queue_get(&playlist->queue, i);
if (!song_in_database(song)) if (!song_in_database(song))
pc_song_deleted(song); pc_song_deleted(pc, song);
} }
queue_clear(&playlist->queue); queue_clear(&playlist->queue);
@ -64,8 +65,8 @@ void playlist_clear(struct playlist *playlist)
#ifndef WIN32 #ifndef WIN32
enum playlist_result enum playlist_result
playlist_append_file(struct playlist *playlist, const char *path, int uid, playlist_append_file(struct playlist *playlist, struct player_control *pc,
unsigned *added_id) const char *path, int uid, unsigned *added_id)
{ {
int ret; int ret;
struct stat st; struct stat st;
@ -87,12 +88,12 @@ playlist_append_file(struct playlist *playlist, const char *path, int uid,
if (song == NULL) if (song == NULL)
return PLAYLIST_RESULT_NO_SUCH_SONG; return PLAYLIST_RESULT_NO_SUCH_SONG;
return playlist_append_song(playlist, song, added_id); return playlist_append_song(playlist, pc, song, added_id);
} }
#endif #endif
enum playlist_result enum playlist_result
playlist_append_song(struct playlist *playlist, playlist_append_song(struct playlist *playlist, struct player_control *pc,
struct song *song, unsigned *added_id) struct song *song, unsigned *added_id)
{ {
const struct song *queued; const struct song *queued;
@ -121,7 +122,7 @@ playlist_append_song(struct playlist *playlist,
playlist_increment_version(playlist); playlist_increment_version(playlist);
playlist_update_queued_song(playlist, queued); playlist_update_queued_song(playlist, pc, queued);
if (added_id) if (added_id)
*added_id = id; *added_id = id;
@ -145,8 +146,8 @@ song_by_uri(const char *uri)
} }
enum playlist_result enum playlist_result
playlist_append_uri(struct playlist *playlist, const char *uri, playlist_append_uri(struct playlist *playlist, struct player_control *pc,
unsigned *added_id) const char *uri, unsigned *added_id)
{ {
struct song *song; struct song *song;
@ -156,11 +157,12 @@ playlist_append_uri(struct playlist *playlist, const char *uri,
if (song == NULL) if (song == NULL)
return PLAYLIST_RESULT_NO_SUCH_SONG; return PLAYLIST_RESULT_NO_SUCH_SONG;
return playlist_append_song(playlist, song, added_id); return playlist_append_song(playlist, pc, song, added_id);
} }
enum playlist_result enum playlist_result
playlist_swap_songs(struct playlist *playlist, unsigned song1, unsigned song2) playlist_swap_songs(struct playlist *playlist, struct player_control *pc,
unsigned song1, unsigned song2)
{ {
const struct song *queued; const struct song *queued;
@ -192,13 +194,14 @@ playlist_swap_songs(struct playlist *playlist, unsigned song1, unsigned song2)
playlist_increment_version(playlist); playlist_increment_version(playlist);
playlist_update_queued_song(playlist, queued); playlist_update_queued_song(playlist, pc, queued);
return PLAYLIST_RESULT_SUCCESS; return PLAYLIST_RESULT_SUCCESS;
} }
enum playlist_result enum playlist_result
playlist_swap_songs_id(struct playlist *playlist, unsigned id1, unsigned id2) playlist_swap_songs_id(struct playlist *playlist, struct player_control *pc,
unsigned id1, unsigned id2)
{ {
int song1 = queue_id_to_position(&playlist->queue, id1); int song1 = queue_id_to_position(&playlist->queue, id1);
int song2 = queue_id_to_position(&playlist->queue, id2); int song2 = queue_id_to_position(&playlist->queue, id2);
@ -206,12 +209,12 @@ playlist_swap_songs_id(struct playlist *playlist, unsigned id1, unsigned id2)
if (song1 < 0 || song2 < 0) if (song1 < 0 || song2 < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG; return PLAYLIST_RESULT_NO_SUCH_SONG;
return playlist_swap_songs(playlist, song1, song2); return playlist_swap_songs(playlist, pc, song1, song2);
} }
static void static void
playlist_delete_internal(struct playlist *playlist, unsigned song, playlist_delete_internal(struct playlist *playlist, struct player_control *pc,
const struct song **queued_p) unsigned song, const struct song **queued_p)
{ {
unsigned songOrder; unsigned songOrder;
@ -220,11 +223,11 @@ playlist_delete_internal(struct playlist *playlist, unsigned song,
songOrder = queue_position_to_order(&playlist->queue, song); songOrder = queue_position_to_order(&playlist->queue, song);
if (playlist->playing && playlist->current == (int)songOrder) { if (playlist->playing && playlist->current == (int)songOrder) {
bool paused = pc_get_state() == PLAYER_STATE_PAUSE; bool paused = pc_get_state(pc) == PLAYER_STATE_PAUSE;
/* the current song is going to be deleted: stop the player */ /* the current song is going to be deleted: stop the player */
pc_stop(); pc_stop(pc);
playlist->playing = false; playlist->playing = false;
/* see which song is going to be played instead */ /* see which song is going to be played instead */
@ -236,11 +239,11 @@ playlist_delete_internal(struct playlist *playlist, unsigned song,
if (playlist->current >= 0 && !paused) if (playlist->current >= 0 && !paused)
/* play the song after the deleted one */ /* play the song after the deleted one */
playlist_play_order(playlist, playlist->current); playlist_play_order(playlist, pc, playlist->current);
else else
/* no songs left to play, stop playback /* no songs left to play, stop playback
completely */ completely */
playlist_stop(playlist); playlist_stop(playlist, pc);
*queued_p = NULL; *queued_p = NULL;
} else if (playlist->current == (int)songOrder) } else if (playlist->current == (int)songOrder)
@ -251,7 +254,7 @@ playlist_delete_internal(struct playlist *playlist, unsigned song,
/* now do it: remove the song */ /* now do it: remove the song */
if (!song_in_database(queue_get(&playlist->queue, song))) if (!song_in_database(queue_get(&playlist->queue, song)))
pc_song_deleted(queue_get(&playlist->queue, song)); pc_song_deleted(pc, queue_get(&playlist->queue, song));
queue_delete(&playlist->queue, song); queue_delete(&playlist->queue, song);
@ -263,7 +266,8 @@ playlist_delete_internal(struct playlist *playlist, unsigned song,
} }
enum playlist_result enum playlist_result
playlist_delete(struct playlist *playlist, unsigned song) playlist_delete(struct playlist *playlist, struct player_control *pc,
unsigned song)
{ {
const struct song *queued; const struct song *queued;
@ -272,16 +276,17 @@ playlist_delete(struct playlist *playlist, unsigned song)
queued = playlist_get_queued_song(playlist); queued = playlist_get_queued_song(playlist);
playlist_delete_internal(playlist, song, &queued); playlist_delete_internal(playlist, pc, song, &queued);
playlist_increment_version(playlist); playlist_increment_version(playlist);
playlist_update_queued_song(playlist, queued); playlist_update_queued_song(playlist, pc, queued);
return PLAYLIST_RESULT_SUCCESS; return PLAYLIST_RESULT_SUCCESS;
} }
enum playlist_result enum playlist_result
playlist_delete_range(struct playlist *playlist, unsigned start, unsigned end) playlist_delete_range(struct playlist *playlist, struct player_control *pc,
unsigned start, unsigned end)
{ {
const struct song *queued; const struct song *queued;
@ -297,37 +302,39 @@ playlist_delete_range(struct playlist *playlist, unsigned start, unsigned end)
queued = playlist_get_queued_song(playlist); queued = playlist_get_queued_song(playlist);
do { do {
playlist_delete_internal(playlist, --end, &queued); playlist_delete_internal(playlist, pc, --end, &queued);
} while (end != start); } while (end != start);
playlist_increment_version(playlist); playlist_increment_version(playlist);
playlist_update_queued_song(playlist, queued); playlist_update_queued_song(playlist, pc, queued);
return PLAYLIST_RESULT_SUCCESS; return PLAYLIST_RESULT_SUCCESS;
} }
enum playlist_result enum playlist_result
playlist_delete_id(struct playlist *playlist, unsigned id) playlist_delete_id(struct playlist *playlist, struct player_control *pc,
unsigned id)
{ {
int song = queue_id_to_position(&playlist->queue, id); int song = queue_id_to_position(&playlist->queue, id);
if (song < 0) if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG; return PLAYLIST_RESULT_NO_SUCH_SONG;
return playlist_delete(playlist, song); return playlist_delete(playlist, pc, song);
} }
void void
playlist_delete_song(struct playlist *playlist, const struct song *song) playlist_delete_song(struct playlist *playlist, struct player_control *pc,
const struct song *song)
{ {
for (int i = queue_length(&playlist->queue) - 1; i >= 0; --i) for (int i = queue_length(&playlist->queue) - 1; i >= 0; --i)
if (song == queue_get(&playlist->queue, i)) if (song == queue_get(&playlist->queue, i))
playlist_delete(playlist, i); playlist_delete(playlist, pc, i);
pc_song_deleted(song); pc_song_deleted(pc, song);
} }
enum playlist_result enum playlist_result
playlist_move_range(struct playlist *playlist, playlist_move_range(struct playlist *playlist, struct player_control *pc,
unsigned start, unsigned end, int to) unsigned start, unsigned end, int to)
{ {
const struct song *queued; const struct song *queued;
@ -382,23 +389,25 @@ playlist_move_range(struct playlist *playlist,
playlist_increment_version(playlist); playlist_increment_version(playlist);
playlist_update_queued_song(playlist, queued); playlist_update_queued_song(playlist, pc, queued);
return PLAYLIST_RESULT_SUCCESS; return PLAYLIST_RESULT_SUCCESS;
} }
enum playlist_result enum playlist_result
playlist_move_id(struct playlist *playlist, unsigned id1, int to) playlist_move_id(struct playlist *playlist, struct player_control *pc,
unsigned id1, int to)
{ {
int song = queue_id_to_position(&playlist->queue, id1); int song = queue_id_to_position(&playlist->queue, id1);
if (song < 0) if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG; return PLAYLIST_RESULT_NO_SUCH_SONG;
return playlist_move_range(playlist, song, song+1, to); return playlist_move_range(playlist, pc, song, song+1, to);
} }
void void
playlist_shuffle(struct playlist *playlist, unsigned start, unsigned end) playlist_shuffle(struct playlist *playlist, struct player_control *pc,
unsigned start, unsigned end)
{ {
const struct song *queued; const struct song *queued;
@ -440,5 +449,5 @@ playlist_shuffle(struct playlist *playlist, unsigned start, unsigned end)
playlist_increment_version(playlist); playlist_increment_version(playlist);
playlist_update_queued_song(playlist, queued); playlist_update_queued_song(playlist, pc, queued);
} }

View File

@ -26,6 +26,7 @@
#include "playlist.h" #include "playlist.h"
#include "playlist_state.h" #include "playlist_state.h"
#include "event_pipe.h" #include "event_pipe.h"
#include "main.h"
struct playlist g_playlist; struct playlist g_playlist;
@ -38,7 +39,7 @@ playlist_tag_event(void)
static void static void
playlist_event(void) playlist_event(void)
{ {
playlist_sync(&g_playlist); playlist_sync(&g_playlist, global_player_control);
} }
void void

View File

@ -27,6 +27,8 @@
#include "playlist.h" #include "playlist.h"
struct player_control;
/** /**
* Returns the song object which is currently queued. Returns none if * Returns the song object which is currently queued. Returns none if
* there is none (yet?) or if MPD isn't playing. * there is none (yet?) or if MPD isn't playing.
@ -44,9 +46,11 @@ playlist_get_queued_song(struct playlist *playlist);
*/ */
void void
playlist_update_queued_song(struct playlist *playlist, playlist_update_queued_song(struct playlist *playlist,
struct player_control *pc,
const struct song *prev); const struct song *prev);
void void
playlist_play_order(struct playlist *playlist, int orderNum); playlist_play_order(struct playlist *playlist, struct player_control *pc,
int orderNum);
#endif #endif

View File

@ -27,7 +27,8 @@
enum playlist_result enum playlist_result
playlist_load_into_queue(const char *uri, struct playlist_provider *source, playlist_load_into_queue(const char *uri, struct playlist_provider *source,
struct playlist *dest, bool secure) struct playlist *dest, struct player_control *pc,
bool secure)
{ {
enum playlist_result result; enum playlist_result result;
struct song *song; struct song *song;
@ -38,7 +39,7 @@ playlist_load_into_queue(const char *uri, struct playlist_provider *source,
if (song == NULL) if (song == NULL)
continue; continue;
result = playlist_append_song(dest, song, NULL); result = playlist_append_song(dest, pc, song, NULL);
if (result != PLAYLIST_RESULT_SUCCESS) { if (result != PLAYLIST_RESULT_SUCCESS) {
if (!song_in_database(song)) if (!song_in_database(song))
song_free(song); song_free(song);
@ -53,7 +54,9 @@ playlist_load_into_queue(const char *uri, struct playlist_provider *source,
} }
enum playlist_result enum playlist_result
playlist_open_into_queue(const char *uri, struct playlist *dest, bool secure) playlist_open_into_queue(const char *uri,
struct playlist *dest, struct player_control *pc,
bool secure)
{ {
struct input_stream *is; struct input_stream *is;
struct playlist_provider *playlist = playlist_open_any(uri, &is); struct playlist_provider *playlist = playlist_open_any(uri, &is);
@ -61,7 +64,7 @@ playlist_open_into_queue(const char *uri, struct playlist *dest, bool secure)
return PLAYLIST_RESULT_NO_SUCH_LIST; return PLAYLIST_RESULT_NO_SUCH_LIST;
enum playlist_result result = enum playlist_result result =
playlist_load_into_queue(uri, playlist, dest, secure); playlist_load_into_queue(uri, playlist, dest, pc, secure);
playlist_plugin_close(playlist); playlist_plugin_close(playlist);
if (is != NULL) if (is != NULL)

View File

@ -40,14 +40,17 @@ struct playlist;
*/ */
enum playlist_result enum playlist_result
playlist_load_into_queue(const char *uri, struct playlist_provider *source, playlist_load_into_queue(const char *uri, struct playlist_provider *source,
struct playlist *dest, bool secure); struct playlist *dest, struct player_control *pc,
bool secure);
/** /**
* Opens a playlist with a playlist plugin and append to the specified * Opens a playlist with a playlist plugin and append to the specified
* play queue. * play queue.
*/ */
enum playlist_result enum playlist_result
playlist_open_into_queue(const char *uri, struct playlist *dest, bool secure); playlist_open_into_queue(const char *uri,
struct playlist *dest, struct player_control *pc,
bool secure);
#endif #endif

View File

@ -109,7 +109,8 @@ spl_save_playlist(const char *name_utf8, const struct playlist *playlist)
} }
enum playlist_result enum playlist_result
playlist_load_spl(struct playlist *playlist, const char *name_utf8) playlist_load_spl(struct playlist *playlist, struct player_control *pc,
const char *name_utf8)
{ {
GPtrArray *list; GPtrArray *list;
@ -119,7 +120,7 @@ playlist_load_spl(struct playlist *playlist, const char *name_utf8)
for (unsigned i = 0; i < list->len; ++i) { for (unsigned i = 0; i < list->len; ++i) {
const char *temp = g_ptr_array_index(list, i); const char *temp = g_ptr_array_index(list, i);
if ((playlist_append_uri(playlist, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) { if ((playlist_append_uri(playlist, pc, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
/* for windows compatibility, convert slashes */ /* for windows compatibility, convert slashes */
char *temp2 = g_strdup(temp); char *temp2 = g_strdup(temp);
char *p = temp2; char *p = temp2;
@ -128,7 +129,7 @@ playlist_load_spl(struct playlist *playlist, const char *name_utf8)
*p = '/'; *p = '/';
p++; p++;
} }
if ((playlist_append_uri(playlist, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) { if ((playlist_append_uri(playlist, pc, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
g_warning("can't add file \"%s\"", temp2); g_warning("can't add file \"%s\"", temp2);
} }
g_free(temp2); g_free(temp2);

View File

@ -49,6 +49,7 @@ spl_save_playlist(const char *name_utf8, const struct playlist *playlist);
* playlist. * playlist.
*/ */
enum playlist_result enum playlist_result
playlist_load_spl(struct playlist *playlist, const char *name_utf8); playlist_load_spl(struct playlist *playlist, struct player_control *pc,
const char *name_utf8);
#endif #endif

View File

@ -53,11 +53,12 @@
#define PLAYLIST_BUFFER_SIZE 2*MPD_PATH_MAX #define PLAYLIST_BUFFER_SIZE 2*MPD_PATH_MAX
void void
playlist_state_save(FILE *fp, const struct playlist *playlist) playlist_state_save(FILE *fp, const struct playlist *playlist,
struct player_control *pc)
{ {
struct player_status player_status; struct player_status player_status;
pc_get_status(&player_status); pc_get_status(pc, &player_status);
fputs(PLAYLIST_STATE_FILE_STATE, fp); fputs(PLAYLIST_STATE_FILE_STATE, fp);
@ -89,10 +90,11 @@ playlist_state_save(FILE *fp, const struct playlist *playlist)
fprintf(fp, PLAYLIST_STATE_FILE_CONSUME "%i\n", fprintf(fp, PLAYLIST_STATE_FILE_CONSUME "%i\n",
playlist->queue.consume); playlist->queue.consume);
fprintf(fp, PLAYLIST_STATE_FILE_CROSSFADE "%i\n", fprintf(fp, PLAYLIST_STATE_FILE_CROSSFADE "%i\n",
(int)(pc_get_cross_fade())); (int)(pc_get_cross_fade(pc)));
fprintf(fp, PLAYLIST_STATE_FILE_MIXRAMPDB "%f\n", pc_get_mixramp_db()); fprintf(fp, PLAYLIST_STATE_FILE_MIXRAMPDB "%f\n",
pc_get_mixramp_db(pc));
fprintf(fp, PLAYLIST_STATE_FILE_MIXRAMPDELAY "%f\n", fprintf(fp, PLAYLIST_STATE_FILE_MIXRAMPDELAY "%f\n",
pc_get_mixramp_delay()); pc_get_mixramp_delay(pc));
fputs(PLAYLIST_STATE_FILE_PLAYLIST_BEGIN "\n", fp); fputs(PLAYLIST_STATE_FILE_PLAYLIST_BEGIN "\n", fp);
queue_save(fp, &playlist->queue); queue_save(fp, &playlist->queue);
fputs(PLAYLIST_STATE_FILE_PLAYLIST_END "\n", fp); fputs(PLAYLIST_STATE_FILE_PLAYLIST_END "\n", fp);
@ -123,7 +125,7 @@ playlist_state_load(FILE *fp, GString *buffer, struct playlist *playlist)
bool bool
playlist_state_restore(const char *line, FILE *fp, GString *buffer, playlist_state_restore(const char *line, FILE *fp, GString *buffer,
struct playlist *playlist) struct playlist *playlist, struct player_control *pc)
{ {
int current = -1; int current = -1;
int seek_time = 0; int seek_time = 0;
@ -148,16 +150,16 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
if (strcmp if (strcmp
(&(line[strlen(PLAYLIST_STATE_FILE_REPEAT)]), (&(line[strlen(PLAYLIST_STATE_FILE_REPEAT)]),
"1") == 0) { "1") == 0) {
playlist_set_repeat(playlist, true); playlist_set_repeat(playlist, pc, true);
} else } else
playlist_set_repeat(playlist, false); playlist_set_repeat(playlist, pc, false);
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_SINGLE)) { } else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_SINGLE)) {
if (strcmp if (strcmp
(&(line[strlen(PLAYLIST_STATE_FILE_SINGLE)]), (&(line[strlen(PLAYLIST_STATE_FILE_SINGLE)]),
"1") == 0) { "1") == 0) {
playlist_set_single(playlist, true); playlist_set_single(playlist, pc, true);
} else } else
playlist_set_single(playlist, false); playlist_set_single(playlist, pc, false);
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_CONSUME)) { } else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_CONSUME)) {
if (strcmp if (strcmp
(&(line[strlen(PLAYLIST_STATE_FILE_CONSUME)]), (&(line[strlen(PLAYLIST_STATE_FILE_CONSUME)]),
@ -166,11 +168,14 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
} else } else
playlist_set_consume(playlist, false); playlist_set_consume(playlist, false);
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_CROSSFADE)) { } else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_CROSSFADE)) {
pc_set_cross_fade(atoi(line + strlen(PLAYLIST_STATE_FILE_CROSSFADE))); pc_set_cross_fade(pc,
atoi(line + strlen(PLAYLIST_STATE_FILE_CROSSFADE)));
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_MIXRAMPDB)) { } else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_MIXRAMPDB)) {
pc_set_mixramp_db(atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDB))); pc_set_mixramp_db(pc,
atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDB)));
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_MIXRAMPDELAY)) { } else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_MIXRAMPDELAY)) {
pc_set_mixramp_delay(atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDELAY))); pc_set_mixramp_delay(pc,
atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDELAY)));
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_RANDOM)) { } else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_RANDOM)) {
random_mode = random_mode =
strcmp(line + strlen(PLAYLIST_STATE_FILE_RANDOM), strcmp(line + strlen(PLAYLIST_STATE_FILE_RANDOM),
@ -185,7 +190,7 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
} }
} }
playlist_set_random(playlist, random_mode); playlist_set_random(playlist, pc, random_mode);
if (!queue_is_empty(&playlist->queue)) { if (!queue_is_empty(&playlist->queue)) {
if (!queue_valid_position(&playlist->queue, current)) if (!queue_valid_position(&playlist->queue, current))
@ -195,28 +200,29 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
called here, after the audio output states were called here, after the audio output states were
restored, before playback begins */ restored, before playback begins */
if (state != PLAYER_STATE_STOP) if (state != PLAYER_STATE_STOP)
pc_update_audio(); pc_update_audio(pc);
if (state == PLAYER_STATE_STOP /* && config_option */) if (state == PLAYER_STATE_STOP /* && config_option */)
playlist->current = current; playlist->current = current;
else if (seek_time == 0) else if (seek_time == 0)
playlist_play(playlist, current); playlist_play(playlist, pc, current);
else else
playlist_seek_song(playlist, current, seek_time); playlist_seek_song(playlist, pc, current, seek_time);
if (state == PLAYER_STATE_PAUSE) if (state == PLAYER_STATE_PAUSE)
pc_pause(); pc_pause(pc);
} }
return true; return true;
} }
unsigned unsigned
playlist_state_get_hash(const struct playlist *playlist) playlist_state_get_hash(const struct playlist *playlist,
struct player_control *pc)
{ {
struct player_status player_status; struct player_status player_status;
pc_get_status(&player_status); pc_get_status(pc, &player_status);
return playlist->queue.version ^ return playlist->queue.version ^
(player_status.state != PLAYER_STATE_STOP (player_status.state != PLAYER_STATE_STOP
@ -226,7 +232,7 @@ playlist_state_get_hash(const struct playlist *playlist)
? (queue_order_to_position(&playlist->queue, ? (queue_order_to_position(&playlist->queue,
playlist->current) << 16) playlist->current) << 16)
: 0) ^ : 0) ^
((int)pc_get_cross_fade() << 20) ^ ((int)pc_get_cross_fade(pc) << 20) ^
(player_status.state << 24) ^ (player_status.state << 24) ^
(playlist->queue.random << 27) ^ (playlist->queue.random << 27) ^
(playlist->queue.repeat << 28) ^ (playlist->queue.repeat << 28) ^

View File

@ -30,13 +30,15 @@
#include <stdio.h> #include <stdio.h>
struct playlist; struct playlist;
struct player_control;
void void
playlist_state_save(FILE *fp, const struct playlist *playlist); playlist_state_save(FILE *fp, const struct playlist *playlist,
struct player_control *pc);
bool bool
playlist_state_restore(const char *line, FILE *fp, GString *buffer, playlist_state_restore(const char *line, FILE *fp, GString *buffer,
struct playlist *playlist); struct playlist *playlist, struct player_control *pc);
/** /**
* Generates a hash number for the current state of the playlist and * Generates a hash number for the current state of the playlist and
@ -45,6 +47,7 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
* be saved. * be saved.
*/ */
unsigned unsigned
playlist_state_get_hash(const struct playlist *playlist); playlist_state_get_hash(const struct playlist *playlist,
struct player_control *pc);
#endif #endif

View File

@ -47,7 +47,7 @@ static unsigned prev_volume_version, prev_output_version,
prev_playlist_version; prev_playlist_version;
static void static void
state_file_write(void) state_file_write(struct player_control *pc)
{ {
FILE *fp; FILE *fp;
@ -64,17 +64,17 @@ state_file_write(void)
save_sw_volume_state(fp); save_sw_volume_state(fp);
audio_output_state_save(fp); audio_output_state_save(fp);
playlist_state_save(fp, &g_playlist); playlist_state_save(fp, &g_playlist, pc);
fclose(fp); fclose(fp);
prev_volume_version = sw_volume_state_get_hash(); prev_volume_version = sw_volume_state_get_hash();
prev_output_version = audio_output_state_get_version(); prev_output_version = audio_output_state_get_version();
prev_playlist_version = playlist_state_get_hash(&g_playlist); prev_playlist_version = playlist_state_get_hash(&g_playlist, pc);
} }
static void static void
state_file_read(void) state_file_read(struct player_control *pc)
{ {
FILE *fp; FILE *fp;
bool success; bool success;
@ -95,7 +95,8 @@ state_file_read(void)
while ((line = read_text_line(fp, buffer)) != NULL) { while ((line = read_text_line(fp, buffer)) != NULL) {
success = read_sw_volume_state(line) || success = read_sw_volume_state(line) ||
audio_output_state_read(line) || audio_output_state_read(line) ||
playlist_state_restore(line, fp, buffer, &g_playlist); playlist_state_restore(line, fp, buffer,
&g_playlist, pc);
if (!success) if (!success)
g_warning("Unrecognized line in state file: %s", line); g_warning("Unrecognized line in state file: %s", line);
} }
@ -104,7 +105,7 @@ state_file_read(void)
prev_volume_version = sw_volume_state_get_hash(); prev_volume_version = sw_volume_state_get_hash();
prev_output_version = audio_output_state_get_version(); prev_output_version = audio_output_state_get_version();
prev_playlist_version = playlist_state_get_hash(&g_playlist); prev_playlist_version = playlist_state_get_hash(&g_playlist, pc);
g_string_free(buffer, true); g_string_free(buffer, true);
@ -115,21 +116,23 @@ state_file_read(void)
* saves the state file. * saves the state file.
*/ */
static gboolean static gboolean
timer_save_state_file(G_GNUC_UNUSED gpointer data) timer_save_state_file(gpointer data)
{ {
struct player_control *pc = data;
if (prev_volume_version == sw_volume_state_get_hash() && if (prev_volume_version == sw_volume_state_get_hash() &&
prev_output_version == audio_output_state_get_version() && prev_output_version == audio_output_state_get_version() &&
prev_playlist_version == playlist_state_get_hash(&g_playlist)) prev_playlist_version == playlist_state_get_hash(&g_playlist, pc))
/* nothing has changed - don't save the state file, /* nothing has changed - don't save the state file,
don't spin up the hard disk */ don't spin up the hard disk */
return true; return true;
state_file_write(); state_file_write(pc);
return true; return true;
} }
void void
state_file_init(const char *path) state_file_init(const char *path, struct player_control *pc)
{ {
assert(state_file_path == NULL); assert(state_file_path == NULL);
@ -137,15 +140,15 @@ state_file_init(const char *path)
return; return;
state_file_path = g_strdup(path); state_file_path = g_strdup(path);
state_file_read(); state_file_read(pc);
save_state_source_id = g_timeout_add_seconds(5 * 60, save_state_source_id = g_timeout_add_seconds(5 * 60,
timer_save_state_file, timer_save_state_file,
NULL); pc);
} }
void void
state_file_finish(void) state_file_finish(struct player_control *pc)
{ {
if (state_file_path == NULL) if (state_file_path == NULL)
/* no state file configured, no cleanup required */ /* no state file configured, no cleanup required */
@ -154,7 +157,7 @@ state_file_finish(void)
if (save_state_source_id != 0) if (save_state_source_id != 0)
g_source_remove(save_state_source_id); g_source_remove(save_state_source_id);
state_file_write(); state_file_write(pc);
g_free(state_file_path); g_free(state_file_path);
} }

View File

@ -20,11 +20,13 @@
#ifndef MPD_STATE_FILE_H #ifndef MPD_STATE_FILE_H
#define MPD_STATE_FILE_H #define MPD_STATE_FILE_H
void struct player_control;
state_file_init(const char *path);
void void
state_file_finish(void); state_file_init(const char *path, struct player_control *pc);
void
state_file_finish(struct player_control *pc);
void write_state_file(void); void write_state_file(void);

View File

@ -25,6 +25,7 @@
#include "client.h" #include "client.h"
#include "player_control.h" #include "player_control.h"
#include "strset.h" #include "strset.h"
#include "client_internal.h"
struct stats stats; struct stats stats;
@ -114,7 +115,7 @@ int stats_print(struct client *client)
stats.album_count, stats.album_count,
stats.song_count, stats.song_count,
(long)g_timer_elapsed(stats.timer, NULL), (long)g_timer_elapsed(stats.timer, NULL),
(long)(pc_get_total_play_time() + 0.5), (long)(pc_get_total_play_time(client->player_control) + 0.5),
stats.song_duration, stats.song_duration,
db_get_mtime()); db_get_mtime());
return 0; return 0;

View File

@ -23,6 +23,7 @@
#include "event_pipe.h" #include "event_pipe.h"
#include "song.h" #include "song.h"
#include "playlist.h" #include "playlist.h"
#include "main.h"
#ifdef ENABLE_SQLITE #ifdef ENABLE_SQLITE
#include "sticker.h" #include "sticker.h"
@ -58,7 +59,7 @@ song_remove_event(void)
sticker_song_delete(removed_song); sticker_song_delete(removed_song);
#endif #endif
playlist_delete_song(&g_playlist, removed_song); playlist_delete_song(&g_playlist, global_player_control, removed_song);
removed_song = NULL; removed_song = NULL;
notify_signal(&remove_notify); notify_signal(&remove_notify);

View File

@ -28,6 +28,7 @@
#include "event_pipe.h" #include "event_pipe.h"
#include "idle.h" #include "idle.h"
#include "playlist.h" #include "playlist.h"
#include "player_control.h"
#include "stdbin.h" #include "stdbin.h"
#include <glib.h> #include <glib.h>
@ -104,7 +105,9 @@ load_audio_output(struct audio_output *ao, const char *name)
return false; return false;
} }
success = audio_output_init(ao, param, &error); static struct player_control dummy_player_control;
success = audio_output_init(ao, param, &dummy_player_control, &error);
if (!success) { if (!success) {
g_printerr("%s\n", error->message); g_printerr("%s\n", error->message);
g_error_free(error); g_error_free(error);