aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2008-08-26 08:27:09 +0200
committerMax Kellermann <max@duempel.org>2008-08-26 08:27:09 +0200
commite8bd9ddc9fc99d31659db1121622bef0f3303fc1 (patch)
treea9fea2466a207ca957fe47c6a0881e957ba2aad5
parent6104e9690e75046b793c0abe6fc20576fb3da19b (diff)
downloadmpd-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.
-rw-r--r--src/Makefile.am2
-rw-r--r--src/decode.c394
-rw-r--r--src/decode.h2
-rw-r--r--src/main.c4
-rw-r--r--src/player.c53
-rw-r--r--src/player_thread.c466
-rw-r--r--src/player_thread.h24
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