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:
parent
35a16b9923
commit
35a939e3e7
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user