player: added commands QUEUE and CANCEL

QUEUE adds a new song to the player's queue.  CANCEL clears the queue.
These two commands replace the old and complex queueState and
queueLockState code.
This commit is contained in:
Max Kellermann 2008-10-12 00:07:54 +02:00
parent 35a16b9923
commit 35a939e3e7
4 changed files with 74 additions and 155 deletions

View File

@ -33,8 +33,6 @@ void pc_init(unsigned int buffered_before_play)
pc.command = PLAYER_COMMAND_NONE; pc.command = PLAYER_COMMAND_NONE;
pc.error = PLAYER_ERROR_NOERROR; pc.error = PLAYER_ERROR_NOERROR;
pc.state = PLAYER_STATE_STOP; pc.state = PLAYER_STATE_STOP;
pc.queueState = PLAYER_QUEUE_BLANK;
pc.queueLockState = PLAYER_QUEUE_UNLOCKED;
pc.crossFade = 0; pc.crossFade = 0;
pc.softwareVolume = 1000; pc.softwareVolume = 1000;
} }
@ -57,26 +55,22 @@ void
playerPlay(struct song *song) playerPlay(struct song *song)
{ {
assert(song != NULL); assert(song != NULL);
assert(pc.queueLockState == PLAYER_QUEUE_UNLOCKED);
if (pc.state != PLAYER_STATE_STOP) if (pc.state != PLAYER_STATE_STOP)
player_command(PLAYER_COMMAND_STOP); player_command(PLAYER_COMMAND_STOP);
pc.queueState = PLAYER_QUEUE_BLANK;
pc.next_song = song; pc.next_song = song;
player_command(PLAYER_COMMAND_PLAY); player_command(PLAYER_COMMAND_PLAY);
} }
void pc_cancel(void)
{
player_command(PLAYER_COMMAND_CANCEL);
}
void playerWait(void) void playerWait(void)
{ {
player_command(PLAYER_COMMAND_CLOSE_AUDIO); player_command(PLAYER_COMMAND_CLOSE_AUDIO);
assert(pc.queueLockState == PLAYER_QUEUE_UNLOCKED);
player_command(PLAYER_COMMAND_CLOSE_AUDIO);
pc.queueState = PLAYER_QUEUE_BLANK;
} }
void playerKill(void) void playerKill(void)
@ -172,36 +166,10 @@ void
queueSong(struct song *song) queueSong(struct song *song)
{ {
assert(song != NULL); assert(song != NULL);
assert(pc.queueState == PLAYER_QUEUE_BLANK); assert(pc.next_song == NULL);
pc.next_song = song; pc.next_song = song;
pc.queueState = PLAYER_QUEUE_FULL; player_command(PLAYER_COMMAND_QUEUE);
}
enum player_queue_state getPlayerQueueState(void)
{
return pc.queueState;
}
void setQueueState(enum player_queue_state queueState)
{
pc.queueState = queueState;
notify_signal(&pc.notify);
}
void playerQueueLock(void)
{
assert(pc.queueLockState == PLAYER_QUEUE_UNLOCKED);
player_command(PLAYER_COMMAND_LOCK_QUEUE);
assert(pc.queueLockState == PLAYER_QUEUE_LOCKED);
}
void playerQueueUnlock(void)
{
if (pc.queueLockState == PLAYER_QUEUE_LOCKED)
player_command(PLAYER_COMMAND_UNLOCK_QUEUE);
assert(pc.queueLockState == PLAYER_QUEUE_UNLOCKED);
} }
int int

View File

@ -38,8 +38,16 @@ enum player_command {
PLAYER_COMMAND_PAUSE, PLAYER_COMMAND_PAUSE,
PLAYER_COMMAND_SEEK, PLAYER_COMMAND_SEEK,
PLAYER_COMMAND_CLOSE_AUDIO, PLAYER_COMMAND_CLOSE_AUDIO,
PLAYER_COMMAND_LOCK_QUEUE,
PLAYER_COMMAND_UNLOCK_QUEUE /** player_control.next_song has been updated */
PLAYER_COMMAND_QUEUE,
/**
* cancel pre-decoding player_control.next_song; if the player
* has already started playing this song, it will completely
* stop
*/
PLAYER_COMMAND_CANCEL,
}; };
#define PLAYER_ERROR_NOERROR 0 #define PLAYER_ERROR_NOERROR 0
@ -49,36 +57,6 @@ enum player_command {
#define PLAYER_ERROR_UNKTYPE 4 #define PLAYER_ERROR_UNKTYPE 4
#define PLAYER_ERROR_FILENOTFOUND 5 #define PLAYER_ERROR_FILENOTFOUND 5
/* 0->1->2->3->5 regular playback
* ->4->0 don't play queued song
*/
enum player_queue_state {
/** there is no queued song */
PLAYER_QUEUE_BLANK = 0,
/** there is a queued song */
PLAYER_QUEUE_FULL = 1,
/** the player thread has forwarded the queued song to the
decoder; it waits for PLAY or STOP */
PLAYER_QUEUE_DECODE = 2,
/** tells the player thread to start playing the queued song;
this is a response to DECODE */
PLAYER_QUEUE_PLAY = 3,
/** tells the player thread to stop before playing the queued
song; this is a response to DECODE */
PLAYER_QUEUE_STOP = 4,
/** the player thread has begun playing the queued song, and
thus its queue is empty */
PLAYER_QUEUE_EMPTY = 5
};
#define PLAYER_QUEUE_UNLOCKED 0
#define PLAYER_QUEUE_LOCKED 1
struct player_control { struct player_control {
unsigned int buffered_before_play; unsigned int buffered_before_play;
@ -92,8 +70,6 @@ struct player_control {
volatile float elapsedTime; volatile float elapsedTime;
struct song *volatile next_song; struct song *volatile next_song;
struct song *errored_song; struct song *errored_song;
volatile enum player_queue_state queueState;
volatile int8_t queueLockState;
volatile double seekWhere; volatile double seekWhere;
volatile float crossFade; volatile float crossFade;
volatile uint16_t softwareVolume; volatile uint16_t softwareVolume;
@ -109,6 +85,11 @@ void pc_deinit(void);
void void
playerPlay(struct song *song); playerPlay(struct song *song);
/**
* see PLAYER_COMMAND_CANCEL
*/
void pc_cancel(void);
void playerSetPause(int pause_flag); void playerSetPause(int pause_flag);
void playerPause(void); void playerPause(void);
@ -134,14 +115,6 @@ void playerWait(void);
void void
queueSong(struct song *song); queueSong(struct song *song);
enum player_queue_state getPlayerQueueState(void);
void setQueueState(enum player_queue_state queueState);
void playerQueueLock(void);
void playerQueueUnlock(void);
int int
playerSeek(struct song *song, float seek_time); playerSeek(struct song *song, float seek_time);

View File

@ -51,6 +51,11 @@ struct player {
*/ */
bool paused; bool paused;
/**
* is there a new song in pc.next_song?
*/
bool queued;
/** /**
* is cross fading enabled? * is cross fading enabled?
*/ */
@ -94,6 +99,9 @@ static int waitOnDecode(struct player *player)
? pc.next_song->tag->time : 0; ? pc.next_song->tag->time : 0;
pc.bitRate = 0; pc.bitRate = 0;
audio_format_clear(&pc.audio_format); audio_format_clear(&pc.audio_format);
pc.next_song = NULL;
player->queued = false;
player->decoder_starting = true; player->decoder_starting = true;
return 0; return 0;
@ -137,13 +145,9 @@ static void processDecodeInput(struct player *player)
case PLAYER_COMMAND_CLOSE_AUDIO: case PLAYER_COMMAND_CLOSE_AUDIO:
break; break;
case PLAYER_COMMAND_LOCK_QUEUE: case PLAYER_COMMAND_QUEUE:
pc.queueLockState = PLAYER_QUEUE_LOCKED; assert(pc.next_song != NULL);
player_command_finished(); player->queued = true;
break;
case PLAYER_COMMAND_UNLOCK_QUEUE:
pc.queueLockState = PLAYER_QUEUE_UNLOCKED;
player_command_finished(); player_command_finished();
break; break;
@ -177,6 +181,27 @@ static void processDecodeInput(struct player *player)
player->buffered_before_play = 0; player->buffered_before_play = 0;
} }
break; break;
case PLAYER_COMMAND_CANCEL:
if (pc.next_song == NULL) {
/* the cancel request arrived too later, we're
already playing the queued song... stop
everything now */
pc.command = PLAYER_COMMAND_STOP;
return;
}
if (player->next_song_chunk != -1) {
/* the decoder is already decoding the song -
stop it and reset the position */
dc_stop(&pc.notify);
player->next_song_chunk = -1;
}
pc.next_song = NULL;
player->queued = false;
player_command_finished();
break;
} }
} }
@ -203,6 +228,7 @@ static void do_play(void)
.buffered_before_play = pc.buffered_before_play, .buffered_before_play = pc.buffered_before_play,
.decoder_starting = false, .decoder_starting = false,
.paused = false, .paused = false,
.queued = false,
.xfade = XFADE_UNKNOWN, .xfade = XFADE_UNKNOWN,
.next_song_chunk = -1, .next_song_chunk = -1,
}; };
@ -286,15 +312,22 @@ static void do_play(void)
} }
} }
if (decoder_is_idle() && if (decoder_is_idle() && !player.queued &&
pc.queueState == PLAYER_QUEUE_FULL && pc.next_song != NULL) {
pc.queueLockState == PLAYER_QUEUE_UNLOCKED) { /* the decoder has finished the current song;
request the next song from the playlist */
pc.next_song = NULL;
wakeup_main_task();
}
if (decoder_is_idle() && player.queued) {
/* the decoder has finished the current song; /* the decoder has finished the current song;
make it decode the next song */ make it decode the next song */
assert(pc.next_song != NULL);
player.queued = false;
player.next_song_chunk = ob.end; player.next_song_chunk = ob.end;
dc_start_async(pc.next_song); dc_start_async(pc.next_song);
pc.queueState = PLAYER_QUEUE_DECODE;
wakeup_main_task();
} }
if (player.next_song_chunk >= 0 && if (player.next_song_chunk >= 0 &&
player.xfade == XFADE_UNKNOWN && player.xfade == XFADE_UNKNOWN &&
@ -386,20 +419,10 @@ static void do_play(void)
player.xfade = XFADE_UNKNOWN; player.xfade = XFADE_UNKNOWN;
/* wait for a signal from the playlist */
if (pc.queueState == PLAYER_QUEUE_DECODE ||
pc.queueLockState == PLAYER_QUEUE_LOCKED) {
notify_wait(&pc.notify);
continue;
}
if (pc.queueState != PLAYER_QUEUE_PLAY)
break;
player.next_song_chunk = -1; player.next_song_chunk = -1;
if (waitOnDecode(&player) < 0) if (waitOnDecode(&player) < 0)
return; return;
pc.queueState = PLAYER_QUEUE_EMPTY;
wakeup_main_task(); wakeup_main_task();
} else if (decoder_is_idle()) { } else if (decoder_is_idle()) {
break; break;
@ -418,6 +441,7 @@ static void * player_task(mpd_unused void *arg)
while (1) { while (1) {
switch (pc.command) { switch (pc.command) {
case PLAYER_COMMAND_PLAY: case PLAYER_COMMAND_PLAY:
case PLAYER_COMMAND_QUEUE:
do_play(); do_play();
break; break;
@ -438,13 +462,8 @@ static void * player_task(mpd_unused void *arg)
pthread_exit(NULL); pthread_exit(NULL);
break; break;
case PLAYER_COMMAND_LOCK_QUEUE: case PLAYER_COMMAND_CANCEL:
pc.queueLockState = PLAYER_QUEUE_LOCKED; pc.next_song = NULL;
player_command_finished();
break;
case PLAYER_COMMAND_UNLOCK_QUEUE:
pc.queueLockState = PLAYER_QUEUE_UNLOCKED;
player_command_finished(); player_command_finished();
break; break;

View File

@ -499,28 +499,9 @@ static void queueNextSongInPlaylist(void)
static void syncPlaylistWithQueue(void) static void syncPlaylistWithQueue(void)
{ {
switch (getPlayerQueueState()) { if (pc.next_song == NULL && playlist.queued != -1) {
case PLAYER_QUEUE_EMPTY: playlist.current = playlist.queued;
setQueueState(PLAYER_QUEUE_BLANK);
if (playlist.queued >= 0) {
DEBUG("playlist: now playing queued song\n");
playlist.current = playlist.queued;
}
playlist.queued = -1; playlist.queued = -1;
break;
case PLAYER_QUEUE_DECODE:
if (playlist.queued != -1)
setQueueState(PLAYER_QUEUE_PLAY);
else
setQueueState(PLAYER_QUEUE_STOP);
break;
case PLAYER_QUEUE_FULL:
case PLAYER_QUEUE_PLAY:
case PLAYER_QUEUE_STOP:
case PLAYER_QUEUE_BLANK:
break;
} }
} }
@ -528,31 +509,9 @@ static void clearPlayerQueue(void)
{ {
assert(playlist.queued >= 0); assert(playlist.queued >= 0);
if (getPlayerQueueState() == PLAYER_QUEUE_PLAY ||
getPlayerQueueState() == PLAYER_QUEUE_FULL) {
playerQueueLock();
syncPlaylistWithQueue();
}
playlist.queued = -1; playlist.queued = -1;
switch (getPlayerQueueState()) {
case PLAYER_QUEUE_BLANK:
case PLAYER_QUEUE_DECODE:
case PLAYER_QUEUE_STOP:
case PLAYER_QUEUE_EMPTY:
break;
case PLAYER_QUEUE_FULL: pc_cancel();
DEBUG("playlist: dequeue song\n");
setQueueState(PLAYER_QUEUE_BLANK);
break;
case PLAYER_QUEUE_PLAY:
DEBUG("playlist: stop decoding queued song\n");
setQueueState(PLAYER_QUEUE_STOP);
break;
}
playerQueueUnlock();
} }
enum playlist_result addToPlaylist(const char *url, int *added_id) enum playlist_result addToPlaylist(const char *url, int *added_id)
@ -907,7 +866,7 @@ void syncPlayerAndPlaylist(void)
playPlaylistIfPlayerStopped(); playPlaylistIfPlayerStopped();
else { else {
syncPlaylistWithQueue(); syncPlaylistWithQueue();
if (getPlayerQueueState() == PLAYER_QUEUE_BLANK) if (pc.next_song == NULL)
queueNextSongInPlaylist(); queueNextSongInPlaylist();
} }