enum playlist_result
playlist_move_range(struct playlist *playlist,
		    unsigned start, unsigned end, int to)
{
	const struct song *queued;
	int currentSong;

	if (!queue_valid_position(&playlist->queue, start) ||
		!queue_valid_position(&playlist->queue, end - 1))
		return PLAYLIST_RESULT_BAD_RANGE;

	if ((to >= 0 && to + end - start - 1 >= queue_length(&playlist->queue)) ||
	    (to < 0 && abs(to) > (int)queue_length(&playlist->queue)))
		return PLAYLIST_RESULT_BAD_RANGE;

	if ((int)start == to)
		/* nothing happens */
		return PLAYLIST_RESULT_SUCCESS;

	queued = playlist_get_queued_song(playlist);

	/*
	 * (to < 0) => move to offset from current song
	 * (-playlist.length == to) => move to position BEFORE current song
	 */
	currentSong = playlist->current >= 0
		? (int)queue_order_to_position(&playlist->queue,
					      playlist->current)
		: -1;
	if (to < 0 && playlist->current >= 0) {
		if (start <= (unsigned)currentSong && (unsigned)currentSong <= end)
			/* no-op, can't be moved to offset of itself */
			return PLAYLIST_RESULT_SUCCESS;
		to = (currentSong + abs(to)) % queue_length(&playlist->queue);
		if (start < (unsigned)to)
			to--;
	}

	queue_move_range(&playlist->queue, start, end, to);

	if (!playlist->queue.random) {
		/* update current/queued */
		if ((int)start <= playlist->current &&
		    (unsigned)playlist->current < end)
			playlist->current += to - start;
		else if (playlist->current >= (int)end &&
			 playlist->current <= to) {
			playlist->current -= end - start;
		} else if (playlist->current >= to &&
			   playlist->current < (int)start) {
			playlist->current += end - start;
		}
	}

	playlist_increment_version(playlist);

	playlist_update_queued_song(playlist, queued);

	return PLAYLIST_RESULT_SUCCESS;
}
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);
	}
}
Beispiel #3
0
int
playlist_get_next_song(const struct playlist *playlist)
{
	if (playlist->current >= 0)
	{
		if (playlist->queue.single == 1 && playlist->queue.repeat == 1)
			return queue_order_to_position(&playlist->queue,
			                               playlist->current);
		else if (playlist->current + 1 < (int)queue_length(&playlist->queue))
			return queue_order_to_position(&playlist->queue,
					       playlist->current + 1);
		else if (playlist->queue.repeat == 1)
			return queue_order_to_position(&playlist->queue, 0);
	}

	return -1;
}
Beispiel #4
0
void
playlist_state_save(FILE *fp, const struct playlist *playlist)
{
	struct player_status player_status;

	pc_get_status(&player_status);

	fputs(PLAYLIST_STATE_FILE_STATE "\n", fp);

	if (playlist->playing) {
		switch (player_status.state) {
		case PLAYER_STATE_PAUSE:
			fputs(PLAYLIST_STATE_FILE_STATE_PAUSE "\n", fp);
			break;
		default:
			fputs(PLAYLIST_STATE_FILE_STATE_PLAY "\n", fp);
		}
		fprintf(fp, PLAYLIST_STATE_FILE_CURRENT "%i\n",
			queue_order_to_position(&playlist->queue,
						playlist->current));
		fprintf(fp, PLAYLIST_STATE_FILE_TIME "%i\n",
			(int)player_status.elapsed_time);
	} else {
		fputs(PLAYLIST_STATE_FILE_STATE_STOP "\n", fp);

		if (playlist->current >= 0)
			fprintf(fp, PLAYLIST_STATE_FILE_CURRENT "%i\n",
				queue_order_to_position(&playlist->queue,
							playlist->current));
	}

	fprintf(fp, PLAYLIST_STATE_FILE_RANDOM "%i\n", playlist->queue.random);
	fprintf(fp, PLAYLIST_STATE_FILE_REPEAT "%i\n", playlist->queue.repeat);
	fprintf(fp, PLAYLIST_STATE_FILE_SINGLE "%i\n", playlist->queue.single);
	fprintf(fp, PLAYLIST_STATE_FILE_CONSUME "%i\n",
		playlist->queue.consume);
	fprintf(fp, PLAYLIST_STATE_FILE_CROSSFADE "%i\n",
		(int)(pc_get_cross_fade()));
	fprintf(fp, PLAYLIST_STATE_FILE_MIXRAMPDB "%f\n", pc_get_mixramp_db());
	fprintf(fp, PLAYLIST_STATE_FILE_MIXRAMPDELAY "%f\n",
		pc_get_mixramp_delay());
	fputs(PLAYLIST_STATE_FILE_PLAYLIST_BEGIN "\n", fp);
	queue_save(fp, &playlist->queue);
	fputs(PLAYLIST_STATE_FILE_PLAYLIST_END "\n", fp);
}
Beispiel #5
0
int
playlist_get_current_song(const struct playlist *playlist)
{
	if (playlist->current >= 0)
		return queue_order_to_position(&playlist->queue,
					       playlist->current);

	return -1;
}
Beispiel #6
0
static void
playlist_order(struct playlist *playlist)
{
	if (playlist->current >= 0)
		/* update playlist.current, order==position now */
		playlist->current = queue_order_to_position(&playlist->queue,
							    playlist->current);

	queue_restore_order(&playlist->queue);
}
Beispiel #7
0
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));
}
Beispiel #9
0
static void
check_descending_priority(G_GNUC_UNUSED const struct queue *queue,
			 unsigned start_order)
{
	assert(start_order < queue_length(queue));

	uint8_t last_priority = 0xff;
	for (unsigned order = start_order; order < queue_length(queue); ++order) {
		unsigned position = queue_order_to_position(queue, order);
		uint8_t priority = queue->items[position].priority;
		assert(priority <= last_priority);
		(void)last_priority;
		last_priority = priority;
	}
}
Beispiel #10
0
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);
}
Beispiel #11
0
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);
}
Beispiel #12
0
/**
 * Called if the player thread has started playing the "queued" song.
 */
static void
playlist_song_started(struct playlist *playlist, struct player_control *pc)
{
	assert(pc->next_song == NULL);
	assert(playlist->queued >= -1);

	/* queued song has started: copy queued to current,
	   and notify the clients */

	int current = playlist->current;
	playlist->current = playlist->queued;
	playlist->queued = -1;

	if(playlist->queue.consume)
		playlist_delete(playlist, pc,
				queue_order_to_position(&playlist->queue,
							current));

	idle_add(IDLE_PLAYER);
}
Beispiel #13
0
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;
}
Beispiel #14
0
/**
 * Called if the player thread has started playing the "queued" song.
 */
static void
playlist_song_started(struct playlist *playlist)
{
	assert(pc.next_song == NULL);
	assert(playlist->queued >= -1);

	/* queued song has started: copy queued to current,
	   and notify the clients */

	int current = playlist->current;
	playlist->current = playlist->queued;
	playlist->queued = -1;

	/* Pause if we are in single mode. */
	if(playlist->queue.single && !playlist->queue.repeat) {
		pc_set_pause(true);
	}

	if(playlist->queue.consume)
		playlist_delete(playlist, queue_order_to_position(&playlist->queue, current));

	idle_add(IDLE_PLAYER);
}
Beispiel #15
0
unsigned
playlist_state_get_hash(const struct playlist *playlist)
{
	struct player_status player_status;

	pc_get_status(&player_status);

	return playlist->queue.version ^
		(player_status.state != PLAYER_STATE_STOP
		 ? ((int)player_status.elapsed_time << 8)
		 : 0) ^
		(playlist->current >= 0
		 ? (queue_order_to_position(&playlist->queue,
					    playlist->current) << 16)
		 : 0) ^
		((int)pc_get_cross_fade() << 20) ^
		(player_status.state << 24) ^
		(playlist->queue.random << 27) ^
		(playlist->queue.repeat << 28) ^
		(playlist->queue.single << 29) ^
		(playlist->queue.consume << 30) ^
		(playlist->queue.random << 31);
}
Beispiel #16
0
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);
}