diff options
author | Max Kellermann <max@duempel.org> | 2008-08-26 08:27:09 +0200 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2008-08-26 08:27:09 +0200 |
commit | e8bd9ddc9fc99d31659db1121622bef0f3303fc1 (patch) | |
tree | a9fea2466a207ca957fe47c6a0881e957ba2aad5 | |
parent | 6104e9690e75046b793c0abe6fc20576fb3da19b (diff) | |
download | mpd-e8bd9ddc9fc99d31659db1121622bef0f3303fc1.tar.gz mpd-e8bd9ddc9fc99d31659db1121622bef0f3303fc1.tar.xz mpd-e8bd9ddc9fc99d31659db1121622bef0f3303fc1.zip |
moved code to player_thread.c
Move code which runs in the player thread to player_thread.c. Having
a lot of player thread code in decode.c isn't easy to understand.
Diffstat (limited to '')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/decode.c | 394 | ||||
-rw-r--r-- | src/decode.h | 2 | ||||
-rw-r--r-- | src/main.c | 4 | ||||
-rw-r--r-- | src/player.c | 53 | ||||
-rw-r--r-- | src/player_thread.c | 466 | ||||
-rw-r--r-- | src/player_thread.h | 24 |
7 files changed, 495 insertions, 450 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index b097e904c..006ccf214 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -68,6 +68,7 @@ mpd_headers = \ pcm_utils.h \ permission.h \ player.h \ + player_thread.h \ playerData.h \ playlist.h \ replayGain.h \ @@ -128,6 +129,7 @@ mpd_SOURCES = \ pcm_utils.c \ permission.c \ player.c \ + player_thread.c \ playerData.c \ playlist.c \ replayGain.c \ diff --git a/src/decode.c b/src/decode.c index 8d929d0fb..92f01a393 100644 --- a/src/decode.c +++ b/src/decode.c @@ -19,166 +19,10 @@ #include "decode.h" #include "decoder_internal.h" -#include "player.h" #include "playerData.h" -#include "pcm_utils.h" #include "path.h" #include "log.h" #include "ls.h" -#include "main_notify.h" -#include "audio.h" -#include "crossfade.h" - -enum xfade_state { - XFADE_DISABLED = -1, - XFADE_UNKNOWN = 0, - XFADE_ENABLED = 1 -}; - -static void dc_command_wait(void) -{ - while (dc.command != DECODE_COMMAND_NONE) { - notify_signal(&dc.notify); - notify_wait(&pc.notify); - } -} - -static void dc_command(enum decoder_command cmd) -{ - dc.command = cmd; - dc_command_wait(); -} - -static void stopDecode(void) -{ - if (dc.command == DECODE_COMMAND_START || - dc.state != DECODE_STATE_STOP) - dc_command(DECODE_COMMAND_STOP); -} - -static void quitDecode(void) -{ - stopDecode(); - pc.state = PLAYER_STATE_STOP; - dc.command = DECODE_COMMAND_NONE; - pc.command = PLAYER_COMMAND_NONE; - wakeup_main_task(); -} - -static int waitOnDecode(int *decodeWaitedOn) -{ - while (dc.command == DECODE_COMMAND_START) { - notify_signal(&dc.notify); - notify_wait(&pc.notify); - } - - if (dc.error != DECODE_ERROR_NOERROR) { - pc.errored_song = dc.next_song; - pc.error = PLAYER_ERROR_FILE; - quitDecode(); - return -1; - } - - pc.totalTime = pc.fileTime; - pc.bitRate = 0; - pc.sampleRate = 0; - pc.bits = 0; - pc.channels = 0; - *decodeWaitedOn = 1; - - return 0; -} - -static int decodeSeek(int *decodeWaitedOn, int *next) -{ - int ret = -1; - - if (dc.state == DECODE_STATE_STOP || - dc.error != DECODE_ERROR_NOERROR || - dc.current_song != pc.next_song) { - stopDecode(); - *next = -1; - ob_clear(); - dc.next_song = pc.next_song; - dc.error = DECODE_ERROR_NOERROR; - dc.command = DECODE_COMMAND_START; - waitOnDecode(decodeWaitedOn); - } - if (dc.state != DECODE_STATE_STOP && dc.seekable) { - *next = -1; - dc.seekWhere = pc.seekWhere > pc.totalTime - 0.1 ? - pc.totalTime - 0.1 : pc.seekWhere; - dc.seekWhere = 0 > dc.seekWhere ? 0 : dc.seekWhere; - dc.seekError = 0; - dc_command(DECODE_COMMAND_SEEK); - if (!dc.seekError) { - pc.elapsedTime = dc.seekWhere; - ret = 0; - } - } - - player_command_finished(); - - return ret; -} - -static void processDecodeInput(int *pause_r, unsigned int *bbp_r, - enum xfade_state *do_xfade_r, - int *decodeWaitedOn_r, - int *next_r) -{ - switch (pc.command) { - case PLAYER_COMMAND_NONE: - case PLAYER_COMMAND_PLAY: - case PLAYER_COMMAND_STOP: - case PLAYER_COMMAND_CLOSE_AUDIO: - break; - - case PLAYER_COMMAND_LOCK_QUEUE: - pc.queueLockState = PLAYER_QUEUE_LOCKED; - player_command_finished(); - break; - - case PLAYER_COMMAND_UNLOCK_QUEUE: - pc.queueLockState = PLAYER_QUEUE_UNLOCKED; - player_command_finished(); - break; - - case PLAYER_COMMAND_PAUSE: - *pause_r = !*pause_r; - if (*pause_r) { - pc.state = PLAYER_STATE_PAUSE; - } else { - if (openAudioDevice(NULL) >= 0) { - pc.state = PLAYER_STATE_PLAY; - } else { - char tmp[MPD_PATH_MAX]; - pc.errored_song = dc.next_song; - pc.error = PLAYER_ERROR_AUDIO; - ERROR("problems opening audio device " - "while playing \"%s\"\n", - get_song_url(tmp, dc.next_song)); - *pause_r = -1; - } - } - player_command_finished(); - if (*pause_r == -1) { - *pause_r = 1; - } else if (*pause_r) { - dropBufferedAudio(); - closeAudioDevice(); - } - break; - - case PLAYER_COMMAND_SEEK: - dropBufferedAudio(); - if (decodeSeek(decodeWaitedOn_r, next_r) == 0) { - *do_xfade_r = XFADE_UNKNOWN; - *bbp_r = 0; - } - break; - } -} static void decodeStart(void) { @@ -337,241 +181,3 @@ void decoderInit(void) if (pthread_create(&decoder_thread, &attr, decoder_task, NULL)) FATAL("Failed to spawn decoder task: %s\n", strerror(errno)); } - -static int playChunk(ob_chunk * chunk, - const AudioFormat * format, double sizeToTime) -{ - pc.elapsedTime = chunk->times; - pc.bitRate = chunk->bitRate; - - pcm_volumeChange(chunk->data, chunk->chunkSize, - format, pc.softwareVolume); - - if (playAudio(chunk->data, - chunk->chunkSize) < 0) - return -1; - - pc.totalPlayTime += sizeToTime * chunk->chunkSize; - return 0; -} - -static void decodeParent(void) -{ - int do_pause = 0; - int buffering = 1; - unsigned int bbp = buffered_before_play; - enum xfade_state do_xfade = XFADE_UNKNOWN; - unsigned int crossFadeChunks = 0; - /** the position of the next cross-faded chunk in the next - song */ - int nextChunk = 0; - int decodeWaitedOn = 0; - static const char silence[CHUNK_SIZE]; - double sizeToTime = 0.0; - /** the position of the first chunk in the next song */ - int next = -1; - - ob_set_lazy(0); - - if (waitOnDecode(&decodeWaitedOn) < 0) - return; - - pc.elapsedTime = 0; - pc.state = PLAYER_STATE_PLAY; - player_command_finished(); - - while (1) { - processDecodeInput(&do_pause, &bbp, &do_xfade, - &decodeWaitedOn, &next); - if (pc.command == PLAYER_COMMAND_STOP) { - dropBufferedAudio(); - break; - } - - if (buffering) { - if (ob_available() < bbp) { - /* not enough decoded buffer space yet */ - notify_wait(&pc.notify); - continue; - } else { - /* buffering is complete */ - buffering = 0; - ob_set_lazy(1); - } - } - - if (decodeWaitedOn) { - if(dc.state!=DECODE_STATE_START && - dc.error==DECODE_ERROR_NOERROR) { - /* the decoder is ready and ok */ - decodeWaitedOn = 0; - if(openAudioDevice(&(ob.audioFormat))<0) { - char tmp[MPD_PATH_MAX]; - pc.errored_song = dc.next_song; - pc.error = PLAYER_ERROR_AUDIO; - ERROR("problems opening audio device " - "while playing \"%s\"\n", - get_song_url(tmp, dc.next_song)); - break; - } - - if (do_pause) { - dropBufferedAudio(); - closeAudioDevice(); - } - pc.totalTime = dc.totalTime; - pc.sampleRate = dc.audioFormat.sampleRate; - pc.bits = dc.audioFormat.bits; - pc.channels = dc.audioFormat.channels; - sizeToTime = audioFormatSizeToTime(&ob.audioFormat); - } - else if(dc.state!=DECODE_STATE_START) { - /* the decoder failed */ - pc.errored_song = dc.next_song; - pc.error = PLAYER_ERROR_FILE; - break; - } - else { - /* the decoder is not yet ready; wait - some more */ - notify_wait(&pc.notify); - continue; - } - } - - if (dc.state == DECODE_STATE_STOP && - pc.queueState == PLAYER_QUEUE_FULL && - pc.queueLockState == PLAYER_QUEUE_UNLOCKED) { - /* the decoder has finished the current song; - make it decode the next song */ - next = ob.end; - dc.next_song = pc.next_song; - dc.error = DECODE_ERROR_NOERROR; - dc.command = DECODE_COMMAND_START; - pc.queueState = PLAYER_QUEUE_DECODE; - wakeup_main_task(); - notify_signal(&dc.notify); - } - if (next >= 0 && do_xfade == XFADE_UNKNOWN && - dc.command != DECODE_COMMAND_START && - dc.state != DECODE_STATE_START) { - /* enable cross fading in this song? if yes, - calculate how many chunks will be required - for it */ - crossFadeChunks = - cross_fade_calc(pc.crossFade, dc.totalTime, - &(ob.audioFormat), - ob.size - - buffered_before_play); - if (crossFadeChunks > 0) { - do_xfade = XFADE_ENABLED; - nextChunk = -1; - } else - /* cross fading is disabled or the - next song is too short */ - do_xfade = XFADE_DISABLED; - } - - if (do_pause) - notify_wait(&pc.notify); - else if (!ob_is_empty() && (int)ob.begin != next) { - ob_chunk *beginChunk = ob_get_chunk(ob.begin); - unsigned int fadePosition; - if (do_xfade == XFADE_ENABLED && next >= 0 && - (fadePosition = ob_relative(next)) - <= crossFadeChunks) { - /* perform cross fade */ - if (nextChunk < 0) { - /* beginning of the cross fade - - adjust crossFadeChunks - which might be bigger than - the remaining number of - chunks in the old song */ - crossFadeChunks = fadePosition; - } - nextChunk = ob_absolute(crossFadeChunks); - if (nextChunk >= 0) { - ob_set_lazy(1); - cross_fade_apply(beginChunk, - ob_get_chunk(nextChunk), - &(ob.audioFormat), - fadePosition, - crossFadeChunks); - } else { - /* there are not enough - decoded chunks yet */ - if (dc.state == DECODE_STATE_STOP) { - /* the decoder isn't - running, abort - cross fading */ - do_xfade = XFADE_DISABLED; - } else { - /* wait for the - decoder */ - ob_set_lazy(0); - notify_wait(&pc.notify); - continue; - } - } - } - - /* play the current chunk */ - if (playChunk(beginChunk, &(ob.audioFormat), - sizeToTime) < 0) - break; - ob_shift(); - notify_signal(&dc.notify); - } else if (!ob_is_empty() && (int)ob.begin == next) { - /* at the beginning of a new song */ - - if (do_xfade == XFADE_ENABLED && nextChunk >= 0) { - /* the cross-fade is finished; skip - the section which was cross-faded - (and thus already played) */ - ob_skip(crossFadeChunks); - } - - do_xfade = XFADE_UNKNOWN; - - /* wait for the decoder to work on the new song */ - if (pc.queueState == PLAYER_QUEUE_DECODE || - pc.queueLockState == PLAYER_QUEUE_LOCKED) { - notify_wait(&pc.notify); - continue; - } - if (pc.queueState != PLAYER_QUEUE_PLAY) - break; - - next = -1; - if (waitOnDecode(&decodeWaitedOn) < 0) - return; - - pc.queueState = PLAYER_QUEUE_EMPTY; - wakeup_main_task(); - } else if (dc.state == DECODE_STATE_STOP && - dc.command != DECODE_COMMAND_START) { - break; - } else { - /*DEBUG("waiting for decoded audio, play silence\n");*/ - if (playAudio(silence, CHUNK_SIZE) < 0) - break; - } - } - - quitDecode(); -} - -/* decode w/ buffering - * this will fork another process - * child process does decoding - * parent process does playing audio - */ -void decode(void) -{ - ob_clear(); - dc.next_song = pc.next_song; - dc.error = DECODE_ERROR_NOERROR; - dc_command(DECODE_COMMAND_START); - - decodeParent(); -} diff --git a/src/decode.h b/src/decode.h index fbee97c82..29e25392f 100644 --- a/src/decode.h +++ b/src/decode.h @@ -59,8 +59,6 @@ typedef struct _DecoderControl { volatile float totalTime; } DecoderControl; -void decode(void); - void decoderInit(void); #endif diff --git a/src/main.c b/src/main.c index 7f66fd6bd..53b204e7e 100644 --- a/src/main.c +++ b/src/main.c @@ -20,7 +20,7 @@ #include "command.h" #include "playlist.h" #include "directory.h" -#include "player.h" +#include "player_thread.h" #include "listen.h" #include "conf.h" #include "path.h" @@ -432,7 +432,7 @@ int main(int argc, char *argv[]) openVolumeDevice(); decoderInit(); - playerInit(); + player_create(); read_state_file(); while (COMMAND_RETURN_KILL != doIOForInterfaces() && diff --git a/src/player.c b/src/player.c index 08ad94096..c2e84c1bd 100644 --- a/src/player.c +++ b/src/player.c @@ -17,67 +17,16 @@ */ #include "player.h" +#include "player_thread.h" #include "path.h" #include "command.h" -#include "log.h" #include "playerData.h" #include "ack.h" #include "os_compat.h" #include "main_notify.h" -#include "audio.h" static void playerCloseAudio(void); -static void * player_task(mpd_unused void *arg) -{ - notify_enter(&pc.notify); - - while (1) { - switch (pc.command) { - case PLAYER_COMMAND_PLAY: - decode(); - break; - - case PLAYER_COMMAND_STOP: - case PLAYER_COMMAND_SEEK: - case PLAYER_COMMAND_PAUSE: - player_command_finished(); - break; - - case PLAYER_COMMAND_CLOSE_AUDIO: - closeAudioDevice(); - player_command_finished(); - break; - - case PLAYER_COMMAND_LOCK_QUEUE: - pc.queueLockState = PLAYER_QUEUE_LOCKED; - player_command_finished(); - break; - - case PLAYER_COMMAND_UNLOCK_QUEUE: - pc.queueLockState = PLAYER_QUEUE_UNLOCKED; - player_command_finished(); - break; - - case PLAYER_COMMAND_NONE: - notify_wait(&pc.notify); - break; - } - } - return NULL; -} - -void playerInit(void) -{ - pthread_attr_t attr; - pthread_t player_thread; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - if (pthread_create(&player_thread, &attr, player_task, NULL)) - FATAL("Failed to spawn player task: %s\n", strerror(errno)); -} - int playerWait(int fd) { if (playerStop(fd) < 0) diff --git a/src/player_thread.c b/src/player_thread.c new file mode 100644 index 000000000..0937fb3ca --- /dev/null +++ b/src/player_thread.c @@ -0,0 +1,466 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com) + * This project's homepage is: 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 "player_thread.h" +#include "playerData.h" +#include "audio.h" +#include "pcm_utils.h" +#include "path.h" +#include "log.h" +#include "main_notify.h" +#include "crossfade.h" + +enum xfade_state { + XFADE_DISABLED = -1, + XFADE_UNKNOWN = 0, + XFADE_ENABLED = 1 +}; + +static void dc_command_wait(void) +{ + while (dc.command != DECODE_COMMAND_NONE) { + notify_signal(&dc.notify); + notify_wait(&pc.notify); + } +} + +static void dc_command(enum decoder_command cmd) +{ + dc.command = cmd; + dc_command_wait(); +} + +static void stopDecode(void) +{ + if (dc.command == DECODE_COMMAND_START || + dc.state != DECODE_STATE_STOP) + dc_command(DECODE_COMMAND_STOP); +} + +static void quitDecode(void) +{ + stopDecode(); + pc.state = PLAYER_STATE_STOP; + dc.command = DECODE_COMMAND_NONE; + pc.command = PLAYER_COMMAND_NONE; + wakeup_main_task(); +} + +static int waitOnDecode(int *decodeWaitedOn) +{ + while (dc.command == DECODE_COMMAND_START) { + notify_signal(&dc.notify); + notify_wait(&pc.notify); + } + + if (dc.error != DECODE_ERROR_NOERROR) { + pc.errored_song = dc.next_song; + pc.error = PLAYER_ERROR_FILE; + quitDecode(); + return -1; + } + + pc.totalTime = pc.fileTime; + pc.bitRate = 0; + pc.sampleRate = 0; + pc.bits = 0; + pc.channels = 0; + *decodeWaitedOn = 1; + + return 0; +} + +static int decodeSeek(int *decodeWaitedOn, int *next) +{ + int ret = -1; + + if (dc.state == DECODE_STATE_STOP || + dc.error != DECODE_ERROR_NOERROR || + dc.current_song != pc.next_song) { + stopDecode(); + *next = -1; + ob_clear(); + dc.next_song = pc.next_song; + dc.error = DECODE_ERROR_NOERROR; + dc.command = DECODE_COMMAND_START; + waitOnDecode(decodeWaitedOn); + } + if (dc.state != DECODE_STATE_STOP && dc.seekable) { + *next = -1; + dc.seekWhere = pc.seekWhere > pc.totalTime - 0.1 ? + pc.totalTime - 0.1 : pc.seekWhere; + dc.seekWhere = 0 > dc.seekWhere ? 0 : dc.seekWhere; + dc.seekError = 0; + dc_command(DECODE_COMMAND_SEEK); + if (!dc.seekError) { + pc.elapsedTime = dc.seekWhere; + ret = 0; + } + } + + player_command_finished(); + + return ret; +} + +static void processDecodeInput(int *pause_r, unsigned int *bbp_r, + enum xfade_state *do_xfade_r, + int *decodeWaitedOn_r, + int *next_r) +{ + switch (pc.command) { + case PLAYER_COMMAND_NONE: + case PLAYER_COMMAND_PLAY: + case PLAYER_COMMAND_STOP: + case PLAYER_COMMAND_CLOSE_AUDIO: + break; + + case PLAYER_COMMAND_LOCK_QUEUE: + pc.queueLockState = PLAYER_QUEUE_LOCKED; + player_command_finished(); + break; + + case PLAYER_COMMAND_UNLOCK_QUEUE: + pc.queueLockState = PLAYER_QUEUE_UNLOCKED; + player_command_finished(); + break; + + case PLAYER_COMMAND_PAUSE: + *pause_r = !*pause_r; + if (*pause_r) { + pc.state = PLAYER_STATE_PAUSE; + } else { + if (openAudioDevice(NULL) >= 0) { + pc.state = PLAYER_STATE_PLAY; + } else { + char tmp[MPD_PATH_MAX]; + pc.errored_song = dc.next_song; + pc.error = PLAYER_ERROR_AUDIO; + ERROR("problems opening audio device " + "while playing \"%s\"\n", + get_song_url(tmp, dc.next_song)); + *pause_r = -1; + } + } + player_command_finished(); + if (*pause_r == -1) { + *pause_r = 1; + } else if (*pause_r) { + dropBufferedAudio(); + closeAudioDevice(); + } + break; + + case PLAYER_COMMAND_SEEK: + dropBufferedAudio(); + if (decodeSeek(decodeWaitedOn_r, next_r) == 0) { + *do_xfade_r = XFADE_UNKNOWN; + *bbp_r = 0; + } + break; + } +} + +static int playChunk(ob_chunk * chunk, + const AudioFormat * format, double sizeToTime) +{ + pc.elapsedTime = chunk->times; + pc.bitRate = chunk->bitRate; + + pcm_volumeChange(chunk->data, chunk->chunkSize, + format, pc.softwareVolume); + + if (playAudio(chunk->data, + chunk->chunkSize) < 0) + return -1; + + pc.totalPlayTime += sizeToTime * chunk->chunkSize; + return 0; +} + +static void decodeParent(void) +{ + int do_pause = 0; + int buffering = 1; + unsigned int bbp = buffered_before_play; + enum xfade_state do_xfade = XFADE_UNKNOWN; + unsigned int crossFadeChunks = 0; + /** the position of the next cross-faded chunk in the next + song */ + int nextChunk = 0; + int decodeWaitedOn = 0; + static const char silence[CHUNK_SIZE]; + double sizeToTime = 0.0; + /** the position of the first chunk in the next song */ + int next = -1; + + ob_set_lazy(0); + + if (waitOnDecode(&decodeWaitedOn) < 0) + return; + + pc.elapsedTime = 0; + pc.state = PLAYER_STATE_PLAY; + player_command_finished(); + + while (1) { + processDecodeInput(&do_pause, &bbp, &do_xfade, + &decodeWaitedOn, &next); + if (pc.command == PLAYER_COMMAND_STOP) { + dropBufferedAudio(); + break; + } + + if (buffering) { + if (ob_available() < bbp) { + /* not enough decoded buffer space yet */ + notify_wait(&pc.notify); + continue; + } else { + /* buffering is complete */ + buffering = 0; + ob_set_lazy(1); + } + } + + if (decodeWaitedOn) { + if(dc.state!=DECODE_STATE_START && + dc.error==DECODE_ERROR_NOERROR) { + /* the decoder is ready and ok */ + decodeWaitedOn = 0; + if(openAudioDevice(&(ob.audioFormat))<0) { + char tmp[MPD_PATH_MAX]; + pc.errored_song = dc.next_song; + pc.error = PLAYER_ERROR_AUDIO; + ERROR("problems opening audio device " + "while playing \"%s\"\n", + get_song_url(tmp, dc.next_song)); + break; + } + + if (do_pause) { + dropBufferedAudio(); + closeAudioDevice(); + } + pc.totalTime = dc.totalTime; + pc.sampleRate = dc.audioFormat.sampleRate; + pc.bits = dc.audioFormat.bits; + pc.channels = dc.audioFormat.channels; + sizeToTime = audioFormatSizeToTime(&ob.audioFormat); + } + else if(dc.state!=DECODE_STATE_START) { + /* the decoder failed */ + pc.errored_song = dc.next_song; + pc.error = PLAYER_ERROR_FILE; + break; + } + else { + /* the decoder is not yet ready; wait + some more */ + notify_wait(&pc.notify); + continue; + } + } + + if (dc.state == DECODE_STATE_STOP && + pc.queueState == PLAYER_QUEUE_FULL && + pc.queueLockState == PLAYER_QUEUE_UNLOCKED) { + /* the decoder has finished the current song; + make it decode the next song */ + next = ob.end; + dc.next_song = pc.next_song; + dc.error = DECODE_ERROR_NOERROR; + dc.command = DECODE_COMMAND_START; + pc.queueState = PLAYER_QUEUE_DECODE; + wakeup_main_task(); + notify_signal(&dc.notify); + } + if (next >= 0 && do_xfade == XFADE_UNKNOWN && + dc.command != DECODE_COMMAND_START && + dc.state != DECODE_STATE_START) { + /* enable cross fading in this song? if yes, + calculate how many chunks will be required + for it */ + crossFadeChunks = + cross_fade_calc(pc.crossFade, dc.totalTime, + &(ob.audioFormat), + ob.size - + buffered_before_play); + if (crossFadeChunks > 0) { + do_xfade = XFADE_ENABLED; + nextChunk = -1; + } else + /* cross fading is disabled or the + next song is too short */ + do_xfade = XFADE_DISABLED; + } + + if (do_pause) + notify_wait(&pc.notify); + else if (!ob_is_empty() && (int)ob.begin != next) { + ob_chunk *beginChunk = ob_get_chunk(ob.begin); + unsigned int fadePosition; + if (do_xfade == XFADE_ENABLED && next >= 0 && + (fadePosition = ob_relative(next)) + <= crossFadeChunks) { + /* perform cross fade */ + if (nextChunk < 0) { + /* beginning of the cross fade + - adjust crossFadeChunks + which might be bigger than + the remaining number of + chunks in the old song */ + crossFadeChunks = fadePosition; + } + nextChunk = ob_absolute(crossFadeChunks); + if (nextChunk >= 0) { + ob_set_lazy(1); + cross_fade_apply(beginChunk, + ob_get_chunk(nextChunk), + &(ob.audioFormat), + fadePosition, + crossFadeChunks); + } else { + /* there are not enough + decoded chunks yet */ + if (dc.state == DECODE_STATE_STOP) { + /* the decoder isn't + running, abort + cross fading */ + do_xfade = XFADE_DISABLED; + } else { + /* wait for the + decoder */ + ob_set_lazy(0); + notify_wait(&pc.notify); + continue; + } + } + } + + /* play the current chunk */ + if (playChunk(beginChunk, &(ob.audioFormat), + sizeToTime) < 0) + break; + ob_shift(); + notify_signal(&dc.notify); + } else if (!ob_is_empty() && (int)ob.begin == next) { + /* at the beginning of a new song */ + + if (do_xfade == XFADE_ENABLED && nextChunk >= 0) { + /* the cross-fade is finished; skip + the section which was cross-faded + (and thus already played) */ + ob_skip(crossFadeChunks); + } + + do_xfade = XFADE_UNKNOWN; + + /* wait for the decoder to work on the new song */ + if (pc.queueState == PLAYER_QUEUE_DECODE || + pc.queueLockState == PLAYER_QUEUE_LOCKED) { + notify_wait(&pc.notify); + continue; + } + if (pc.queueState != PLAYER_QUEUE_PLAY) + break; + + next = -1; + if (waitOnDecode(&decodeWaitedOn) < 0) + return; + + pc.queueState = PLAYER_QUEUE_EMPTY; + wakeup_main_task(); + } else if (dc.state == DECODE_STATE_STOP && + dc.command != DECODE_COMMAND_START) { + break; + } else { + /*DEBUG("waiting for decoded audio, play silence\n");*/ + if (playAudio(silence, CHUNK_SIZE) < 0) + break; + } + } + + quitDecode(); +} + +/* decode w/ buffering + * this will fork another process + * child process does decoding + * parent process does playing audio + */ +static void decode(void) +{ + ob_clear(); + + dc.next_song = pc.next_song; + dc.error = DECODE_ERROR_NOERROR; + dc_command(DECODE_COMMAND_START); + + decodeParent(); +} + +static void * player_task(mpd_unused void *arg) +{ + notify_enter(&pc.notify); + + while (1) { + switch (pc.command) { + case PLAYER_COMMAND_PLAY: + decode(); + break; + + case PLAYER_COMMAND_STOP: + case PLAYER_COMMAND_SEEK: + case PLAYER_COMMAND_PAUSE: + player_command_finished(); + break; + + case PLAYER_COMMAND_CLOSE_AUDIO: + closeAudioDevice(); + player_command_finished(); + break; + + case PLAYER_COMMAND_LOCK_QUEUE: + pc.queueLockState = PLAYER_QUEUE_LOCKED; + player_command_finished(); + break; + + case PLAYER_COMMAND_UNLOCK_QUEUE: + pc.queueLockState = PLAYER_QUEUE_UNLOCKED; + player_command_finished(); + break; + + case PLAYER_COMMAND_NONE: + notify_wait(&pc.notify); + break; + } + } + return NULL; +} + +void player_create(void) +{ + pthread_attr_t attr; + pthread_t player_thread; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (pthread_create(&player_thread, &attr, player_task, NULL)) + FATAL("Failed to spawn player task: %s\n", strerror(errno)); +} diff --git a/src/player_thread.h b/src/player_thread.h new file mode 100644 index 000000000..4d154d6a1 --- /dev/null +++ b/src/player_thread.h @@ -0,0 +1,24 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2008 Max Kellermann <max@duempel.org> + * This project's homepage is: 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 + */ + +#ifndef PLAYER_THREAD_H +#define PLAYER_THREAD_H + +void player_create(void); + +#endif |