mpd/src/queue.c
Max Kellermann 710b3275ea queue: update order array after song move
Commit f78cddb4 introduced a regression: after a song was moved, the
order array was not updated (in random mode).  This caused MPD to
think the "current" song has changed when you moved something to the
position of the current song.
2009-02-09 22:47:23 +01:00

289 lines
6.2 KiB
C

/*
* 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->items[i].version = 0;
queue->version = 1;
}
}
void
queue_modify(struct queue *queue, unsigned order)
{
unsigned position;
assert(order < queue->length);
position = queue->order[order];
queue->items[position].version = queue->version;
queue_increment_version(queue);
}
void
queue_modify_all(struct queue *queue)
{
for (unsigned i = 0; i < queue->length; i++)
queue->items[i].version = 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->items[queue->length] = (struct queue_item){
.song = song,
.id = id,
.version = queue->version,
};
queue->order[queue->length] = queue->length;
queue->idToPosition[id] = queue->length;
++queue->length;
return id;
}
void
queue_swap(struct queue *queue, unsigned position1, unsigned position2)
{
struct queue_item tmp;
unsigned id1 = queue->items[position1].id;
unsigned id2 = queue->items[position2].id;
tmp = queue->items[position1];
queue->items[position1] = queue->items[position2];
queue->items[position2] = tmp;
queue->items[position1].version = queue->version;
queue->items[position2].version = queue->version;
queue->idToPosition[id1] = position2;
queue->idToPosition[id2] = position1;
}
static void
queue_move_song_to(struct queue *queue, unsigned from, unsigned to)
{
unsigned from_id = queue->items[from].id;
queue->items[to] = queue->items[from];
queue->items[to].version = queue->version;
queue->idToPosition[from_id] = to;
}
void
queue_move(struct queue *queue, unsigned from, unsigned to)
{
struct queue_item item = queue->items[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[item.id] = to;
queue->items[to] = item;
queue->items[to].version = queue->version;
/* now deal with order */
if (queue->random) {
for (unsigned i = 0; i < queue->length; i++) {
if (queue->order[i] > from && queue->order[i] <= to)
queue->order[i]--;
else if (queue->order[i] < from &&
queue->order[i] >= to)
queue->order[i]++;
else if (from == queue->order[i])
queue->order[i] = to;
}
}
}
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++) {
struct queue_item *item = &queue->items[i];
if (!song_in_database(item->song))
song_free(item->song);
queue->idToPosition[item->id] = -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->items = g_new(struct queue_item, 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);
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->items);
g_free(queue->order);
g_free(queue->idToPosition);
g_rand_free(queue->rand);
}
void
queue_shuffle_order(struct queue *queue)
{
assert(queue->random);
for (unsigned i = 0; i < queue->length; i++)
queue_swap_order(queue, i,
g_rand_int_range(queue->rand, i,
queue->length));
}
void
queue_shuffle_order_last(struct queue *queue, unsigned start, unsigned end)
{
queue_swap_order(queue, end - 1,
g_rand_int_range(queue->rand, start, end));
}
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);
}
}