void playlist_stop(struct playlist *playlist, struct player_control *pc) { if (!playlist->playing) return; assert(playlist->current >= 0); g_debug("stop"); pc_stop(pc); playlist->queued = -1; playlist->playing = false; if (playlist->queue.random) { /* shuffle the playlist, so the next playback will result in a new random order */ unsigned current_position = queue_order_to_position(&playlist->queue, playlist->current); queue_shuffle_order(&playlist->queue); /* make sure that "current" stays valid, and the next "play" command plays the same song again */ playlist->current = queue_position_to_order(&playlist->queue, current_position); } }
void playlist_update_queued_song(struct playlist *playlist, struct player_control *pc, const struct song *prev) { int next_order; const struct song *next_song; if (!playlist->playing) return; assert(!queue_is_empty(&playlist->queue)); assert((playlist->queued < 0) == (prev == NULL)); next_order = playlist->current >= 0 ? queue_next_order(&playlist->queue, playlist->current) : 0; if (next_order == 0 && playlist->queue.random && !playlist->queue.single) { /* shuffle the song order again, so we get a different order each time the playlist is played completely */ unsigned current_position = queue_order_to_position(&playlist->queue, playlist->current); queue_shuffle_order(&playlist->queue); /* make sure that the playlist->current still points to the current song, after the song order has been shuffled */ playlist->current = queue_position_to_order(&playlist->queue, current_position); } if (next_order >= 0) next_song = queue_get_order(&playlist->queue, next_order); else next_song = NULL; if (prev != NULL && next_song != prev) { /* clear the currently queued song */ pc_cancel(pc); playlist->queued = -1; } if (next_order >= 0) { if (next_song != prev) playlist_queue_song_order(playlist, pc, next_order); else playlist->queued = next_order; } }
static void playlist_delete_internal(struct playlist *playlist, unsigned song, const struct song **queued_p) { unsigned songOrder; assert(song < queue_length(&playlist->queue)); songOrder = queue_position_to_order(&playlist->queue, song); if (playlist->playing && playlist->current == (int)songOrder) { bool paused = pc_get_state() == PLAYER_STATE_PAUSE; /* the current song is going to be deleted: stop the player */ pc_stop(); playlist->playing = false; /* see which song is going to be played instead */ playlist->current = queue_next_order(&playlist->queue, playlist->current); if (playlist->current == (int)songOrder) playlist->current = -1; if (playlist->current >= 0 && !paused) /* play the song after the deleted one */ playlist_play_order(playlist, playlist->current); else /* no songs left to play, stop playback completely */ playlist_stop(playlist); *queued_p = NULL; } else if (playlist->current == (int)songOrder) /* there's a "current song" but we're not playing currently - clear "current" */ playlist->current = -1; /* now do it: remove the song */ if (!song_in_database(queue_get(&playlist->queue, song))) pc_song_deleted(queue_get(&playlist->queue, song)); queue_delete(&playlist->queue, song); /* update the "current" and "queued" variables */ if (playlist->current > (int)songOrder) { playlist->current--; } }
enum playlist_result playlist_play(struct playlist *playlist, struct player_control *pc, int song) { unsigned i = song; pc_clear_error(pc); if (song == -1) { /* play any song ("current" song, or the first song */ if (queue_is_empty(&playlist->queue)) return PLAYLIST_RESULT_SUCCESS; if (playlist->playing) { /* already playing: unpause playback, just in case it was paused, and return */ pc_set_pause(pc, false); return PLAYLIST_RESULT_SUCCESS; } /* select a song: "current" song, or the first one */ 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 >= 0) /* "i" is currently the song position (which would be equal to the order number in no-random mode); convert it to a order number, because random mode is enabled */ i = queue_position_to_order(&playlist->queue, song); if (!playlist->playing) playlist->current = 0; /* swap the new song with the previous "current" one, so playback continues as planned */ queue_swap_order(&playlist->queue, i, playlist->current); i = playlist->current; } playlist->stop_on_error = false; playlist->error_count = 0; playlist_play_order(playlist, pc, i); return PLAYLIST_RESULT_SUCCESS; }
enum playlist_result playlist_swap_songs(struct playlist *playlist, struct player_control *pc, unsigned song1, unsigned song2) { const struct song *queued; if (!queue_valid_position(&playlist->queue, song1) || !queue_valid_position(&playlist->queue, song2)) return PLAYLIST_RESULT_BAD_RANGE; queued = playlist_get_queued_song(playlist); queue_swap(&playlist->queue, song1, song2); if (playlist->queue.random) { /* update the queue order, so that playlist->current still points to the current song order */ queue_swap_order(&playlist->queue, queue_position_to_order(&playlist->queue, song1), queue_position_to_order(&playlist->queue, song2)); } else { /* correct the "current" song order */ if (playlist->current == (int)song1) playlist->current = song2; else if (playlist->current == (int)song2) playlist->current = song1; } playlist_increment_version(playlist); playlist_update_queued_song(playlist, pc, queued); return PLAYLIST_RESULT_SUCCESS; }
void playlist_shuffle(struct playlist *playlist, struct player_control *pc, unsigned start, unsigned end) { const struct song *queued; if (end > queue_length(&playlist->queue)) /* correct the "end" offset */ end = queue_length(&playlist->queue); if ((start+1) >= end) /* needs at least two entries. */ return; queued = playlist_get_queued_song(playlist); if (playlist->playing && playlist->current >= 0) { unsigned current_position; current_position = queue_order_to_position(&playlist->queue, playlist->current); if (current_position >= start && current_position < end) { /* put current playing song first */ queue_swap(&playlist->queue, start, current_position); if (playlist->queue.random) { playlist->current = queue_position_to_order(&playlist->queue, start); } else playlist->current = start; /* start shuffle after the current song */ start++; } } else { /* no playback currently: reset playlist->current */ playlist->current = -1; } queue_shuffle_range(&playlist->queue, start, end); playlist_increment_version(playlist); playlist_update_queued_song(playlist, pc, queued); }
enum playlist_result playlist_seek_song(struct playlist *playlist, struct player_control *pc, unsigned song, float seek_time) { const struct song *queued; unsigned i; bool success; if (!queue_valid_position(&playlist->queue, song)) return PLAYLIST_RESULT_BAD_RANGE; queued = playlist_get_queued_song(playlist); if (playlist->queue.random) i = queue_position_to_order(&playlist->queue, song); else i = song; pc_clear_error(pc); playlist->stop_on_error = true; playlist->error_count = 0; if (!playlist->playing || (unsigned)playlist->current != i) { /* seeking is not within the current song - prepare song change */ playlist->playing = true; playlist->current = i; queued = NULL; } success = pc_seek(pc, queue_get_order(&playlist->queue, i), seek_time); if (!success) { playlist_update_queued_song(playlist, pc, queued); return PLAYLIST_RESULT_NOT_PLAYING; } playlist->queued = -1; playlist_update_queued_song(playlist, pc, NULL); return PLAYLIST_RESULT_SUCCESS; }
void playlist_set_random(struct playlist *playlist, struct player_control *pc, bool status) { const struct song *queued; if (status == playlist->queue.random) return; queued = playlist_get_queued_song(playlist); playlist->queue.random = status; if (playlist->queue.random) { /* shuffle the queue order, but preserve playlist->current */ int current_position = playlist->playing && playlist->current >= 0 ? (int)queue_order_to_position(&playlist->queue, playlist->current) : -1; queue_shuffle_order(&playlist->queue); if (current_position >= 0) { /* make sure the current song is the first in the order list, so the whole rest of the playlist is played after that */ unsigned current_order = queue_position_to_order(&playlist->queue, current_position); queue_swap_order(&playlist->queue, 0, current_order); playlist->current = 0; } else playlist->current = -1; } else playlist_order(playlist); playlist_update_queued_song(playlist, pc, queued); idle_add(IDLE_OPTIONS); }
enum playlist_result playlist_set_priority(struct playlist *playlist, struct player_control *pc, unsigned start, unsigned end, uint8_t priority) { if (start >= queue_length(&playlist->queue)) return PLAYLIST_RESULT_BAD_RANGE; if (end > queue_length(&playlist->queue)) end = queue_length(&playlist->queue); if (start >= end) return PLAYLIST_RESULT_SUCCESS; /* remember "current" and "queued" */ int current_position = playlist->current >= 0 ? (int)queue_order_to_position(&playlist->queue, playlist->current) : -1; const struct song *queued = playlist_get_queued_song(playlist); /* apply the priority changes */ queue_set_priority_range(&playlist->queue, start, end, priority, playlist->current); playlist_increment_version(playlist); /* restore "current" and choose a new "queued" */ if (current_position >= 0) playlist->current = queue_position_to_order(&playlist->queue, current_position); playlist_update_queued_song(playlist, pc, queued); return PLAYLIST_RESULT_SUCCESS; }
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->id_to_position[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]; }
int main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv) { struct song songs[16]; struct queue queue; queue_init(&queue, 32); for (unsigned i = 0; i < G_N_ELEMENTS(songs); ++i) queue_append(&queue, &songs[i]); assert(queue_length(&queue) == G_N_ELEMENTS(songs)); /* priority=10 for 4 items */ queue_set_priority_range(&queue, 4, 8, 10, -1); queue.random = true; queue_shuffle_order(&queue); check_descending_priority(&queue, 0); for (unsigned i = 0; i < 4; ++i) { assert(queue_position_to_order(&queue, i) >= 4); } for (unsigned i = 4; i < 8; ++i) { assert(queue_position_to_order(&queue, i) < 4); } for (unsigned i = 8; i < G_N_ELEMENTS(songs); ++i) { assert(queue_position_to_order(&queue, i) >= 4); } /* priority=50 one more item */ queue_set_priority_range(&queue, 15, 16, 50, -1); check_descending_priority(&queue, 0); assert(queue_position_to_order(&queue, 15) == 0); for (unsigned i = 0; i < 4; ++i) { assert(queue_position_to_order(&queue, i) >= 4); } for (unsigned i = 4; i < 8; ++i) { assert(queue_position_to_order(&queue, i) >= 1 && queue_position_to_order(&queue, i) < 5); } for (unsigned i = 8; i < 15; ++i) { assert(queue_position_to_order(&queue, i) >= 5); } /* priority=20 for one of the 4 priority=10 items */ queue_set_priority_range(&queue, 3, 4, 20, -1); check_descending_priority(&queue, 0); assert(queue_position_to_order(&queue, 3) == 1); assert(queue_position_to_order(&queue, 15) == 0); for (unsigned i = 0; i < 3; ++i) { assert(queue_position_to_order(&queue, i) >= 5); } for (unsigned i = 4; i < 8; ++i) { assert(queue_position_to_order(&queue, i) >= 2 && queue_position_to_order(&queue, i) < 6); } for (unsigned i = 8; i < 15; ++i) { assert(queue_position_to_order(&queue, i) >= 6); } /* priority=20 for another one of the 4 priority=10 items; pass "after_order" (with priority=10) and see if it's moved after that one */ unsigned current_order = 4; unsigned current_position = queue_order_to_position(&queue, current_order); unsigned a_order = 3; unsigned a_position = queue_order_to_position(&queue, a_order); assert(queue.items[a_position].priority == 10); queue_set_priority(&queue, a_position, 20, current_order); current_order = queue_position_to_order(&queue, current_position); assert(current_order == 3); a_order = queue_position_to_order(&queue, a_position); assert(a_order == 4); check_descending_priority(&queue, current_order + 1); /* priority=70 for one of the last items; must be inserted right after the current song, before the priority=20 one we just created */ unsigned b_order = 10; unsigned b_position = queue_order_to_position(&queue, b_order); assert(queue.items[b_position].priority == 0); queue_set_priority(&queue, b_position, 70, current_order); current_order = queue_position_to_order(&queue, current_position); assert(current_order == 3); b_order = queue_position_to_order(&queue, b_position); assert(b_order == 4); check_descending_priority(&queue, current_order + 1); /* priority=60 for the old prio50 item; must not be moved, because it's before the current song, and it's status hasn't changed (it was already higher before) */ unsigned c_order = 0; unsigned c_position = queue_order_to_position(&queue, c_order); assert(queue.items[c_position].priority == 50); queue_set_priority(&queue, c_position, 60, current_order); current_order = queue_position_to_order(&queue, current_position); assert(current_order == 3); c_order = queue_position_to_order(&queue, c_position); assert(c_order == 0); /* move the prio=20 item back */ a_order = queue_position_to_order(&queue, a_position); assert(a_order == 5); assert(queue.items[a_position].priority == 20); queue_set_priority(&queue, a_position, 5, current_order); current_order = queue_position_to_order(&queue, current_position); assert(current_order == 3); a_order = queue_position_to_order(&queue, a_position); assert(a_order == 6); }