playlist: moved code to queue.c
Attempt to untie the playlist.c knot: moved the playlist storage code to queue.c, struct queue.
This commit is contained in:
parent
d5dcd0ed66
commit
f78cddb407
@ -92,6 +92,7 @@ mpd_headers = \
|
|||||||
player_control.h \
|
player_control.h \
|
||||||
playlist.h \
|
playlist.h \
|
||||||
playlist_save.h \
|
playlist_save.h \
|
||||||
|
queue.h \
|
||||||
replay_gain.h \
|
replay_gain.h \
|
||||||
sig_handlers.h \
|
sig_handlers.h \
|
||||||
song.h \
|
song.h \
|
||||||
@ -175,6 +176,7 @@ mpd_SOURCES = \
|
|||||||
player_control.c \
|
player_control.c \
|
||||||
playlist.c \
|
playlist.c \
|
||||||
playlist_save.c \
|
playlist_save.c \
|
||||||
|
queue.c \
|
||||||
replay_gain.c \
|
replay_gain.c \
|
||||||
sig_handlers.c \
|
sig_handlers.c \
|
||||||
song.c \
|
song.c \
|
||||||
|
540
src/playlist.c
540
src/playlist.c
@ -78,30 +78,20 @@ static int playlist_noGoToNext;
|
|||||||
|
|
||||||
bool playlist_saveAbsolutePaths = DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS;
|
bool playlist_saveAbsolutePaths = DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS;
|
||||||
|
|
||||||
static void swapOrder(int a, int b);
|
|
||||||
static void playPlaylistOrderNumber(int orderNum);
|
static void playPlaylistOrderNumber(int orderNum);
|
||||||
static void randomizeOrder(int start, int end);
|
static void randomizeOrder(int start, int end);
|
||||||
|
|
||||||
static void incrPlaylistVersion(void)
|
static void incrPlaylistVersion(void)
|
||||||
{
|
{
|
||||||
static unsigned long max = ((uint32_t) 1 << 31) - 1;
|
queue_increment_version(&playlist.queue);
|
||||||
playlist.version++;
|
|
||||||
if (playlist.version >= max) {
|
|
||||||
for (unsigned i = 0; i < playlist.length; i++)
|
|
||||||
playlist.songMod[i] = 0;
|
|
||||||
|
|
||||||
playlist.version = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
idle_add(IDLE_PLAYLIST);
|
idle_add(IDLE_PLAYLIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
void playlistVersionChange(void)
|
void playlistVersionChange(void)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < playlist.length; i++)
|
queue_modify_all(&playlist.queue);
|
||||||
playlist.songMod[i] = playlist.version;
|
idle_add(IDLE_PLAYLIST);
|
||||||
|
|
||||||
incrPlaylistVersion();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void incrPlaylistCurrent(void)
|
static void incrPlaylistCurrent(void)
|
||||||
@ -109,98 +99,42 @@ static void incrPlaylistCurrent(void)
|
|||||||
if (playlist.current < 0)
|
if (playlist.current < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (playlist.current >= (int)playlist.length - 1) {
|
playlist.current = queue_next_order(&playlist.queue, playlist.current);
|
||||||
if (playlist.repeat)
|
|
||||||
playlist.current = 0;
|
|
||||||
else
|
|
||||||
playlist.current = -1;
|
|
||||||
} else
|
|
||||||
playlist.current++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
playlist_tag_event(void)
|
playlist_tag_event(void)
|
||||||
{
|
{
|
||||||
unsigned song;
|
|
||||||
|
|
||||||
if (playlist_state != PLAYLIST_STATE_PLAY ||
|
if (playlist_state != PLAYLIST_STATE_PLAY ||
|
||||||
playlist.current < 0)
|
playlist.current < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
assert((unsigned)playlist.current < playlist.length);
|
queue_modify(&playlist.queue, playlist.current);
|
||||||
|
idle_add(IDLE_PLAYLIST);
|
||||||
song = playlist.order[playlist.current];
|
|
||||||
playlist.songMod[song] = playlist.version;
|
|
||||||
incrPlaylistVersion();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void initPlaylist(void)
|
void initPlaylist(void)
|
||||||
{
|
{
|
||||||
g_rand = g_rand_new();
|
g_rand = g_rand_new();
|
||||||
|
|
||||||
playlist.length = 0;
|
|
||||||
playlist.repeat = false;
|
|
||||||
playlist.version = 1;
|
|
||||||
playlist.random = false;
|
|
||||||
playlist.queued = -1;
|
|
||||||
playlist.current = -1;
|
|
||||||
|
|
||||||
playlist_max_length = config_get_positive(CONF_MAX_PLAYLIST_LENGTH,
|
playlist_max_length = config_get_positive(CONF_MAX_PLAYLIST_LENGTH,
|
||||||
DEFAULT_PLAYLIST_MAX_LENGTH);
|
DEFAULT_PLAYLIST_MAX_LENGTH);
|
||||||
|
|
||||||
|
queue_init(&playlist.queue, playlist_max_length);
|
||||||
|
|
||||||
|
playlist.queued = -1;
|
||||||
|
playlist.current = -1;
|
||||||
|
|
||||||
playlist_saveAbsolutePaths =
|
playlist_saveAbsolutePaths =
|
||||||
config_get_bool(CONF_SAVE_ABSOLUTE_PATHS,
|
config_get_bool(CONF_SAVE_ABSOLUTE_PATHS,
|
||||||
DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS);
|
DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS);
|
||||||
|
|
||||||
playlist.songs = g_malloc(sizeof(playlist.songs[0]) *
|
|
||||||
playlist_max_length);
|
|
||||||
playlist.songMod = g_malloc(sizeof(playlist.songMod[0]) *
|
|
||||||
playlist_max_length);
|
|
||||||
playlist.order = g_malloc(sizeof(playlist.order[0]) *
|
|
||||||
playlist_max_length);
|
|
||||||
playlist.idToPosition = g_malloc(sizeof(playlist.idToPosition[0]) *
|
|
||||||
playlist_max_length *
|
|
||||||
PLAYLIST_HASH_MULT);
|
|
||||||
playlist.positionToId = g_malloc(sizeof(playlist.positionToId[0]) *
|
|
||||||
playlist_max_length);
|
|
||||||
|
|
||||||
memset(playlist.songs, 0, sizeof(char *) * playlist_max_length);
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < playlist_max_length * PLAYLIST_HASH_MULT;
|
|
||||||
i++) {
|
|
||||||
playlist.idToPosition[i] = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
event_pipe_register(PIPE_EVENT_TAG, playlist_tag_event);
|
event_pipe_register(PIPE_EVENT_TAG, playlist_tag_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned getNextId(void)
|
|
||||||
{
|
|
||||||
static unsigned cur = (unsigned)-1;
|
|
||||||
|
|
||||||
do {
|
|
||||||
cur++;
|
|
||||||
if (cur >= playlist_max_length * PLAYLIST_HASH_MULT) {
|
|
||||||
cur = 0;
|
|
||||||
}
|
|
||||||
} while (playlist.idToPosition[cur] != -1);
|
|
||||||
|
|
||||||
return cur;
|
|
||||||
}
|
|
||||||
|
|
||||||
void finishPlaylist(void)
|
void finishPlaylist(void)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < playlist.length; i++)
|
queue_finish(&playlist.queue);
|
||||||
if (!song_in_database(playlist.songs[i]))
|
|
||||||
song_free(playlist.songs[i]);
|
|
||||||
|
|
||||||
playlist.length = 0;
|
|
||||||
|
|
||||||
g_free(playlist.songs);
|
|
||||||
g_free(playlist.songMod);
|
|
||||||
g_free(playlist.order);
|
|
||||||
g_free(playlist.idToPosition);
|
|
||||||
g_free(playlist.positionToId);
|
|
||||||
|
|
||||||
g_rand_free(g_rand);
|
g_rand_free(g_rand);
|
||||||
g_rand = NULL;
|
g_rand = NULL;
|
||||||
@ -210,16 +144,14 @@ void clearPlaylist(void)
|
|||||||
{
|
{
|
||||||
stopPlaylist();
|
stopPlaylist();
|
||||||
|
|
||||||
for (unsigned i = 0; i < playlist.length; i++) {
|
for (unsigned i = 0; i < queue_length(&playlist.queue); i++) {
|
||||||
if (!song_in_database(playlist.songs[i])) {
|
const struct song *song = queue_get(&playlist.queue, i);
|
||||||
pc_song_deleted(playlist.songs[i]);
|
if (!song_in_database(song))
|
||||||
song_free(playlist.songs[i]);
|
pc_song_deleted(song);
|
||||||
}
|
|
||||||
|
|
||||||
playlist.idToPosition[playlist.positionToId[i]] = -1;
|
|
||||||
playlist.songs[i] = NULL;
|
|
||||||
}
|
}
|
||||||
playlist.length = 0;
|
|
||||||
|
queue_clear(&playlist.queue);
|
||||||
|
|
||||||
playlist.current = -1;
|
playlist.current = -1;
|
||||||
|
|
||||||
incrPlaylistVersion();
|
incrPlaylistVersion();
|
||||||
@ -227,8 +159,9 @@ void clearPlaylist(void)
|
|||||||
|
|
||||||
void showPlaylist(struct client *client)
|
void showPlaylist(struct client *client)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < playlist.length; i++) {
|
for (unsigned i = 0; i < queue_length(&playlist.queue); i++) {
|
||||||
char *uri = song_get_uri(playlist.songs[i]);
|
const struct song *song = queue_get(&playlist.queue, i);
|
||||||
|
char *uri = song_get_uri(song);
|
||||||
client_printf(client, "%i:%s\n", i, uri);
|
client_printf(client, "%i:%s\n", i, uri);
|
||||||
g_free(uri);
|
g_free(uri);
|
||||||
}
|
}
|
||||||
@ -236,8 +169,9 @@ void showPlaylist(struct client *client)
|
|||||||
|
|
||||||
static void playlist_save(FILE *fp)
|
static void playlist_save(FILE *fp)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < playlist.length; i++) {
|
for (unsigned i = 0; i < queue_length(&playlist.queue); i++) {
|
||||||
char *uri = song_get_uri(playlist.songs[i]);
|
const struct song *song = queue_get(&playlist.queue, i);
|
||||||
|
char *uri = song_get_uri(song);
|
||||||
fprintf(fp, "%i:%s\n", i, uri);
|
fprintf(fp, "%i:%s\n", i, uri);
|
||||||
g_free(uri);
|
g_free(uri);
|
||||||
}
|
}
|
||||||
@ -256,7 +190,8 @@ void savePlaylistState(FILE *fp)
|
|||||||
fprintf(fp, "%s\n", PLAYLIST_STATE_FILE_STATE_PLAY);
|
fprintf(fp, "%s\n", PLAYLIST_STATE_FILE_STATE_PLAY);
|
||||||
}
|
}
|
||||||
fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_CURRENT,
|
fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_CURRENT,
|
||||||
playlist.order[playlist.current]);
|
queue_order_to_position(&playlist.queue,
|
||||||
|
playlist.current));
|
||||||
fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_TIME,
|
fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_TIME,
|
||||||
getPlayerElapsedTime());
|
getPlayerElapsedTime());
|
||||||
break;
|
break;
|
||||||
@ -264,8 +199,10 @@ void savePlaylistState(FILE *fp)
|
|||||||
fprintf(fp, "%s\n", PLAYLIST_STATE_FILE_STATE_STOP);
|
fprintf(fp, "%s\n", PLAYLIST_STATE_FILE_STATE_STOP);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_RANDOM, playlist.random);
|
fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_RANDOM,
|
||||||
fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_REPEAT, playlist.repeat);
|
playlist.queue.random);
|
||||||
|
fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_REPEAT,
|
||||||
|
playlist.queue.repeat);
|
||||||
fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_CROSSFADE,
|
fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_CROSSFADE,
|
||||||
(int)(getPlayerCrossFade()));
|
(int)(getPlayerCrossFade()));
|
||||||
fprintf(fp, "%s\n", PLAYLIST_STATE_FILE_PLAYLIST_BEGIN);
|
fprintf(fp, "%s\n", PLAYLIST_STATE_FILE_PLAYLIST_BEGIN);
|
||||||
@ -302,13 +239,13 @@ static void loadPlaylistFromStateFile(FILE *fp, char *buffer,
|
|||||||
if (addToPlaylist(temp, NULL) == PLAYLIST_RESULT_SUCCESS
|
if (addToPlaylist(temp, NULL) == PLAYLIST_RESULT_SUCCESS
|
||||||
&& current == song) {
|
&& current == song) {
|
||||||
if (state != PLAYER_STATE_STOP) {
|
if (state != PLAYER_STATE_STOP) {
|
||||||
playPlaylist(playlist.length - 1, 0);
|
playPlaylist(queue_length(&playlist.queue) - 1, 0);
|
||||||
}
|
}
|
||||||
if (state == PLAYER_STATE_PAUSE) {
|
if (state == PLAYER_STATE_PAUSE) {
|
||||||
playerPause();
|
playerPause();
|
||||||
}
|
}
|
||||||
if (state != PLAYER_STATE_STOP) {
|
if (state != PLAYER_STATE_STOP) {
|
||||||
seekSongInPlaylist(playlist.length - 1,
|
seekSongInPlaylist(queue_length(&playlist.queue) - 1,
|
||||||
seek_time);
|
seek_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -384,19 +321,16 @@ void readPlaylistState(FILE *fp)
|
|||||||
|
|
||||||
static void printPlaylistSongInfo(struct client *client, unsigned song)
|
static void printPlaylistSongInfo(struct client *client, unsigned song)
|
||||||
{
|
{
|
||||||
song_print_info(client, playlist.songs[song]);
|
song_print_info(client, queue_get(&playlist.queue, song));
|
||||||
client_printf(client, "Pos: %u\nId: %u\n",
|
client_printf(client, "Pos: %u\nId: %u\n",
|
||||||
song, playlist.positionToId[song]);
|
song, queue_position_to_id(&playlist.queue, song));
|
||||||
}
|
}
|
||||||
|
|
||||||
int playlistChanges(struct client *client, uint32_t version)
|
int playlistChanges(struct client *client, uint32_t version)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < playlist.length; i++) {
|
for (unsigned i = 0; i < queue_length(&playlist.queue); i++) {
|
||||||
if (version > playlist.version ||
|
if (queue_song_newer(&playlist.queue, i, version))
|
||||||
playlist.songMod[i] >= version ||
|
|
||||||
playlist.songMod[i] == 0) {
|
|
||||||
printPlaylistSongInfo(client, i);
|
printPlaylistSongInfo(client, i);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -404,13 +338,10 @@ int playlistChanges(struct client *client, uint32_t version)
|
|||||||
|
|
||||||
int playlistChangesPosId(struct client *client, uint32_t version)
|
int playlistChangesPosId(struct client *client, uint32_t version)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < playlist.length; i++) {
|
for (unsigned i = 0; i < queue_length(&playlist.queue); i++) {
|
||||||
if (version > playlist.version ||
|
if (queue_song_newer(&playlist.queue, i, version))
|
||||||
playlist.songMod[i] >= version ||
|
|
||||||
playlist.songMod[i] == 0) {
|
|
||||||
client_printf(client, "cpos: %i\nId: %i\n",
|
client_printf(client, "cpos: %i\nId: %i\n",
|
||||||
i, playlist.positionToId[i]);
|
i, queue_position_to_id(&playlist.queue, i));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -419,8 +350,8 @@ int playlistChangesPosId(struct client *client, uint32_t version)
|
|||||||
enum playlist_result
|
enum playlist_result
|
||||||
playlistInfo(struct client *client, unsigned start, unsigned end)
|
playlistInfo(struct client *client, unsigned start, unsigned end)
|
||||||
{
|
{
|
||||||
if (end > playlist.length)
|
if (end > queue_length(&playlist.queue))
|
||||||
end = playlist.length;
|
end = queue_length(&playlist.queue);
|
||||||
|
|
||||||
if (start > end)
|
if (start > end)
|
||||||
return PLAYLIST_RESULT_BAD_RANGE;
|
return PLAYLIST_RESULT_BAD_RANGE;
|
||||||
@ -433,19 +364,13 @@ playlistInfo(struct client *client, unsigned start, unsigned end)
|
|||||||
|
|
||||||
static int song_id_to_position(unsigned id)
|
static int song_id_to_position(unsigned id)
|
||||||
{
|
{
|
||||||
if (id >= PLAYLIST_HASH_MULT*playlist_max_length)
|
return queue_id_to_position(&playlist.queue, id);
|
||||||
return -1;
|
|
||||||
|
|
||||||
assert(playlist.idToPosition[id] >= -1);
|
|
||||||
assert(playlist.idToPosition[id] < (int)playlist.length);
|
|
||||||
|
|
||||||
return playlist.idToPosition[id];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum playlist_result playlistId(struct client *client, int id)
|
enum playlist_result playlistId(struct client *client, int id)
|
||||||
{
|
{
|
||||||
int begin = 0;
|
int begin = 0;
|
||||||
unsigned end = playlist.length;
|
unsigned end = queue_length(&playlist.queue);
|
||||||
|
|
||||||
if (id >= 0) {
|
if (id >= 0) {
|
||||||
begin = song_id_to_position(id);
|
begin = song_id_to_position(id);
|
||||||
@ -463,51 +388,40 @@ enum playlist_result playlistId(struct client *client, int id)
|
|||||||
|
|
||||||
static void swapSongs(unsigned song1, unsigned song2)
|
static void swapSongs(unsigned song1, unsigned song2)
|
||||||
{
|
{
|
||||||
struct song *sTemp;
|
queue_swap(&playlist.queue, song1, song2);
|
||||||
unsigned iTemp;
|
|
||||||
|
|
||||||
sTemp = playlist.songs[song1];
|
|
||||||
playlist.songs[song1] = playlist.songs[song2];
|
|
||||||
playlist.songs[song2] = sTemp;
|
|
||||||
|
|
||||||
playlist.songMod[song1] = playlist.version;
|
|
||||||
playlist.songMod[song2] = playlist.version;
|
|
||||||
|
|
||||||
playlist.idToPosition[playlist.positionToId[song1]] = song2;
|
|
||||||
playlist.idToPosition[playlist.positionToId[song2]] = song1;
|
|
||||||
|
|
||||||
iTemp = playlist.positionToId[song1];
|
|
||||||
playlist.positionToId[song1] = playlist.positionToId[song2];
|
|
||||||
playlist.positionToId[song2] = iTemp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void queueNextSongInPlaylist(void)
|
static void queueNextSongInPlaylist(void)
|
||||||
{
|
{
|
||||||
if (playlist.current < (int)playlist.length - 1) {
|
if (playlist.current + 1 < (int)queue_length(&playlist.queue)) {
|
||||||
|
struct song *song;
|
||||||
char *uri;
|
char *uri;
|
||||||
|
|
||||||
playlist.queued = playlist.current + 1;
|
playlist.queued = playlist.current + 1;
|
||||||
|
|
||||||
uri = song_get_uri(playlist. songs[playlist.order[playlist.queued]]);
|
song = queue_get_order(&playlist.queue, playlist.queued);
|
||||||
|
uri = song_get_uri(song);
|
||||||
g_debug("playlist: queue song %i:\"%s\"",
|
g_debug("playlist: queue song %i:\"%s\"",
|
||||||
playlist.queued, uri);
|
playlist.queued, uri);
|
||||||
g_free(uri);
|
g_free(uri);
|
||||||
|
|
||||||
queueSong(playlist.songs[playlist.order[playlist.queued]]);
|
queueSong(song);
|
||||||
} else if (playlist.length && playlist.repeat) {
|
} else if (!queue_is_empty(&playlist.queue) && playlist.queue.repeat) {
|
||||||
|
struct song *song;
|
||||||
char *uri;
|
char *uri;
|
||||||
|
|
||||||
if (playlist.length > 1 && playlist.random) {
|
if (queue_length(&playlist.queue) > 1 && playlist.queue.random)
|
||||||
randomizeOrder(0, playlist.length - 1);
|
randomizeOrder(0, queue_length(&playlist.queue) - 1);
|
||||||
}
|
|
||||||
playlist.queued = 0;
|
playlist.queued = 0;
|
||||||
|
|
||||||
uri = song_get_uri(playlist. songs[playlist.order[playlist.queued]]);
|
song = queue_get_order(&playlist.queue, playlist.queued);
|
||||||
|
uri = song_get_uri(song);
|
||||||
g_debug("playlist: queue song %i:\"%s\"",
|
g_debug("playlist: queue song %i:\"%s\"",
|
||||||
playlist.queued, uri);
|
playlist.queued, uri);
|
||||||
g_free(uri);
|
g_free(uri);
|
||||||
|
|
||||||
queueSong(playlist.songs[playlist.order[playlist.queued]]);
|
queueSong(song);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,34 +505,27 @@ addSongToPlaylist(struct song *song, unsigned *added_id)
|
|||||||
{
|
{
|
||||||
unsigned id;
|
unsigned id;
|
||||||
|
|
||||||
if (playlist.length == playlist_max_length)
|
if (queue_is_full(&playlist.queue))
|
||||||
return PLAYLIST_RESULT_TOO_LARGE;
|
return PLAYLIST_RESULT_TOO_LARGE;
|
||||||
|
|
||||||
if (playlist_state == PLAYLIST_STATE_PLAY && playlist.queued >= 0 &&
|
if (playlist_state == PLAYLIST_STATE_PLAY && playlist.queued >= 0 &&
|
||||||
playlist.current == (int)playlist.length - 1)
|
playlist.current == (int)queue_length(&playlist.queue) - 1)
|
||||||
clearPlayerQueue();
|
clearPlayerQueue();
|
||||||
|
|
||||||
id = getNextId();
|
id = queue_append(&playlist.queue, song);
|
||||||
|
|
||||||
playlist.songs[playlist.length] = song;
|
if (playlist.queue.random) {
|
||||||
playlist.songMod[playlist.length] = playlist.version;
|
|
||||||
playlist.order[playlist.length] = playlist.length;
|
|
||||||
playlist.positionToId[playlist.length] = id;
|
|
||||||
playlist.idToPosition[playlist.positionToId[playlist.length]] =
|
|
||||||
playlist.length;
|
|
||||||
playlist.length++;
|
|
||||||
|
|
||||||
if (playlist.random) {
|
|
||||||
unsigned start;
|
unsigned start;
|
||||||
/*if(playlist_state==PLAYLIST_STATE_STOP) start = 0;
|
/*if(playlist_state==PLAYLIST_STATE_STOP) start = 0;
|
||||||
else */ if (playlist.queued >= 0)
|
else */ if (playlist.queued >= 0)
|
||||||
start = playlist.queued + 1;
|
start = playlist.queued + 1;
|
||||||
else
|
else
|
||||||
start = playlist.current + 1;
|
start = playlist.current + 1;
|
||||||
if (start < playlist.length) {
|
if (start < queue_length(&playlist.queue)) {
|
||||||
unsigned swap = g_rand_int_range(g_rand, start,
|
unsigned swap = g_rand_int_range(g_rand, start,
|
||||||
playlist.length);
|
queue_length(&playlist.queue));
|
||||||
swapOrder(playlist.length - 1, swap);
|
queue_swap_order(&playlist.queue,
|
||||||
|
queue_length(&playlist.queue) - 1, swap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -632,12 +539,15 @@ addSongToPlaylist(struct song *song, unsigned *added_id)
|
|||||||
|
|
||||||
enum playlist_result swapSongsInPlaylist(unsigned song1, unsigned song2)
|
enum playlist_result swapSongsInPlaylist(unsigned song1, unsigned song2)
|
||||||
{
|
{
|
||||||
if (song1 >= playlist.length || song2 >= playlist.length)
|
if (!queue_valid_position(&playlist.queue, song1) ||
|
||||||
|
!queue_valid_position(&playlist.queue, song2))
|
||||||
return PLAYLIST_RESULT_BAD_RANGE;
|
return PLAYLIST_RESULT_BAD_RANGE;
|
||||||
|
|
||||||
if (playlist_state == PLAYLIST_STATE_PLAY && playlist.queued >= 0) {
|
if (playlist_state == PLAYLIST_STATE_PLAY && playlist.queued >= 0) {
|
||||||
unsigned queuedSong = playlist.order[playlist.queued];
|
unsigned queuedSong = queue_order_to_position(&playlist.queue,
|
||||||
unsigned currentSong = playlist.order[playlist.current];
|
playlist.queued);
|
||||||
|
unsigned currentSong = queue_order_to_position(&playlist.queue,
|
||||||
|
playlist.current);
|
||||||
|
|
||||||
if (queuedSong == song1 || queuedSong == song2
|
if (queuedSong == song1 || queuedSong == song2
|
||||||
|| currentSong == song1 || currentSong == song2)
|
|| currentSong == song1 || currentSong == song2)
|
||||||
@ -645,18 +555,12 @@ enum playlist_result swapSongsInPlaylist(unsigned song1, unsigned song2)
|
|||||||
}
|
}
|
||||||
|
|
||||||
swapSongs(song1, song2);
|
swapSongs(song1, song2);
|
||||||
if (playlist.random) {
|
if (playlist.queue.random) {
|
||||||
unsigned i, k;
|
queue_swap_order(&playlist.queue,
|
||||||
int j = -1;
|
queue_position_to_order(&playlist.queue,
|
||||||
for (i = 0; playlist.order[i] != song1; i++) {
|
song1),
|
||||||
if (playlist.order[i] == song2)
|
queue_position_to_order(&playlist.queue,
|
||||||
j = i;
|
song2));
|
||||||
}
|
|
||||||
k = i;
|
|
||||||
for (; j == -1; i++)
|
|
||||||
if (playlist.order[i] == song2)
|
|
||||||
j = i;
|
|
||||||
swapOrder(k, j);
|
|
||||||
} else {
|
} else {
|
||||||
if (playlist.current == (int)song1)
|
if (playlist.current == (int)song1)
|
||||||
playlist.current = song2;
|
playlist.current = song2;
|
||||||
@ -680,57 +584,20 @@ enum playlist_result swapSongsInPlaylistById(unsigned id1, unsigned id2)
|
|||||||
return swapSongsInPlaylist(song1, song2);
|
return swapSongsInPlaylist(song1, song2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define moveSongFromTo(from, to) { \
|
|
||||||
playlist.idToPosition[playlist.positionToId[from]] = to; \
|
|
||||||
playlist.positionToId[to] = playlist.positionToId[from]; \
|
|
||||||
playlist.songs[to] = playlist.songs[from]; \
|
|
||||||
playlist.songMod[to] = playlist.version; \
|
|
||||||
}
|
|
||||||
|
|
||||||
enum playlist_result deleteFromPlaylist(unsigned song)
|
enum playlist_result deleteFromPlaylist(unsigned song)
|
||||||
{
|
{
|
||||||
unsigned i;
|
|
||||||
unsigned songOrder;
|
unsigned songOrder;
|
||||||
|
|
||||||
if (song >= playlist.length)
|
if (song >= queue_length(&playlist.queue))
|
||||||
return PLAYLIST_RESULT_BAD_RANGE;
|
return PLAYLIST_RESULT_BAD_RANGE;
|
||||||
|
|
||||||
|
songOrder = queue_position_to_order(&playlist.queue, song);
|
||||||
|
|
||||||
if (playlist_state == PLAYLIST_STATE_PLAY && playlist.queued >= 0
|
if (playlist_state == PLAYLIST_STATE_PLAY && playlist.queued >= 0
|
||||||
&& (playlist.order[playlist.queued] == song
|
&& (playlist.queued == (int)songOrder ||
|
||||||
|| playlist.order[playlist.current] == song))
|
playlist.current == (int)songOrder))
|
||||||
clearPlayerQueue();
|
clearPlayerQueue();
|
||||||
|
|
||||||
if (!song_in_database(playlist.songs[song])) {
|
|
||||||
pc_song_deleted(playlist.songs[song]);
|
|
||||||
song_free(playlist.songs[song]);
|
|
||||||
}
|
|
||||||
|
|
||||||
playlist.idToPosition[playlist.positionToId[song]] = -1;
|
|
||||||
|
|
||||||
/* delete song from songs array */
|
|
||||||
for (i = song; i < playlist.length - 1; i++) {
|
|
||||||
moveSongFromTo(i + 1, i);
|
|
||||||
}
|
|
||||||
/* now find it in the order array */
|
|
||||||
for (i = 0; i < playlist.length - 1; i++) {
|
|
||||||
if (playlist.order[i] == song)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
songOrder = i;
|
|
||||||
/* delete the entry from the order array */
|
|
||||||
for (; i < playlist.length - 1; i++)
|
|
||||||
playlist.order[i] = playlist.order[i + 1];
|
|
||||||
/* readjust values in the order array */
|
|
||||||
for (i = 0; i < playlist.length - 1; i++) {
|
|
||||||
if (playlist.order[i] > song)
|
|
||||||
playlist.order[i]--;
|
|
||||||
}
|
|
||||||
/* now take care of other misc stuff */
|
|
||||||
playlist.songs[playlist.length - 1] = NULL;
|
|
||||||
playlist.length--;
|
|
||||||
|
|
||||||
incrPlaylistVersion();
|
|
||||||
|
|
||||||
if (playlist_state != PLAYLIST_STATE_STOP
|
if (playlist_state != PLAYLIST_STATE_STOP
|
||||||
&& playlist.current == (int)songOrder) {
|
&& playlist.current == (int)songOrder) {
|
||||||
/*if(playlist.current>=playlist.length) return playerStop(fd);
|
/*if(playlist.current>=playlist.length) return playerStop(fd);
|
||||||
@ -739,9 +606,17 @@ enum playlist_result deleteFromPlaylist(unsigned song)
|
|||||||
playlist_noGoToNext = 1;
|
playlist_noGoToNext = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!song_in_database(queue_get(&playlist.queue, song)))
|
||||||
|
pc_song_deleted(queue_get(&playlist.queue, song));
|
||||||
|
|
||||||
|
queue_delete(&playlist.queue, song);
|
||||||
|
|
||||||
|
incrPlaylistVersion();
|
||||||
|
|
||||||
if (playlist.current > (int)songOrder) {
|
if (playlist.current > (int)songOrder) {
|
||||||
playlist.current--;
|
playlist.current--;
|
||||||
} else if (playlist.current >= (int)playlist.length) {
|
} else if (playlist.current >= (int)queue_length(&playlist.queue)) {
|
||||||
|
--playlist.current;
|
||||||
incrPlaylistCurrent();
|
incrPlaylistCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -764,11 +639,8 @@ enum playlist_result deleteFromPlaylistById(unsigned id)
|
|||||||
void
|
void
|
||||||
deleteASongFromPlaylist(const struct song *song)
|
deleteASongFromPlaylist(const struct song *song)
|
||||||
{
|
{
|
||||||
if (NULL == playlist.songs)
|
for (int i = queue_length(&playlist.queue) - 1; i >= 0; --i)
|
||||||
return;
|
if (song == queue_get(&playlist.queue, i))
|
||||||
|
|
||||||
for (int i = playlist.length - 1; i >= 0; --i)
|
|
||||||
if (song == playlist.songs[i])
|
|
||||||
deleteFromPlaylist(i);
|
deleteFromPlaylist(i);
|
||||||
|
|
||||||
pc_song_deleted(song);
|
pc_song_deleted(song);
|
||||||
@ -781,23 +653,26 @@ void stopPlaylist(void)
|
|||||||
playlist.queued = -1;
|
playlist.queued = -1;
|
||||||
playlist_state = PLAYLIST_STATE_STOP;
|
playlist_state = PLAYLIST_STATE_STOP;
|
||||||
playlist_noGoToNext = 0;
|
playlist_noGoToNext = 0;
|
||||||
if (playlist.random)
|
if (playlist.queue.random)
|
||||||
randomizeOrder(0, playlist.length - 1);
|
randomizeOrder(0, queue_length(&playlist.queue) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void playPlaylistOrderNumber(int orderNum)
|
static void playPlaylistOrderNumber(int orderNum)
|
||||||
{
|
{
|
||||||
|
struct song *song;
|
||||||
char *uri;
|
char *uri;
|
||||||
|
|
||||||
playlist_state = PLAYLIST_STATE_PLAY;
|
playlist_state = PLAYLIST_STATE_PLAY;
|
||||||
playlist_noGoToNext = 0;
|
playlist_noGoToNext = 0;
|
||||||
playlist.queued = -1;
|
playlist.queued = -1;
|
||||||
|
|
||||||
uri = song_get_uri(playlist.songs[playlist.order[orderNum]]);
|
song = queue_get_order(&playlist.queue, orderNum);
|
||||||
|
|
||||||
|
uri = song_get_uri(song);
|
||||||
g_debug("playlist: play %i:\"%s\"", orderNum, uri);
|
g_debug("playlist: play %i:\"%s\"", orderNum, uri);
|
||||||
g_free(uri);
|
g_free(uri);
|
||||||
|
|
||||||
playerPlay(playlist.songs[playlist.order[orderNum]]);
|
playerPlay(song);
|
||||||
playlist.current = orderNum;
|
playlist.current = orderNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -808,34 +683,33 @@ enum playlist_result playPlaylist(int song, int stopOnError)
|
|||||||
clearPlayerError();
|
clearPlayerError();
|
||||||
|
|
||||||
if (song == -1) {
|
if (song == -1) {
|
||||||
if (playlist.length == 0)
|
if (queue_is_empty(&playlist.queue))
|
||||||
return PLAYLIST_RESULT_SUCCESS;
|
return PLAYLIST_RESULT_SUCCESS;
|
||||||
|
|
||||||
if (playlist_state == PLAYLIST_STATE_PLAY) {
|
if (playlist_state == PLAYLIST_STATE_PLAY) {
|
||||||
playerSetPause(0);
|
playerSetPause(0);
|
||||||
return PLAYLIST_RESULT_SUCCESS;
|
return PLAYLIST_RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
if (playlist.current >= 0 &&
|
|
||||||
playlist.current < (int)playlist.length) {
|
|
||||||
i = playlist.current;
|
|
||||||
} else {
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
} else if (song < 0 || song >= (int)playlist.length) {
|
|
||||||
return PLAYLIST_RESULT_BAD_RANGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (playlist.random) {
|
i = playlist.current >= 0
|
||||||
|
? playlist.current
|
||||||
|
: 0;
|
||||||
|
} else if (!queue_valid_position(&playlist.queue, song))
|
||||||
|
return PLAYLIST_RESULT_BAD_RANGE;
|
||||||
|
|
||||||
|
if (playlist.queue.random) {
|
||||||
if (song == -1 && playlist_state == PLAYLIST_STATE_PLAY) {
|
if (song == -1 && playlist_state == PLAYLIST_STATE_PLAY) {
|
||||||
randomizeOrder(0, playlist.length - 1);
|
randomizeOrder(0, queue_length(&playlist.queue) - 1);
|
||||||
} else {
|
} else {
|
||||||
if (song >= 0)
|
if (song >= 0)
|
||||||
for (i = 0; song != (int)playlist.order[i];
|
i = queue_position_to_order(&playlist.queue, song);
|
||||||
i++) ;
|
|
||||||
if (playlist_state == PLAYLIST_STATE_STOP) {
|
if (playlist_state == PLAYLIST_STATE_STOP) {
|
||||||
playlist.current = 0;
|
playlist.current = 0;
|
||||||
}
|
}
|
||||||
swapOrder(i, playlist.current);
|
|
||||||
|
queue_swap_order(&playlist.queue,
|
||||||
|
i, playlist.current);
|
||||||
i = playlist.current;
|
i = playlist.current;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -887,7 +761,7 @@ static void currentSongInPlaylist(void)
|
|||||||
|
|
||||||
syncPlaylistWithQueue();
|
syncPlaylistWithQueue();
|
||||||
|
|
||||||
if (playlist.current >= 0 && playlist.current < (int)playlist.length)
|
if (playlist.current >= 0)
|
||||||
playPlaylistOrderNumber(playlist.current);
|
playPlaylistOrderNumber(playlist.current);
|
||||||
else
|
else
|
||||||
stopPlaylist();
|
stopPlaylist();
|
||||||
@ -895,23 +769,31 @@ static void currentSongInPlaylist(void)
|
|||||||
|
|
||||||
void nextSongInPlaylist(void)
|
void nextSongInPlaylist(void)
|
||||||
{
|
{
|
||||||
|
int next_order;
|
||||||
|
|
||||||
if (playlist_state != PLAYLIST_STATE_PLAY)
|
if (playlist_state != PLAYLIST_STATE_PLAY)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
assert(!queue_is_empty(&playlist.queue));
|
||||||
|
assert(queue_valid_order(&playlist.queue, playlist.current));
|
||||||
|
|
||||||
syncPlaylistWithQueue();
|
syncPlaylistWithQueue();
|
||||||
|
|
||||||
playlist_stopOnError = 0;
|
playlist_stopOnError = 0;
|
||||||
|
|
||||||
if (playlist.current < (int)playlist.length - 1) {
|
next_order = queue_next_order(&playlist.queue, playlist.current);
|
||||||
playPlaylistOrderNumber(playlist.current + 1);
|
if (next_order < 0) {
|
||||||
} else if (playlist.length && playlist.repeat) {
|
|
||||||
if (playlist.random)
|
|
||||||
randomizeOrder(0, playlist.length - 1);
|
|
||||||
playPlaylistOrderNumber(0);
|
|
||||||
} else {
|
|
||||||
incrPlaylistCurrent();
|
|
||||||
stopPlaylist();
|
stopPlaylist();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (next_order == 0) {
|
||||||
|
assert(playlist.queue.repeat);
|
||||||
|
|
||||||
|
randomizeOrder(0, queue_length(&playlist.queue) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
playPlaylistOrderNumber(next_order);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void playPlaylistIfPlayerStopped(void)
|
static void playPlaylistIfPlayerStopped(void)
|
||||||
@ -928,7 +810,7 @@ static void playPlaylistIfPlayerStopped(void)
|
|||||||
&& ((playlist_stopOnError && error != PLAYER_ERROR_NOERROR)
|
&& ((playlist_stopOnError && error != PLAYER_ERROR_NOERROR)
|
||||||
|| error == PLAYER_ERROR_AUDIO
|
|| error == PLAYER_ERROR_AUDIO
|
||||||
|| error == PLAYER_ERROR_SYSTEM
|
|| error == PLAYER_ERROR_SYSTEM
|
||||||
|| playlist_errorCount >= playlist.length)) {
|
|| playlist_errorCount >= queue_length(&playlist.queue))) {
|
||||||
stopPlaylist();
|
stopPlaylist();
|
||||||
} else if (playlist_noGoToNext)
|
} else if (playlist_noGoToNext)
|
||||||
currentSongInPlaylist();
|
currentSongInPlaylist();
|
||||||
@ -939,37 +821,34 @@ static void playPlaylistIfPlayerStopped(void)
|
|||||||
|
|
||||||
bool getPlaylistRepeatStatus(void)
|
bool getPlaylistRepeatStatus(void)
|
||||||
{
|
{
|
||||||
return playlist.repeat;
|
return playlist.queue.repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getPlaylistRandomStatus(void)
|
bool getPlaylistRandomStatus(void)
|
||||||
{
|
{
|
||||||
return playlist.random;
|
return playlist.queue.random;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPlaylistRepeatStatus(bool status)
|
void setPlaylistRepeatStatus(bool status)
|
||||||
{
|
{
|
||||||
if (playlist_state == PLAYLIST_STATE_PLAY &&
|
if (playlist_state == PLAYLIST_STATE_PLAY &&
|
||||||
playlist.repeat && !status && playlist.queued == 0)
|
playlist.queue.repeat && !status && playlist.queued == 0)
|
||||||
clearPlayerQueue();
|
clearPlayerQueue();
|
||||||
|
|
||||||
playlist.repeat = status;
|
playlist.queue.repeat = status;
|
||||||
|
|
||||||
idle_add(IDLE_OPTIONS);
|
idle_add(IDLE_OPTIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum playlist_result moveSongInPlaylist(unsigned from, int to)
|
enum playlist_result moveSongInPlaylist(unsigned from, int to)
|
||||||
{
|
{
|
||||||
unsigned i;
|
|
||||||
struct song *tmpSong;
|
|
||||||
unsigned tmpId;
|
|
||||||
int currentSong;
|
int currentSong;
|
||||||
|
|
||||||
if (from >= playlist.length)
|
if (!queue_valid_position(&playlist.queue, from))
|
||||||
return PLAYLIST_RESULT_BAD_RANGE;
|
return PLAYLIST_RESULT_BAD_RANGE;
|
||||||
|
|
||||||
if ((to >= 0 && to >= (int)playlist.length) ||
|
if ((to >= 0 && to >= (int)queue_length(&playlist.queue)) ||
|
||||||
(to < 0 && abs(to) > (int)playlist.length))
|
(to < 0 && abs(to) > (int)queue_length(&playlist.queue)))
|
||||||
return PLAYLIST_RESULT_BAD_RANGE;
|
return PLAYLIST_RESULT_BAD_RANGE;
|
||||||
|
|
||||||
if ((int)from == to) /* no-op */
|
if ((int)from == to) /* no-op */
|
||||||
@ -980,52 +859,28 @@ enum playlist_result moveSongInPlaylist(unsigned from, int to)
|
|||||||
* (-playlist.length == to) => move to position BEFORE current song
|
* (-playlist.length == to) => move to position BEFORE current song
|
||||||
*/
|
*/
|
||||||
currentSong = playlist.current >= 0
|
currentSong = playlist.current >= 0
|
||||||
? (int)playlist.order[playlist.current] : -1;
|
? (int)queue_order_to_position(&playlist.queue,
|
||||||
|
playlist.current)
|
||||||
|
: -1;
|
||||||
if (to < 0 && playlist.current >= 0) {
|
if (to < 0 && playlist.current >= 0) {
|
||||||
if ((unsigned)currentSong == from)
|
if ((unsigned)currentSong == from)
|
||||||
/* no-op, can't be moved to offset of itself */
|
/* no-op, can't be moved to offset of itself */
|
||||||
return PLAYLIST_RESULT_SUCCESS;
|
return PLAYLIST_RESULT_SUCCESS;
|
||||||
to = (currentSong + abs(to)) % playlist.length;
|
to = (currentSong + abs(to)) % queue_length(&playlist.queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playlist_state == PLAYLIST_STATE_PLAY && playlist.queued >= 0) {
|
if (playlist_state == PLAYLIST_STATE_PLAY && playlist.queued >= 0) {
|
||||||
int queuedSong = playlist.order[playlist.queued];
|
int queuedSong = queue_order_to_position(&playlist.queue,
|
||||||
|
playlist.queued);
|
||||||
if (queuedSong == (int)from || queuedSong == to
|
if (queuedSong == (int)from || queuedSong == to
|
||||||
|| currentSong == (int)from || currentSong == to)
|
|| currentSong == (int)from || currentSong == to)
|
||||||
clearPlayerQueue();
|
clearPlayerQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpSong = playlist.songs[from];
|
queue_move(&playlist.queue, from, to);
|
||||||
tmpId = playlist.positionToId[from];
|
|
||||||
/* move songs to one less in from->to */
|
if (!playlist.queue.random) {
|
||||||
for (i = from; (int)i < to; i++) {
|
/* update current/queued */
|
||||||
moveSongFromTo(i + 1, i);
|
|
||||||
}
|
|
||||||
/* move songs to one more in to->from */
|
|
||||||
for (i = from; (int)i > to; i--) {
|
|
||||||
moveSongFromTo(i - 1, i);
|
|
||||||
}
|
|
||||||
/* put song at _to_ */
|
|
||||||
playlist.idToPosition[tmpId] = to;
|
|
||||||
playlist.positionToId[to] = tmpId;
|
|
||||||
playlist.songs[to] = tmpSong;
|
|
||||||
playlist.songMod[to] = playlist.version;
|
|
||||||
/* now deal with order */
|
|
||||||
if (playlist.random) {
|
|
||||||
for (i = 0; i < playlist.length; i++) {
|
|
||||||
if (playlist.order[i] > from &&
|
|
||||||
(int)playlist.order[i] <= to) {
|
|
||||||
playlist.order[i]--;
|
|
||||||
} else if (playlist.order[i] < from &&
|
|
||||||
(int)playlist.order[i] >= to) {
|
|
||||||
playlist.order[i]++;
|
|
||||||
} else if (from == playlist.order[i]) {
|
|
||||||
playlist.order[i] = to;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (playlist.current == (int)from)
|
if (playlist.current == (int)from)
|
||||||
playlist.current = to;
|
playlist.current = to;
|
||||||
else if (playlist.current > (int)from &&
|
else if (playlist.current > (int)from &&
|
||||||
@ -1063,25 +918,14 @@ enum playlist_result moveSongInPlaylistById(unsigned id1, int to)
|
|||||||
|
|
||||||
static void orderPlaylist(void)
|
static void orderPlaylist(void)
|
||||||
{
|
{
|
||||||
unsigned i;
|
if (playlist.current >= 0)
|
||||||
|
playlist.current = queue_order_to_position(&playlist.queue,
|
||||||
if (playlist.current >= 0 && playlist.current < (int)playlist.length)
|
playlist.current);
|
||||||
playlist.current = playlist.order[playlist.current];
|
|
||||||
|
|
||||||
if (playlist_state == PLAYLIST_STATE_PLAY && playlist.queued >= 0)
|
if (playlist_state == PLAYLIST_STATE_PLAY && playlist.queued >= 0)
|
||||||
clearPlayerQueue();
|
clearPlayerQueue();
|
||||||
|
|
||||||
for (i = 0; i < playlist.length; i++) {
|
queue_restore_order(&playlist.queue);
|
||||||
playlist.order[i] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void swapOrder(int a, int b)
|
|
||||||
{
|
|
||||||
int bak = playlist.order[a];
|
|
||||||
playlist.order[a] = playlist.order[b];
|
|
||||||
playlist.order[b] = bak;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void randomizeOrder(int start, int end)
|
static void randomizeOrder(int start, int end)
|
||||||
@ -1101,26 +945,25 @@ static void randomizeOrder(int start, int end)
|
|||||||
playlist.current = i;
|
playlist.current = i;
|
||||||
else if (i == playlist.current)
|
else if (i == playlist.current)
|
||||||
playlist.current = ri;
|
playlist.current = ri;
|
||||||
swapOrder(i, ri);
|
queue_swap_order(&playlist.queue, i, ri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPlaylistRandomStatus(bool status)
|
void setPlaylistRandomStatus(bool status)
|
||||||
{
|
{
|
||||||
if (status == playlist.random)
|
if (status == playlist.queue.random)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
playlist.random = status;
|
playlist.queue.random = status;
|
||||||
|
|
||||||
if (playlist.random) {
|
if (playlist.queue.random) {
|
||||||
/*if(playlist_state==PLAYLIST_STATE_PLAY) {
|
/*if(playlist_state==PLAYLIST_STATE_PLAY) {
|
||||||
randomizeOrder(playlist.current+1,
|
randomizeOrder(playlist.current+1,
|
||||||
playlist.length-1);
|
playlist.length-1);
|
||||||
}
|
}
|
||||||
else */ randomizeOrder(0, playlist.length - 1);
|
else */ randomizeOrder(0, queue_length(&playlist.queue) - 1);
|
||||||
if (playlist.current >= 0 &&
|
if (playlist.current >= 0) {
|
||||||
playlist.current < (int)playlist.length) {
|
queue_swap_order(&playlist.queue, playlist.current, 0);
|
||||||
swapOrder(playlist.current, 0);
|
|
||||||
playlist.current = 0;
|
playlist.current = 0;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -1146,8 +989,8 @@ void previousSongInPlaylist(void)
|
|||||||
} else {
|
} else {
|
||||||
if (playlist.current > 0) {
|
if (playlist.current > 0) {
|
||||||
playPlaylistOrderNumber(playlist.current - 1);
|
playPlaylistOrderNumber(playlist.current - 1);
|
||||||
} else if (playlist.repeat) {
|
} else if (playlist.queue.repeat) {
|
||||||
playPlaylistOrderNumber(playlist.length - 1);
|
playPlaylistOrderNumber(queue_length(&playlist.queue) - 1);
|
||||||
} else {
|
} else {
|
||||||
playPlaylistOrderNumber(playlist.current);
|
playPlaylistOrderNumber(playlist.current);
|
||||||
}
|
}
|
||||||
@ -1157,21 +1000,20 @@ void previousSongInPlaylist(void)
|
|||||||
void shufflePlaylist(void)
|
void shufflePlaylist(void)
|
||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
int ri;
|
|
||||||
|
|
||||||
if (playlist.length > 1) {
|
if (queue_length(&playlist.queue) > 1) {
|
||||||
if (playlist_state == PLAYLIST_STATE_PLAY) {
|
if (playlist_state == PLAYLIST_STATE_PLAY) {
|
||||||
if (playlist.queued >= 0)
|
if (playlist.queued >= 0)
|
||||||
clearPlayerQueue();
|
clearPlayerQueue();
|
||||||
|
|
||||||
if (playlist.current >= 0)
|
if (playlist.current >= 0)
|
||||||
/* put current playing song first */
|
/* put current playing song first */
|
||||||
swapSongs(0, playlist.order[playlist.current]);
|
swapSongs(0, queue_order_to_position(&playlist.queue,
|
||||||
|
playlist.current));
|
||||||
|
|
||||||
if (playlist.random) {
|
if (playlist.queue.random) {
|
||||||
int j;
|
playlist.current =
|
||||||
for (j = 0; 0 != playlist.order[j]; j++) ;
|
queue_position_to_order(&playlist.queue, 0);
|
||||||
playlist.current = j;
|
|
||||||
} else
|
} else
|
||||||
playlist.current = 0;
|
playlist.current = 0;
|
||||||
i = 1;
|
i = 1;
|
||||||
@ -1179,11 +1021,10 @@ void shufflePlaylist(void)
|
|||||||
i = 0;
|
i = 0;
|
||||||
playlist.current = -1;
|
playlist.current = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* shuffle the rest of the list */
|
/* shuffle the rest of the list */
|
||||||
for (; i < playlist.length; i++) {
|
queue_shuffle_range(&playlist.queue, i,
|
||||||
ri = g_rand_int_range(g_rand, i, playlist.length);
|
queue_length(&playlist.queue));
|
||||||
swapSongs(i, ri);
|
|
||||||
}
|
|
||||||
|
|
||||||
incrPlaylistVersion();
|
incrPlaylistVersion();
|
||||||
}
|
}
|
||||||
@ -1212,8 +1053,8 @@ enum playlist_result savePlaylist(const char *utf8file)
|
|||||||
if (fp == NULL)
|
if (fp == NULL)
|
||||||
return PLAYLIST_RESULT_ERRNO;
|
return PLAYLIST_RESULT_ERRNO;
|
||||||
|
|
||||||
for (unsigned i = 0; i < playlist.length; i++)
|
for (unsigned i = 0; i < queue_length(&playlist.queue); i++)
|
||||||
playlist_print_song(fp, playlist.songs[i]);
|
playlist_print_song(fp, queue_get(&playlist.queue, i));
|
||||||
|
|
||||||
while (fclose(fp) && errno == EINTR) ;
|
while (fclose(fp) && errno == EINTR) ;
|
||||||
|
|
||||||
@ -1223,22 +1064,21 @@ enum playlist_result savePlaylist(const char *utf8file)
|
|||||||
|
|
||||||
int getPlaylistCurrentSong(void)
|
int getPlaylistCurrentSong(void)
|
||||||
{
|
{
|
||||||
if (playlist.current >= 0 &&
|
if (playlist.current >= 0)
|
||||||
playlist.current < (int)playlist.length) {
|
return queue_order_to_position(&playlist.queue,
|
||||||
return playlist.order[playlist.current];
|
playlist.current);
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long getPlaylistVersion(void)
|
unsigned long getPlaylistVersion(void)
|
||||||
{
|
{
|
||||||
return playlist.version;
|
return playlist.queue.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getPlaylistLength(void)
|
int getPlaylistLength(void)
|
||||||
{
|
{
|
||||||
return playlist.length;
|
return queue_length(&playlist.queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum playlist_result seekSongInPlaylist(unsigned song, float seek_time)
|
enum playlist_result seekSongInPlaylist(unsigned song, float seek_time)
|
||||||
@ -1246,11 +1086,11 @@ enum playlist_result seekSongInPlaylist(unsigned song, float seek_time)
|
|||||||
unsigned i;
|
unsigned i;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (song >= playlist.length)
|
if (!queue_valid_position(&playlist.queue, song))
|
||||||
return PLAYLIST_RESULT_BAD_RANGE;
|
return PLAYLIST_RESULT_BAD_RANGE;
|
||||||
|
|
||||||
if (playlist.random)
|
if (playlist.queue.random)
|
||||||
for (i = 0; song != playlist.order[i]; i++) ;
|
i = queue_position_to_order(&playlist.queue, song);
|
||||||
else
|
else
|
||||||
i = song;
|
i = song;
|
||||||
|
|
||||||
@ -1268,7 +1108,7 @@ enum playlist_result seekSongInPlaylist(unsigned song, float seek_time)
|
|||||||
playPlaylistOrderNumber(i);
|
playPlaylistOrderNumber(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = playerSeek(playlist.songs[playlist.order[i]], seek_time);
|
ret = playerSeek(queue_get_order(&playlist.queue, i), seek_time);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return PLAYLIST_RESULT_NOT_PLAYING;
|
return PLAYLIST_RESULT_NOT_PLAYING;
|
||||||
|
|
||||||
@ -1286,7 +1126,7 @@ enum playlist_result seekSongInPlaylistById(unsigned id, float seek_time)
|
|||||||
|
|
||||||
unsigned getPlaylistSongId(unsigned song)
|
unsigned getPlaylistSongId(unsigned song)
|
||||||
{
|
{
|
||||||
return playlist.positionToId[song];
|
return queue_position_to_id(&playlist.queue, song);
|
||||||
}
|
}
|
||||||
|
|
||||||
int PlaylistInfo(struct client *client, const char *utf8file, int detail)
|
int PlaylistInfo(struct client *client, const char *utf8file, int detail)
|
||||||
@ -1357,8 +1197,10 @@ void searchForSongsInPlaylist(struct client *client,
|
|||||||
items[i].needle = g_utf8_casefold(originalNeedles[i], -1);
|
items[i].needle = g_utf8_casefold(originalNeedles[i], -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < playlist.length; i++) {
|
for (i = 0; i < queue_length(&playlist.queue); i++) {
|
||||||
if (strstrSearchTags(playlist.songs[i], numItems, items))
|
const struct song *song = queue_get(&playlist.queue, i);
|
||||||
|
|
||||||
|
if (strstrSearchTags(song, numItems, items))
|
||||||
printPlaylistSongInfo(client, i);
|
printPlaylistSongInfo(client, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1373,8 +1215,10 @@ void searchForSongsInPlaylist(struct client *client,
|
|||||||
void findSongsInPlaylist(struct client *client,
|
void findSongsInPlaylist(struct client *client,
|
||||||
unsigned numItems, LocateTagItem * items)
|
unsigned numItems, LocateTagItem * items)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < playlist.length; i++) {
|
for (unsigned i = 0; i < queue_length(&playlist.queue); i++) {
|
||||||
if (tagItemsFoundAndMatches(playlist.songs[i], numItems, items))
|
const struct song *song = queue_get(&playlist.queue, i);
|
||||||
|
|
||||||
|
if (tagItemsFoundAndMatches(song, numItems, items))
|
||||||
printPlaylistSongInfo(client, i);
|
printPlaylistSongInfo(client, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#define MPD_PLAYLIST_H
|
#define MPD_PLAYLIST_H
|
||||||
|
|
||||||
#include "locate.h"
|
#include "locate.h"
|
||||||
|
#include "queue.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -43,18 +44,10 @@ enum playlist_result {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _Playlist {
|
typedef struct _Playlist {
|
||||||
struct song **songs;
|
struct queue queue;
|
||||||
/* holds version a song was modified on */
|
|
||||||
uint32_t *songMod;
|
|
||||||
unsigned *order;
|
|
||||||
unsigned *positionToId;
|
|
||||||
int *idToPosition;
|
|
||||||
unsigned length;
|
|
||||||
int current;
|
int current;
|
||||||
int queued;
|
int queued;
|
||||||
bool repeat;
|
|
||||||
bool random;
|
|
||||||
uint32_t version;
|
|
||||||
} Playlist;
|
} Playlist;
|
||||||
|
|
||||||
extern bool playlist_saveAbsolutePaths;
|
extern bool playlist_saveAbsolutePaths;
|
||||||
|
267
src/queue.c
Normal file
267
src/queue.c
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2009 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "queue.h"
|
||||||
|
#include "song.h"
|
||||||
|
|
||||||
|
unsigned
|
||||||
|
queue_generate_id(const struct queue *queue)
|
||||||
|
{
|
||||||
|
static unsigned cur = (unsigned)-1;
|
||||||
|
|
||||||
|
do {
|
||||||
|
cur++;
|
||||||
|
|
||||||
|
if (cur >= queue->max_length * QUEUE_HASH_MULT)
|
||||||
|
cur = 0;
|
||||||
|
} while (queue->idToPosition[cur] != -1);
|
||||||
|
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
queue_next_order(const struct queue *queue, unsigned order)
|
||||||
|
{
|
||||||
|
assert(order < queue->length);
|
||||||
|
|
||||||
|
if (order + 1 < queue->length)
|
||||||
|
return order + 1;
|
||||||
|
else if (queue->repeat)
|
||||||
|
/* restart at first song */
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
/* end of queue */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
queue_increment_version(struct queue *queue)
|
||||||
|
{
|
||||||
|
static unsigned long max = ((uint32_t) 1 << 31) - 1;
|
||||||
|
|
||||||
|
queue->version++;
|
||||||
|
|
||||||
|
if (queue->version >= max) {
|
||||||
|
for (unsigned i = 0; i < queue->length; i++)
|
||||||
|
queue->songMod[i] = 0;
|
||||||
|
|
||||||
|
queue->version = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
queue_modify(struct queue *queue, unsigned order)
|
||||||
|
{
|
||||||
|
unsigned position;
|
||||||
|
|
||||||
|
assert(order < queue->length);
|
||||||
|
|
||||||
|
position = queue->order[order];
|
||||||
|
queue->songMod[position] = queue->version;
|
||||||
|
|
||||||
|
queue_increment_version(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
queue_modify_all(struct queue *queue)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < queue->length; i++)
|
||||||
|
queue->songMod[i] = queue->version;
|
||||||
|
|
||||||
|
queue_increment_version(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned
|
||||||
|
queue_append(struct queue *queue, struct song *song)
|
||||||
|
{
|
||||||
|
unsigned id = queue_generate_id(queue);
|
||||||
|
|
||||||
|
assert(!queue_is_full(queue));
|
||||||
|
|
||||||
|
queue->songs[queue->length] = song;
|
||||||
|
queue->songMod[queue->length] = queue->version;
|
||||||
|
queue->order[queue->length] = queue->length;
|
||||||
|
queue->positionToId[queue->length] = id;
|
||||||
|
queue->idToPosition[queue->positionToId[queue->length]] =
|
||||||
|
queue->length;
|
||||||
|
|
||||||
|
++queue->length;
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
queue_swap(struct queue *queue, unsigned position1, unsigned position2)
|
||||||
|
{
|
||||||
|
struct song *sTemp;
|
||||||
|
unsigned iTemp;
|
||||||
|
|
||||||
|
sTemp = queue->songs[position1];
|
||||||
|
queue->songs[position1] = queue->songs[position2];
|
||||||
|
queue->songs[position2] = sTemp;
|
||||||
|
|
||||||
|
queue->songMod[position1] = queue->version;
|
||||||
|
queue->songMod[position2] = queue->version;
|
||||||
|
|
||||||
|
queue->idToPosition[queue->positionToId[position1]] = position2;
|
||||||
|
queue->idToPosition[queue->positionToId[position2]] = position1;
|
||||||
|
|
||||||
|
iTemp = queue->positionToId[position1];
|
||||||
|
queue->positionToId[position1] = queue->positionToId[position2];
|
||||||
|
queue->positionToId[position2] = iTemp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
queue_move_song_to(struct queue *queue, unsigned from, unsigned to)
|
||||||
|
{
|
||||||
|
unsigned from_id = queue->positionToId[from];
|
||||||
|
|
||||||
|
queue->idToPosition[from_id] = to;
|
||||||
|
queue->positionToId[to] = from_id;
|
||||||
|
queue->songs[to] = queue->songs[from];
|
||||||
|
queue->songMod[to] = queue->version;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
queue_move(struct queue *queue, unsigned from, unsigned to)
|
||||||
|
{
|
||||||
|
struct song *song;
|
||||||
|
unsigned id;
|
||||||
|
|
||||||
|
song = queue_get(queue, from);
|
||||||
|
id = queue_position_to_id(queue, from);
|
||||||
|
|
||||||
|
/* move songs to one less in from->to */
|
||||||
|
|
||||||
|
for (unsigned i = from; i < to; i++)
|
||||||
|
queue_move_song_to(queue, i + 1, i);
|
||||||
|
|
||||||
|
/* move songs to one more in to->from */
|
||||||
|
|
||||||
|
for (unsigned i = from; i > to; i--)
|
||||||
|
queue_move_song_to(queue, i - 1, i);
|
||||||
|
|
||||||
|
/* put song at _to_ */
|
||||||
|
|
||||||
|
queue->idToPosition[id] = to;
|
||||||
|
queue->positionToId[to] = id;
|
||||||
|
queue->songs[to] = song;
|
||||||
|
queue->songMod[to] = queue->version;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
queue_delete(struct queue *queue, unsigned position)
|
||||||
|
{
|
||||||
|
struct song *song;
|
||||||
|
unsigned id, order;
|
||||||
|
|
||||||
|
assert(position < queue->length);
|
||||||
|
|
||||||
|
song = queue_get(queue, position);
|
||||||
|
if (!song_in_database(song))
|
||||||
|
song_free(song);
|
||||||
|
|
||||||
|
id = queue_position_to_id(queue, position);
|
||||||
|
order = queue_position_to_order(queue, position);
|
||||||
|
|
||||||
|
--queue->length;
|
||||||
|
|
||||||
|
/* release the song id */
|
||||||
|
|
||||||
|
queue->idToPosition[id] = -1;
|
||||||
|
|
||||||
|
/* delete song from songs array */
|
||||||
|
|
||||||
|
for (unsigned i = position; i < queue->length; i++)
|
||||||
|
queue_move_song_to(queue, i + 1, i);
|
||||||
|
|
||||||
|
/* delete the entry from the order array */
|
||||||
|
|
||||||
|
for (unsigned i = order; i < queue->length; i++)
|
||||||
|
queue->order[i] = queue->order[i + 1];
|
||||||
|
|
||||||
|
/* readjust values in the order array */
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < queue->length; i++)
|
||||||
|
if (queue->order[i] > position)
|
||||||
|
--queue->order[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
queue_clear(struct queue *queue)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < queue->length; i++) {
|
||||||
|
if (!song_in_database(queue->songs[i]))
|
||||||
|
song_free(queue->songs[i]);
|
||||||
|
|
||||||
|
queue->idToPosition[queue->positionToId[i]] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue->length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
queue_init(struct queue *queue, unsigned max_length)
|
||||||
|
{
|
||||||
|
queue->max_length = max_length;
|
||||||
|
queue->length = 0;
|
||||||
|
queue->version = 1;
|
||||||
|
queue->repeat = false;
|
||||||
|
queue->random = false;
|
||||||
|
|
||||||
|
queue->songs = g_malloc(sizeof(queue->songs[0]) * max_length);
|
||||||
|
queue->songMod = g_malloc(sizeof(queue->songMod[0]) *
|
||||||
|
max_length);
|
||||||
|
queue->order = g_malloc(sizeof(queue->order[0]) *
|
||||||
|
max_length);
|
||||||
|
queue->idToPosition = g_malloc(sizeof(queue->idToPosition[0]) *
|
||||||
|
max_length * QUEUE_HASH_MULT);
|
||||||
|
queue->positionToId = g_malloc(sizeof(queue->positionToId[0]) *
|
||||||
|
max_length);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < max_length * QUEUE_HASH_MULT; ++i)
|
||||||
|
queue->idToPosition[i] = -1;
|
||||||
|
|
||||||
|
queue->rand = g_rand_new();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
queue_finish(struct queue *queue)
|
||||||
|
{
|
||||||
|
queue_clear(queue);
|
||||||
|
|
||||||
|
g_free(queue->songs);
|
||||||
|
g_free(queue->songMod);
|
||||||
|
g_free(queue->order);
|
||||||
|
g_free(queue->idToPosition);
|
||||||
|
g_free(queue->positionToId);
|
||||||
|
|
||||||
|
g_rand_free(queue->rand);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
queue_shuffle_range(struct queue *queue, unsigned start, unsigned end)
|
||||||
|
{
|
||||||
|
assert(start <= end);
|
||||||
|
assert(end <= queue->length);
|
||||||
|
|
||||||
|
for (unsigned i = start; i < end; i++) {
|
||||||
|
unsigned ri = g_rand_int_range(queue->rand, i, end);
|
||||||
|
queue_swap(queue, i, ri);
|
||||||
|
}
|
||||||
|
}
|
314
src/queue.h
Normal file
314
src/queue.h
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2009 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QUEUE_H
|
||||||
|
#define QUEUE_H
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
/**
|
||||||
|
* reserve max_length * QUEUE_HASH_MULT elements in the id
|
||||||
|
* number space
|
||||||
|
*/
|
||||||
|
QUEUE_HASH_MULT = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A queue of songs. This is the backend of the playlist: it contains
|
||||||
|
* an ordered list of songs.
|
||||||
|
*
|
||||||
|
* Songs can be addressed in three possible ways:
|
||||||
|
*
|
||||||
|
* - the position in the queue
|
||||||
|
* - the unique id (which stays the same, regardless of moves)
|
||||||
|
* - the order number (which only differs from "position" in random mode)
|
||||||
|
*/
|
||||||
|
struct queue {
|
||||||
|
/** configured maximum length of the queue */
|
||||||
|
unsigned max_length;
|
||||||
|
|
||||||
|
/** number of songs in the queue */
|
||||||
|
unsigned length;
|
||||||
|
|
||||||
|
/** the current version number */
|
||||||
|
uint32_t version;
|
||||||
|
|
||||||
|
/** all songs in "position" order */
|
||||||
|
struct song **songs;
|
||||||
|
|
||||||
|
/** holds version a song was modified on */
|
||||||
|
uint32_t *songMod;
|
||||||
|
|
||||||
|
/** map order numbers to positions */
|
||||||
|
unsigned *order;
|
||||||
|
|
||||||
|
/** map positions to song ids */
|
||||||
|
unsigned *positionToId;
|
||||||
|
|
||||||
|
/** map song ids to posiitons */
|
||||||
|
int *idToPosition;
|
||||||
|
|
||||||
|
/** repeat playback when the end of the queue has been
|
||||||
|
reached? */
|
||||||
|
bool repeat;
|
||||||
|
|
||||||
|
/** play back songs in random order? */
|
||||||
|
bool random;
|
||||||
|
|
||||||
|
/** random number generator for shuffle and random mode */
|
||||||
|
GRand *rand;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline unsigned
|
||||||
|
queue_length(const struct queue *queue)
|
||||||
|
{
|
||||||
|
assert(queue->length <= queue->max_length);
|
||||||
|
|
||||||
|
return queue->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the queue is empty, i.e. there are no songs.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
queue_is_empty(const struct queue *queue)
|
||||||
|
{
|
||||||
|
return queue->length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the maximum number of songs has been reached.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
queue_is_full(const struct queue *queue)
|
||||||
|
{
|
||||||
|
assert(queue->length <= queue->max_length);
|
||||||
|
|
||||||
|
return queue->length >= queue->max_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is that a valid position number?
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
queue_valid_position(const struct queue *queue, unsigned position)
|
||||||
|
{
|
||||||
|
return position < queue->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is that a valid order number?
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
queue_valid_order(const struct queue *queue, unsigned order)
|
||||||
|
{
|
||||||
|
return order < queue->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
queue_id_to_position(const struct queue *queue, unsigned id)
|
||||||
|
{
|
||||||
|
if (id >= queue->max_length * QUEUE_HASH_MULT)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
assert(queue->idToPosition[id] >= -1);
|
||||||
|
assert(queue->idToPosition[id] < (int)queue->length);
|
||||||
|
|
||||||
|
return queue->idToPosition[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
queue_position_to_id(const struct queue *queue, unsigned position)
|
||||||
|
{
|
||||||
|
assert(position < queue->length);
|
||||||
|
|
||||||
|
return queue->positionToId[position];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned
|
||||||
|
queue_order_to_position(const struct queue *queue, unsigned order)
|
||||||
|
{
|
||||||
|
assert(order < queue->length);
|
||||||
|
|
||||||
|
return queue->order[order];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned
|
||||||
|
queue_position_to_order(const struct queue *queue, unsigned position)
|
||||||
|
{
|
||||||
|
assert(position < queue->length);
|
||||||
|
|
||||||
|
for (unsigned i = 0;; ++i) {
|
||||||
|
assert(i < queue->length);
|
||||||
|
|
||||||
|
if (queue->order[i] == position)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the song at the specified position.
|
||||||
|
*/
|
||||||
|
static inline struct song *
|
||||||
|
queue_get(const struct queue *queue, unsigned position)
|
||||||
|
{
|
||||||
|
assert(position < queue->length);
|
||||||
|
|
||||||
|
return queue->songs[position];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the song at the specified order number.
|
||||||
|
*/
|
||||||
|
static inline struct song *
|
||||||
|
queue_get_order(const struct queue *queue, unsigned order)
|
||||||
|
{
|
||||||
|
return queue_get(queue, queue_order_to_position(queue, order));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the song at the specified position newer than the specified
|
||||||
|
* version?
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
queue_song_newer(const struct queue *queue, unsigned position,
|
||||||
|
uint32_t version)
|
||||||
|
{
|
||||||
|
assert(position < queue->length);
|
||||||
|
|
||||||
|
return version > queue->version ||
|
||||||
|
queue->songMod[position] >= version ||
|
||||||
|
queue->songMod[position] == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a queue object.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
queue_init(struct queue *queue, unsigned max_length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deinitializes a queue object. It does not free the queue pointer
|
||||||
|
* itself.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
queue_finish(struct queue *queue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a non-existing id number.
|
||||||
|
*/
|
||||||
|
unsigned
|
||||||
|
queue_generate_id(const struct queue *queue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the order number following the specified one. This takes
|
||||||
|
* end of queue and "repeat" mode into account.
|
||||||
|
*
|
||||||
|
* @return the next order number, or -1 to stop playback
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
queue_next_order(const struct queue *queue, unsigned order);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the queue's version number. This handles integer
|
||||||
|
* overflow well.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
queue_increment_version(struct queue *queue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the specified song as "modified" and increments the version
|
||||||
|
* number.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
queue_modify(struct queue *queue, unsigned order);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks all songs as "modified" and increments the version number.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
queue_modify_all(struct queue *queue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a song to the queue and returns its position. Prior to
|
||||||
|
* that, the caller must check if the queue is already full.
|
||||||
|
*
|
||||||
|
* If a song is not in the database (determined by
|
||||||
|
* song_in_database()), it is freed when removed from the queue.
|
||||||
|
*/
|
||||||
|
unsigned
|
||||||
|
queue_append(struct queue *queue, struct song *song);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swaps two songs, addressed by their position.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
queue_swap(struct queue *queue, unsigned position1, unsigned position2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swaps two songs, addressed by their order number.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
queue_swap_order(struct queue *queue, unsigned order1, unsigned order2)
|
||||||
|
{
|
||||||
|
unsigned tmp = queue->order[order1];
|
||||||
|
queue->order[order1] = queue->order[order2];
|
||||||
|
queue->order[order2] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves a song to a new position.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
queue_move(struct queue *queue, unsigned from, unsigned to);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a song from the playlist.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
queue_delete(struct queue *queue, unsigned position);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all songs from the playlist.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
queue_clear(struct queue *queue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the "order" array, and restores "normal" order.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
queue_restore_order(struct queue *queue)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < queue->length; ++i)
|
||||||
|
queue->order[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shuffles a (position) range in the queue. The songs are physically
|
||||||
|
* shuffled, not by using the "order" mapping.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
queue_shuffle_range(struct queue *queue, unsigned start, unsigned end);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user