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; } }
void playlist_next(struct playlist *playlist, struct player_control *pc) { int next_order; int current; if (!playlist->playing) return; assert(!queue_is_empty(&playlist->queue)); assert(queue_valid_order(&playlist->queue, playlist->current)); current = playlist->current; playlist->stop_on_error = false; /* determine the next song from the queue's order list */ next_order = queue_next_order(&playlist->queue, playlist->current); if (next_order < 0) { /* no song after this one: stop playback */ playlist_stop(playlist, pc); /* reset "current song" */ playlist->current = -1; } else { if (next_order == 0 && playlist->queue.random) { /* The queue told us that the next song is the first song. This means we are in repeat mode. Shuffle the queue order, so this time, the user hears the songs in a different than before */ assert(playlist->queue.repeat); queue_shuffle_order(&playlist->queue); /* note that playlist->current and playlist->queued are now invalid, but playlist_play_order() will discard them anyway */ } playlist_play_order(playlist, pc, next_order); } /* Consume mode removes each played songs. */ if(playlist->queue.consume) playlist_delete(playlist, pc, queue_order_to_position(&playlist->queue, current)); }
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); }
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); }