aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2008-08-16 09:28:15 -0700
committerEric Wong <normalperson@yhbt.net>2008-08-16 09:39:32 -0700
commit44d9f62f34e0561d83ea32941f0ea1b529b1490d (patch)
tree5345eba046b6e3bcf8c063e7bae8b501b7a99f4a
parentf9f70860622613686e6ac0bf7ebd448f437d92a7 (diff)
downloadmpd-44d9f62f34e0561d83ea32941f0ea1b529b1490d.tar.gz
mpd-44d9f62f34e0561d83ea32941f0ea1b529b1490d.tar.xz
mpd-44d9f62f34e0561d83ea32941f0ea1b529b1490d.zip
core rewrite (decode,player,outputBuffer,playlist)
This is a huge refactoring of the core mpd process. The queueing/buffering mechanism is heavily reworked. The player.c code has been merged into outputBuffer (the actual ring buffering logic is handled by ringbuf.c); and decode.c actually handles decoding stuff. The end result is several hundreds of lines shorter, even though we still have a lot of DEBUG statements left in there for tracing and a lot of assertions, too.
-rw-r--r--src/Makefile.am11
-rw-r--r--src/action_status.h16
-rw-r--r--src/command.c50
-rw-r--r--src/condition.c54
-rw-r--r--src/condition.h42
-rw-r--r--src/decode.c666
-rw-r--r--src/decode.h59
-rw-r--r--src/inputPlugins/_flac_common.c11
-rw-r--r--src/inputPlugins/_flac_common.h16
-rw-r--r--src/inputPlugins/aac_plugin.c44
-rw-r--r--src/inputPlugins/audiofile_plugin.c46
-rw-r--r--src/inputPlugins/flac_plugin.c53
-rw-r--r--src/inputPlugins/mod_plugin.c30
-rw-r--r--src/inputPlugins/mp3_plugin.c146
-rw-r--r--src/inputPlugins/mp4_plugin.c55
-rw-r--r--src/inputPlugins/mpc_plugin.c59
-rw-r--r--src/inputPlugins/oggflac_plugin.c55
-rw-r--r--src/inputPlugins/oggvorbis_plugin.c59
-rw-r--r--src/inputPlugins/wavpack_plugin.c83
-rw-r--r--src/inputStream_http.c8
-rw-r--r--src/interface.c3
-rw-r--r--src/log.c1
-rw-r--r--src/main.c7
-rw-r--r--src/main_notify.c83
-rw-r--r--src/main_notify.h6
-rw-r--r--src/myfprintf.c2
-rw-r--r--src/notify.c68
-rw-r--r--src/notify.h60
-rw-r--r--src/outputBuffer.c661
-rw-r--r--src/outputBuffer.h135
-rw-r--r--src/outputBuffer_accessors.h77
-rw-r--r--src/outputBuffer_audio.h35
-rw-r--r--src/outputBuffer_ob_send.h133
-rw-r--r--src/outputBuffer_sequence.h30
-rw-r--r--src/outputBuffer_xfade.h105
-rw-r--r--src/player.c345
-rw-r--r--src/player.h139
-rw-r--r--src/playerData.c19
-rw-r--r--src/playerData.h6
-rw-r--r--src/player_error.c53
-rw-r--r--src/player_error.h40
-rw-r--r--src/playlist.c542
-rw-r--r--src/playlist.h23
-rw-r--r--src/ringbuf.h6
-rw-r--r--src/sig_handlers.c1
-rw-r--r--src/stats.c4
-rw-r--r--src/volume.c3
47 files changed, 1951 insertions, 2199 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index d79bda29f..93943db23 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -29,8 +29,8 @@ mpd_inputPlugins = \
mpd_headers = \
- notify.h \
ack.h \
+ action_status.h \
audio.h \
audioOutput.h \
buffer2array.h \
@@ -61,10 +61,14 @@ mpd_headers = \
compress.h \
os_compat.h \
outputBuffer.h \
+ outputBuffer_accessors.h \
+ outputBuffer_audio.h \
+ outputBuffer_ob_send.h \
+ outputBuffer_xfade.h \
path.h \
pcm_utils.h \
permission.h \
- player.h \
+ player_error.h \
playerData.h \
playlist.h \
replayGain.h \
@@ -92,7 +96,6 @@ mpd_SOURCES = \
$(mpd_headers) \
$(mpd_audioOutputs) \
$(mpd_inputPlugins) \
- notify.c \
audio.c \
audioOutput.c \
buffer2array.c \
@@ -122,7 +125,7 @@ mpd_SOURCES = \
path.c \
pcm_utils.c \
permission.c \
- player.c \
+ player_error.c \
playerData.c \
playlist.c \
replayGain.c \
diff --git a/src/action_status.h b/src/action_status.h
new file mode 100644
index 000000000..d645435c3
--- /dev/null
+++ b/src/action_status.h
@@ -0,0 +1,16 @@
+#ifndef ACTION_STATUS_H
+#define ACTION_STATUS_H
+
+/* should probably be used in other places (decoder/input buffering), too */
+enum action_status {
+ /* all mutexes for conditions are unlocked and caller signaled */
+ AS_COMPLETE = 0,
+
+ /* mutexes are locked and caller has not been signaled, yet */
+ AS_INPROGRESS,
+
+ /* mutexes are unlocked and caller has not been signaled */
+ AS_DEFERRED
+};
+
+#endif /* !ACTION_STATUS_H */
diff --git a/src/command.c b/src/command.c
index a7e5b38fd..a8c49e8c9 100644
--- a/src/command.c
+++ b/src/command.c
@@ -17,7 +17,6 @@
*/
#include "command.h"
-#include "player.h"
#include "playlist.h"
#include "ls.h"
#include "directory.h"
@@ -33,6 +32,8 @@
#include "sllist.h"
#include "ack.h"
#include "os_compat.h"
+#include "player_error.h"
+#include "outputBuffer.h"
#define COMMAND_PLAY "play"
#define COMMAND_PLAYID "playid"
@@ -280,13 +281,15 @@ static int handleCurrentSong(int fd, int *permission, int argc, char *argv[])
static int handlePause(int fd, int *permission, int argc, char *argv[])
{
+ enum ob_action action = OB_ACTION_PAUSE_FLIP;
if (argc == 2) {
- int pause_flag;
- if (check_int(fd, &pause_flag, argv[1], check_boolean, argv[1]) < 0)
+ int set;
+ if (check_int(fd, &set, argv[1], check_boolean, argv[1]) < 0)
return -1;
- return playerSetPause(fd, pause_flag);
+ action = set ? OB_ACTION_PAUSE_SET : OB_ACTION_PAUSE_UNSET;
}
- return playerPause(fd);
+ ob_trigger_action(action);
+ return 0;
}
static int commandStatus(int fd, int *permission, int argc, char *argv[])
@@ -295,15 +298,16 @@ static int commandStatus(int fd, int *permission, int argc, char *argv[])
int updateJobId;
int song;
- playPlaylistIfPlayerStopped();
- switch (getPlayerState()) {
- case PLAYER_STATE_STOP:
+ switch (ob_get_state()) {
+ case OB_STATE_STOP:
+ case OB_STATE_QUIT:
state = COMMAND_STOP;
break;
- case PLAYER_STATE_PAUSE:
+ case OB_STATE_PAUSE:
state = COMMAND_PAUSE;
break;
- case PLAYER_STATE_PLAY:
+ case OB_STATE_PLAY:
+ case OB_STATE_SEEK:
state = COMMAND_PLAY;
break;
}
@@ -318,7 +322,7 @@ static int commandStatus(int fd, int *permission, int argc, char *argv[])
fdprintf(fd, "%s: %i\n", COMMAND_STATUS_PLAYLIST_LENGTH,
getPlaylistLength());
fdprintf(fd, "%s: %i\n", COMMAND_STATUS_CROSSFADE,
- (int)(getPlayerCrossFade() + 0.5));
+ (int)(ob_get_xfade() + 0.5));
fdprintf(fd, "%s: %s\n", COMMAND_STATUS_STATE, state);
@@ -328,14 +332,14 @@ static int commandStatus(int fd, int *permission, int argc, char *argv[])
fdprintf(fd, "%s: %i\n", COMMAND_STATUS_SONGID,
getPlaylistSongId(song));
}
- if (getPlayerState() != PLAYER_STATE_STOP) {
- fdprintf(fd, "%s: %i:%i\n", COMMAND_STATUS_TIME,
- getPlayerElapsedTime(), getPlayerTotalTime());
- fdprintf(fd, "%s: %li\n", COMMAND_STATUS_BITRATE,
- getPlayerBitRate());
- fdprintf(fd, "%s: %u:%i:%i\n", COMMAND_STATUS_AUDIO,
- getPlayerSampleRate(), getPlayerBits(),
- getPlayerChannels());
+ if (ob_get_state() != OB_STATE_STOP) {
+ fdprintf(fd, "%s: %lu:%lu\n", COMMAND_STATUS_TIME,
+ ob_get_elapsed_time(), ob_get_total_time());
+ fdprintf(fd, "%s: %u\n", COMMAND_STATUS_BITRATE,
+ ob_get_bit_rate());
+ fdprintf(fd, "%s: %u:%u:%u\n", COMMAND_STATUS_AUDIO,
+ ob_get_sample_rate(), ob_get_bits(),
+ ob_get_channels());
}
if ((updateJobId = isUpdatingDB())) {
@@ -343,9 +347,9 @@ static int commandStatus(int fd, int *permission, int argc, char *argv[])
updateJobId);
}
- if (getPlayerError() != PLAYER_ERROR_NOERROR) {
+ if (player_errno != PLAYER_ERROR_NONE) {
fdprintf(fd, "%s: %s\n", COMMAND_STATUS_ERROR,
- getPlayerErrorStr());
+ player_strerror());
}
return 0;
@@ -741,7 +745,7 @@ static int handleStats(int fd, int *permission, int argc, char *argv[])
static int handleClearError(int fd, int *permission, int argc, char *argv[])
{
- clearPlayerError();
+ player_clearerror();
return 0;
}
@@ -890,7 +894,7 @@ static int handleCrossfade(int fd, int *permission, int argc, char *argv[])
if (check_int(fd, &xfade_time, argv[1], check_non_negative, argv[1]) < 0)
return -1;
- setPlayerCrossFade(xfade_time);
+ ob_set_xfade(xfade_time);
return 0;
}
diff --git a/src/condition.c b/src/condition.c
index e71dcdd88..ca63ba537 100644
--- a/src/condition.c
+++ b/src/condition.c
@@ -27,54 +27,44 @@ void cond_init(struct condition *cond)
xpthread_cond_init(&cond->cond, NULL);
}
-void cond_enter(struct condition *cond)
-{
- pthread_mutex_lock(&cond->mutex);
-}
-
-void cond_leave(struct condition *cond)
-{
- pthread_mutex_unlock(&cond->mutex);
-}
-
-void cond_wait(struct condition *cond)
-{
- pthread_cond_wait(&cond->cond, &cond->mutex);
-}
-
-static struct timespec * ts_timeout(struct timespec *ts, const long sec)
+int cond_timedwait(struct condition *cond, const long msec)
{
+ static const long nsec_per_msec = 1000000;
+ static const long nsec_per_usec = 1000;
+ static const long nsec_per_sec = 1000000000;
+ struct timespec ts;
struct timeval tv;
+ int ret;
+
gettimeofday(&tv, NULL);
- ts->tv_sec = tv.tv_sec + sec;
- ts->tv_nsec = tv.tv_usec * 1000;
- return ts;
-}
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = (tv.tv_usec * nsec_per_usec) + (msec * nsec_per_msec);
+ if (ts.tv_nsec >= nsec_per_sec) {
+ ts.tv_nsec -= nsec_per_sec;
+ ts.tv_sec++;
+ }
-int cond_timedwait(struct condition *cond, const long sec)
-{
- struct timespec ts;
- int ret = pthread_cond_timedwait(&cond->cond, &cond->mutex,
- ts_timeout(&ts, sec));
+ ret = pthread_cond_timedwait(&cond->cond, &cond->mutex, &ts);
if (!ret || ret == ETIMEDOUT)
return ret;
FATAL("cond_timedwait: %s\n", strerror(ret));
return ret;
}
-int cond_signal_async(struct condition *cond)
+int cond_signal_trysync(struct condition *cond)
{
- if (!pthread_mutex_trylock(&cond->mutex)) {
- pthread_cond_signal(&cond->cond);
- pthread_mutex_unlock(&cond->mutex);
- return 0;
- }
- return EBUSY;
+ if (pthread_mutex_trylock(&cond->mutex) == EBUSY)
+ return EBUSY;
+ pthread_cond_signal(&cond->cond);
+ pthread_mutex_unlock(&cond->mutex);
+ return 0;
}
void cond_signal_sync(struct condition *cond)
{
+ pthread_mutex_lock(&cond->mutex);
pthread_cond_signal(&cond->cond);
+ pthread_mutex_unlock(&cond->mutex);
}
void cond_destroy(struct condition *cond)
diff --git a/src/condition.h b/src/condition.h
index 576ff9dc3..75e9cfad5 100644
--- a/src/condition.h
+++ b/src/condition.h
@@ -27,40 +27,62 @@ struct condition {
pthread_cond_t cond;
};
+#define STATIC_COND_INITIALIZER \
+ { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER }
+
void cond_init(struct condition *cond);
/**
* The thread which shall be notified by this object must call this
* function before any cond_wait() invocation. It locks the mutex.
*/
-void cond_enter(struct condition *cond);
+static inline void cond_enter(struct condition *cond)
+{
+ pthread_mutex_lock(&cond->mutex);
+}
+
+/**
+ * Will try to enter a condition where it can eventually wait.
+ *
+ * Returns immediately, 0 if successful, EBUSY if failed
+ */
+static inline int cond_tryenter(struct condition *cond)
+{
+ return pthread_mutex_trylock(&cond->mutex);
+}
/**
* Neutralize cond_leave().
*/
-void cond_leave(struct condition *cond);
+static inline void cond_leave(struct condition *cond)
+{
+ pthread_mutex_unlock(&cond->mutex);
+}
/**
* Wait for a conditio. Return immediately if we have already
* been notified since we last returned from cond_wait().
*/
-void cond_wait(struct condition *cond);
+static inline void cond_wait(struct condition *cond)
+{
+ pthread_cond_wait(&cond->cond, &cond->mutex);
+}
/**
* Wait for a condition with timeout
*
- * @param sec number of seconds to wait for (subject to change)
+ * @param sec number of milliseconds to wait for
*
* @return ETIMEDOUT if timed out, 0 if notification was received
*/
-int cond_timedwait(struct condition *cond, const long sec);
+int cond_timedwait(struct condition *cond, const long msec);
/**
* Notify the thread there is a waiter. This function never blocks.
*
* @return EBUSY if it was unable to lock the mutex, 0 on success
*/
-int cond_signal_async(struct condition *cond);
+int cond_signal_trysync(struct condition *cond);
/**
* Notify the thread synchronously, i.e. wait until it can deliver
@@ -69,6 +91,14 @@ int cond_signal_async(struct condition *cond);
void cond_signal_sync(struct condition *cond);
/**
+ * Signal the thread without caring about delivery success/failure
+ */
+static inline void cond_signal(struct condition *cond)
+{
+ pthread_cond_signal(&cond->cond);
+}
+
+/**
* cond_destroy - destroy the cond and internal structures
*/
void cond_destroy(struct condition *cond);
diff --git a/src/decode.c b/src/decode.c
index 6b57239e3..12c1b8746 100644
--- a/src/decode.c
+++ b/src/decode.c
@@ -17,241 +17,210 @@
*/
#include "decode.h"
-
-#include "player.h"
+#include "outputBuffer.h"
+#include "player_error.h"
+#include "playlist.h"
#include "playerData.h"
#include "pcm_utils.h"
#include "path.h"
#include "log.h"
#include "ls.h"
-#include "main_notify.h"
+#include "condition.h"
-enum xfade_state {
- XFADE_DISABLED = -1,
- XFADE_UNKNOWN = 0,
- XFADE_ENABLED = 1
-};
+static struct condition dc_action_cond = STATIC_COND_INITIALIZER;
+static struct condition dc_halt_cond = STATIC_COND_INITIALIZER;
-/* called inside decoder_task (inputPlugins) */
-void decoder_wakeup_player(void)
-{
- wakeup_player_nb();
-}
+struct decoder_control dc; /* ugh, global for now... */
-void decoder_sleep(void)
+/* blocking, waits until the signaled thread has replied */
+void dc_trigger_action(enum dc_action action, float seek_where)
{
- notify_wait(&dc.notify);
- wakeup_player_nb();
+ assert(!pthread_equal(pthread_self(), dc.thread));
+ /* assert(pthread_equal(pthread_self(), main_thread)); */
+ assert(action != DC_ACTION_NONE);
+
+ /* DEBUG(__FILE__ ":%s %d\n", __func__,__LINE__); */
+ cond_enter(&dc_action_cond);
+ assert(dc.action == DC_ACTION_NONE);
+ if (action == DC_ACTION_SEEK)
+ dc.seek_where = seek_where; /* usually 0 */
+ dc.action = action;
+ do {
+ /* DEBUG(__FILE__ ":%s %d\n", __func__,__LINE__); */
+ cond_signal(&dc_halt_cond); /* blind signal w/o lock */
+ /* DEBUG(__FILE__ ":%s %d\n", __func__,__LINE__); */
+ cond_timedwait(&dc_action_cond, 10);
+ /* DEBUG(__FILE__ ":%s %d\n", __func__,__LINE__); */
+ } while (dc.action != DC_ACTION_NONE); /* spurious wakeup protection */
+ /* DEBUG(__FILE__ ":%s %d\n", __func__,__LINE__); */
+ cond_leave(&dc_action_cond);
+ /* DEBUG(__FILE__ ":%s %d\n", __func__,__LINE__); */
}
-static void player_wakeup_decoder_nb(void)
+static void take_action(void)
{
- notify_signal(&dc.notify);
+ assert(pthread_equal(pthread_self(), dc.thread));
+ assert(dc.state == DC_STATE_STOP);
+ /* DEBUG("%s dc.action(%d): %d\n", __func__,__LINE__, dc.action); */
+ cond_enter(&dc_action_cond);
+ /* DEBUG("%s dc.action(%d): %d\n", __func__,__LINE__, dc.action); */
+
+ switch (dc.action) {
+ case DC_ACTION_NONE: goto out;
+ case DC_ACTION_START:
+ case DC_ACTION_SEEK:
+ dc.state = DC_STATE_DECODE;
+ return;
+ case DC_ACTION_STOP: dc.state = DC_STATE_STOP; break;
+ case DC_ACTION_QUIT: dc.state = DC_STATE_QUIT;
+ }
+ dc.action = DC_ACTION_NONE;
+ cond_signal(&dc_action_cond);
+out:
+ assert(dc.action == DC_ACTION_NONE);
+ cond_leave(&dc_action_cond);
}
-/* called from player_task */
-static void player_wakeup_decoder(void)
+/*
+ * This will grab an action, but will not signal the calling thread.
+ * dc_action_end() is required to signal the calling thread
+ */
+void dc_action_begin(void)
{
- notify_signal(&dc.notify);
- player_sleep();
-}
+ enum dc_action ret = dc.action;
-static void stopDecode(void)
-{
- if (dc.start || dc.state != DECODE_STATE_STOP) {
- dc.stop = 1;
- do { player_wakeup_decoder_nb(); } while (dc.stop);
+ assert(pthread_equal(pthread_self(), dc.thread));
+
+ if (ret != DC_ACTION_NONE) {
+ /* DEBUG(__FILE__ ":%s %d\n", __func__,__LINE__); */
+ cond_enter(&dc_action_cond);
+ /* dc.action can't get set to NONE outside this thread */
+ assert(dc.action == ret);
+ if (ret == DC_ACTION_SEEK)
+ ob_seek_start();
}
}
-static void quitDecode(void)
+void dc_action_end(void)
{
- stopDecode();
- pc.state = PLAYER_STATE_STOP;
- dc.seek = 0;
- pc.play = 0;
- pc.stop = 0;
- pc.pause = 0;
- wakeup_main_task();
+ assert(pthread_equal(pthread_self(), dc.thread));
+ assert(dc.action != DC_ACTION_NONE);
+ /* DEBUG("DONE ACTION %d\n", dc.action); */
+ if (dc.action == DC_ACTION_SEEK)
+ ob_seek_finish();
+ dc.action = DC_ACTION_NONE;
+
+ cond_signal(&dc_action_cond);
+ cond_leave(&dc_action_cond);
}
-static unsigned calculateCrossFadeChunks(AudioFormat * af, float totalTime)
+void dc_action_seek_fail(enum seek_err_type err_type)
{
- unsigned int buffered_chunks, chunks;
-
- if (pc.crossFade == 0 || pc.crossFade >= totalTime ||
- !isCurrentAudioFormat(af))
- return 0;
-
- assert(pc.crossFade > 0);
- assert(af->bits > 0);
- assert(af->channels > 0);
- assert(af->sampleRate > 0);
-
- chunks = (af->sampleRate * af->bits * af->channels / 8.0 / CHUNK_SIZE);
- chunks = (chunks * pc.crossFade + 0.5);
-
- buffered_chunks = ob.size;
- assert(buffered_chunks >= buffered_before_play);
- if (chunks > (buffered_chunks - buffered_before_play))
- chunks = buffered_chunks - buffered_before_play;
-
- return chunks;
+ assert(pthread_equal(pthread_self(), dc.thread));
+ cond_enter(&dc_action_cond);
+ assert(dc.action == DC_ACTION_SEEK);
+ dc.action = DC_ACTION_NONE;
+ dc.seek_where = err_type;
+ cond_signal(&dc_action_cond);
+ cond_leave(&dc_action_cond);
}
-static int waitOnDecode(int *decodeWaitedOn)
+/* Returns true if we need to interrupt the decoding inside an inputPlugin */
+int dc_intr(void)
{
- while (dc.start)
- player_wakeup_decoder();
-
- if (dc.error != DECODE_ERROR_NOERROR) {
- pc.errored_song = pc.current_song;
- pc.error = PLAYER_ERROR_FILE;
- quitDecode();
- return -1;
+ if (!pthread_equal(pthread_self(), dc.thread))
+ return 0;
+ switch (dc.action) {
+ case DC_ACTION_NONE:
+ case DC_ACTION_SEEK:
+ return 0;
+ default:
+ /* DEBUG(__FILE__": %s %d\n", __func__, __LINE__); */
+ /* DEBUG("dc.action: %d\n", (int)dc.action); */
+ 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 dc_seek(void)
{
- int ret = -1;
-
- if (dc.state == DECODE_STATE_STOP ||
- dc.error != DECODE_ERROR_NOERROR ||
- dc.current_song != pc.current_song) {
- stopDecode();
- *next = -1;
- ob_clear();
- dc.error = DECODE_ERROR_NOERROR;
- dc.start = 1;
- 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.seek = 1;
- do { player_wakeup_decoder(); } while (dc.seek);
- if (!dc.seekError) {
- pc.elapsedTime = dc.seekWhere;
- ret = 0;
- }
- }
- pc.seek = 0;
- wakeup_main_task();
-
- return ret;
+ if (pthread_equal(pthread_self(), dc.thread))
+ return (dc.action == DC_ACTION_SEEK);
+ return 0;
}
-static void processDecodeInput(int *pause_r, unsigned int *bbp_r,
- enum xfade_state *do_xfade_r,
- int *decodeWaitedOn_r,
- int *next_r)
+static void finalize_per_track_actions(void)
{
- if(pc.lockQueue) {
- pc.queueLockState = PLAYER_QUEUE_LOCKED;
- pc.lockQueue = 0;
- wakeup_main_task();
- }
- if(pc.unlockQueue) {
- pc.queueLockState = PLAYER_QUEUE_UNLOCKED;
- pc.unlockQueue = 0;
- wakeup_main_task();
- }
- if(pc.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 = pc.current_song;
- pc.error = PLAYER_ERROR_AUDIO;
- ERROR("problems opening audio device "
- "while playing \"%s\"\n",
- get_song_url(tmp, pc.current_song));
- *pause_r = -1;
- }
- }
- pc.pause = 0;
- wakeup_main_task();
- if (*pause_r == -1) {
- *pause_r = 1;
- } else if (*pause_r) {
- dropBufferedAudio();
- closeAudioDevice();
- }
- }
- if(pc.seek) {
- dropBufferedAudio();
- if (decodeSeek(decodeWaitedOn_r, next_r) == 0) {
- *do_xfade_r = XFADE_UNKNOWN;
- *bbp_r = 0;
- }
+ enum dc_action action;
+ /* DEBUG(":%s dc.action(%d): %d\n", __func__,__LINE__, dc.action); */
+ assert(pthread_equal(pthread_self(), dc.thread));
+ cond_enter(&dc_action_cond);
+ dc.state = DC_STATE_STOP;
+ action = dc.action;
+ dc.action = DC_ACTION_NONE;
+
+ if (action == DC_ACTION_STOP) {
+ cond_signal(&dc_action_cond);
+ } else if (action == DC_ACTION_SEEK) {
+ dc.seek_where = DC_SEEK_MISMATCH;
+ cond_signal(&dc_action_cond);
}
+ cond_leave(&dc_action_cond);
+ /* DEBUG(":%s dc.action(%d): %d\n", __func__,__LINE__, dc.action); */
}
-static void decodeStart(void)
+static void decode_start(void)
{
- int ret;
+ int err = -1;
int close_instream = 1;
- InputStream inStream;
+ InputStream is;
InputPlugin *plugin = NULL;
char path_max_fs[MPD_PATH_MAX];
char path_max_utf8[MPD_PATH_MAX];
-
- if (!get_song_url(path_max_utf8, pc.current_song)) {
- dc.error = DECODE_ERROR_FILE;
- goto stop_no_close;
+ assert(pthread_equal(pthread_self(), dc.thread));
+ assert(dc.state == DC_STATE_DECODE);
+ assert(dc.current_song);
+ get_song_url(path_max_utf8, dc.current_song);
+ assert(*path_max_utf8);
+
+ switch (dc.action) {
+ case DC_ACTION_START:
+ dc_action_end();
+ break;
+ case DC_ACTION_SEEK:
+ /* DEBUG("dc.seek_where(%d): %f\n", __LINE__, dc.seek_where); */
+ /* make sure dc_action_start() works inside inputPlugins: */
+ cond_leave(&dc_action_cond);
+ /* DEBUG("dc.action(%d) %d\n", __LINE__, dc.action); */
+ break;
+ default: assert("unknown action!" && 0);
}
- if (!isRemoteUrl(path_max_utf8)) {
+
+ if (isRemoteUrl(path_max_utf8)) {
+ pathcpy_trunc(path_max_fs, path_max_utf8);
+ } else {
rmp2amp_r(path_max_fs,
utf8_to_fs_charset(path_max_fs, path_max_utf8));
- } else
- pathcpy_trunc(path_max_fs, path_max_utf8);
-
- dc.current_song = pc.current_song; /* NEED LOCK */
- if (openInputStream(&inStream, path_max_fs) < 0) {
- dc.error = DECODE_ERROR_FILE;
- goto stop_no_close;
}
- dc.state = DECODE_STATE_START;
- dc.start = 0;
-
- /* for http streams, seekable is determined in bufferInputStream */
- dc.seekable = inStream.seekable;
-
- if (dc.stop)
- goto stop;
+ if (openInputStream(&is, path_max_fs) < 0) {
+ DEBUG("couldn't open song: %s\n", path_max_fs);
+ player_seterror(PLAYER_ERROR_FILENOTFOUND, dc.current_song);
+ return;
+ }
- ret = DECODE_ERROR_UNKTYPE;
if (isRemoteUrl(path_max_utf8)) {
unsigned int next = 0;
/* first we try mime types: */
- while (ret && (plugin = getInputPluginFromMimeType(inStream.mime, next++))) {
+ while (err && (plugin = getInputPluginFromMimeType(is.mime, next++))) {
if (!plugin->streamDecodeFunc)
continue;
if (!(plugin->streamTypes & INPUT_PLUGIN_STREAM_URL))
continue;
if (plugin->tryDecodeFunc
- && !plugin->tryDecodeFunc(&inStream))
+ && !plugin->tryDecodeFunc(&is))
continue;
- ret = plugin->streamDecodeFunc(&inStream);
+ err = plugin->streamDecodeFunc(&is);
break;
}
@@ -259,16 +228,16 @@ static void decodeStart(void)
if (plugin == NULL) {
const char *s = getSuffix(path_max_utf8);
next = 0;
- while (ret && (plugin = getInputPluginFromSuffix(s, next++))) {
+ while (err && (plugin = getInputPluginFromSuffix(s, next++))) {
if (!plugin->streamDecodeFunc)
continue;
if (!(plugin->streamTypes &
INPUT_PLUGIN_STREAM_URL))
continue;
if (plugin->tryDecodeFunc &&
- !plugin->tryDecodeFunc(&inStream))
+ !plugin->tryDecodeFunc(&is))
continue;
- ret = plugin->streamDecodeFunc(&inStream);
+ err = plugin->streamDecodeFunc(&is);
break;
}
}
@@ -278,330 +247,101 @@ static void decodeStart(void)
if (plugin == NULL) {
/* we already know our mp3Plugin supports streams, no
* need to check for stream{Types,DecodeFunc} */
- if ((plugin = getInputPluginFromName("mp3"))) {
- ret = plugin->streamDecodeFunc(&inStream);
- }
+ if ((plugin = getInputPluginFromName("mp3")))
+ err = plugin->streamDecodeFunc(&is);
}
} else {
unsigned int next = 0;
const char *s = getSuffix(path_max_utf8);
- while (ret && (plugin = getInputPluginFromSuffix(s, next++))) {
+ while (err && (plugin = getInputPluginFromSuffix(s, next++))) {
if (!plugin->streamTypes & INPUT_PLUGIN_STREAM_FILE)
continue;
if (plugin->tryDecodeFunc &&
- !plugin->tryDecodeFunc(&inStream))
+ !plugin->tryDecodeFunc(&is))
continue;
if (plugin->fileDecodeFunc) {
- closeInputStream(&inStream);
+ closeInputStream(&is);
close_instream = 0;
- ret = plugin->fileDecodeFunc(path_max_fs);
+ err = plugin->fileDecodeFunc(path_max_fs);
break;
} else if (plugin->streamDecodeFunc) {
- ret = plugin->streamDecodeFunc(&inStream);
+ err = plugin->streamDecodeFunc(&is);
break;
}
}
}
- if (ret < 0 || ret == DECODE_ERROR_UNKTYPE) {
- pc.errored_song = pc.current_song;
- if (ret != DECODE_ERROR_UNKTYPE)
- dc.error = DECODE_ERROR_FILE;
+ if (err) {
+ if (plugin)
+ player_seterror(PLAYER_ERROR_SYSTEM, dc.current_song);
else
- dc.error = DECODE_ERROR_UNKTYPE;
+ player_seterror(PLAYER_ERROR_UNKTYPE, dc.current_song);
}
-
-stop:
+ if (player_errno)
+ ERROR("player_error: %s\n", player_strerror());
if (close_instream)
- closeInputStream(&inStream);
-stop_no_close:
- dc.state = DECODE_STATE_STOP;
- dc.stop = 0;
+ closeInputStream(&is);
}
static void * decoder_task(mpd_unused void *arg)
{
- notify_enter(&dc.notify);
-
+ assert(pthread_equal(pthread_self(), dc.thread));
+ cond_enter(&dc_halt_cond);
while (1) {
- assert(dc.state == DECODE_STATE_STOP);
-
- if (dc.start || dc.seek) {
- decodeStart();
- } else if (dc.stop) {
- dc.stop = 0;
- decoder_wakeup_player();
- } else {
- decoder_sleep();
+ take_action();
+ switch (dc.state) {
+ case DC_STATE_STOP:
+ /* DEBUG(__FILE__": halted %d\n", __LINE__); */
+ cond_wait(&dc_halt_cond);
+ /* DEBUG(__FILE__": unhalted %d\n", __LINE__); */
+ break;
+ case DC_STATE_DECODE:
+ /* DEBUG(__FILE__": %s %d\n", __func__, __LINE__); */
+ /* DEBUG("dc.action: %d\n", (int)dc.action); */
+ if ((dc.current_song = playlist_queued_song())) {
+ char p[MPD_PATH_MAX];
+ ob_advance_sequence();
+ get_song_url(p, dc.current_song);
+ DEBUG("decoding song: %s\n", p);
+ decode_start();
+ DEBUG("DONE decoding song: %s\n", p);
+ ob_flush();
+ dc.current_song = NULL;
+ }
+ finalize_per_track_actions();
+ playlist_queue_next();
+ break;
+ case DC_STATE_QUIT:
+ goto out;
}
}
-
+out:
+ cond_leave(&dc_halt_cond);
+ assert(dc.state == DC_STATE_QUIT);
return NULL;
}
-void decoderInit(void)
+void decoder_init(void)
{
pthread_attr_t attr;
- pthread_t decoder_thread;
+ assert(!dc.thread);
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- if (pthread_create(&decoder_thread, &attr, decoder_task, NULL))
+ if (pthread_create(&dc.thread, &attr, decoder_task, NULL))
FATAL("Failed to spawn decoder task: %s\n", strerror(errno));
}
-static void crossFade(ob_chunk * a, ob_chunk * b,
- AudioFormat * format,
- unsigned int fadePosition, unsigned int crossFadeChunks)
+int dc_try_unhalt(void)
{
- assert(fadePosition <= crossFadeChunks);
-
- pcm_mix(a->data,
- b->data,
- a->chunkSize,
- b->chunkSize,
- format,
- ((float)fadePosition) /
- crossFadeChunks);
- if (b->chunkSize > a->chunkSize)
- a->chunkSize = b->chunkSize;
+ assert(!pthread_equal(pthread_self(), dc.thread));
+ return cond_signal_trysync(&dc_halt_cond);
}
-static int playChunk(ob_chunk * chunk,
- const AudioFormat * format, double sizeToTime)
+void dc_halt(void)
{
- 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;
- pc.play = 0;
- wakeup_main_task();
-
- while (1) {
- processDecodeInput(&do_pause, &bbp, &do_xfade,
- &decodeWaitedOn, &next);
- if (pc.stop) {
- dropBufferedAudio();
- break;
- }
-
- if (buffering) {
- if (ob_available() < bbp) {
- /* not enough decoded buffer space yet */
- player_sleep();
- 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 = pc.current_song;
- pc.error = PLAYER_ERROR_AUDIO;
- ERROR("problems opening audio device "
- "while playing \"%s\"\n",
- get_song_url(tmp, pc.current_song));
- break;
- } else {
- player_wakeup_decoder();
- }
- 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 = pc.current_song;
- pc.error = PLAYER_ERROR_FILE;
- break;
- }
- else {
- /* the decoder is not yet ready; wait
- some more */
- player_sleep();
- 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.start = 1;
- pc.queueState = PLAYER_QUEUE_DECODE;
- wakeup_main_task();
- player_wakeup_decoder_nb();
- }
- if (next >= 0 && do_xfade == XFADE_UNKNOWN && !dc.start &&
- dc.state != DECODE_STATE_START) {
- /* enable cross fading in this song? if yes,
- calculate how many chunks will be required
- for it */
- crossFadeChunks =
- calculateCrossFadeChunks(&(ob.audioFormat),
- dc.totalTime);
- 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)
- player_sleep();
- 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);
- crossFade(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);
- player_sleep();
- continue;
- }
- }
- }
-
- /* play the current chunk */
- if (playChunk(beginChunk, &(ob.audioFormat),
- sizeToTime) < 0)
- break;
- ob_shift();
- player_wakeup_decoder_nb();
- } 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) {
- player_sleep();
- 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.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.error = DECODE_ERROR_NOERROR;
- dc.seek = 0;
- dc.stop = 0;
- dc.start = 1;
- do { player_wakeup_decoder(); } while (dc.start);
-
- decodeParent();
+ assert(pthread_equal(pthread_self(), dc.thread));
+ cond_wait(&dc_halt_cond);
}
diff --git a/src/decode.h b/src/decode.h
index 8ee2a095f..b1ba138e2 100644
--- a/src/decode.h
+++ b/src/decode.h
@@ -22,43 +22,50 @@
#include "song.h"
#include "audio.h"
-#include "notify.h"
+#include "condition.h"
#define DECODE_TYPE_FILE 0
#define DECODE_TYPE_URL 1
-enum decoder_state {
- DECODE_STATE_STOP = 0,
- DECODE_STATE_START,
- DECODE_STATE_DECODE
+enum dc_action {
+ DC_ACTION_NONE = 0,
+ DC_ACTION_START,
+ DC_ACTION_SEEK, /* like start, but clears previously decoded audio */
+ DC_ACTION_STOP,
+ DC_ACTION_QUIT
};
-#define DECODE_ERROR_NOERROR 0
-#define DECODE_ERROR_UNKTYPE 10
-#define DECODE_ERROR_FILE 20
+/* only changeable by dc.thread */
+enum dc_state {
+ DC_STATE_STOP = 0, /* decoder stopped (no song) */
+ DC_STATE_DECODE, /* open() + {file,stream}DecodeFunc (+ paused) */
+ DC_STATE_QUIT /* NIH, the pthread cancellation API blows... */
+};
-typedef struct _DecoderControl {
- Notify notify;
+struct decoder_control {
+ Song * current_song; /* only needed for wavpack, remove? */
+ enum dc_state state; /* rw=dc.thread, r=main */
+ enum dc_action action; /* rw protected by action_cond */
+ float total_time; /* w=dc.thread, r=main */
+ float seek_where; /* -1 == error, rw protected by action_cond */
+ pthread_t thread;
+ AudioFormat audio_format; /* w=dc.thread, r=all */
+};
- volatile enum decoder_state state;
- volatile mpd_sint8 stop;
- volatile mpd_sint8 start;
- volatile mpd_uint16 error;
- volatile mpd_sint8 seek;
- volatile mpd_sint8 seekError;
- volatile mpd_sint8 seekable;
- volatile double seekWhere;
- AudioFormat audioFormat;
- Song *current_song;
- volatile float totalTime;
-} DecoderControl;
+extern struct decoder_control dc;
-void decode(void);
+void dc_trigger_action(enum dc_action action, float seek_where);
+void decoder_init(void);
+int dc_try_unhalt(void);
+void dc_halt(void);
-void decoder_wakeup_player(void);
+void dc_action_begin(void);
+void dc_action_end(void);
-void decoder_sleep(void);
+int dc_intr(void);
+int dc_seek(void);
-void decoderInit(void);
+enum seek_err_type { DC_SEEK_MISMATCH = -2, DC_SEEK_ERROR = -1 };
+void dc_action_seek_fail(enum seek_err_type);
#endif
diff --git a/src/inputPlugins/_flac_common.c b/src/inputPlugins/_flac_common.c
index cf23a5e8c..3401a8b4f 100644
--- a/src/inputPlugins/_flac_common.c
+++ b/src/inputPlugins/_flac_common.c
@@ -166,11 +166,10 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
switch (block->type) {
case FLAC__METADATA_TYPE_STREAMINFO:
- dc.audioFormat.bits = (mpd_sint8)si->bits_per_sample;
- dc.audioFormat.sampleRate = si->sample_rate;
- dc.audioFormat.channels = (mpd_sint8)si->channels;
- dc.totalTime = ((float)si->total_samples) / (si->sample_rate);
- getOutputAudioFormat(&(dc.audioFormat), &(ob.audioFormat));
+ dc.audio_format.bits = (mpd_sint8)si->bits_per_sample;
+ dc.audio_format.sampleRate = si->sample_rate;
+ dc.audio_format.channels = (mpd_sint8)si->channels;
+ dc.total_time = ((float)si->total_samples) / (si->sample_rate);
break;
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
flacParseReplayGain(block, data);
@@ -183,7 +182,7 @@ void flac_error_common_cb(const char *plugin,
const FLAC__StreamDecoderErrorStatus status,
FlacData * data)
{
- if (dc.stop)
+ if (dc_intr())
return;
switch (status) {
diff --git a/src/inputPlugins/_flac_common.h b/src/inputPlugins/_flac_common.h
index 10c2f3d38..5c147a064 100644
--- a/src/inputPlugins/_flac_common.h
+++ b/src/inputPlugins/_flac_common.h
@@ -165,17 +165,13 @@ MpdTag *copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block,
MpdTag * tag);
/* keep this inlined, this is just macro but prettier :) */
-static inline int flacSendChunk(FlacData * data)
+static inline enum dc_action flacSendChunk(FlacData * data)
{
- if (ob_send(data->inStream,
- 1, data->chunk,
- data->chunk_length, data->time,
- data->bitRate,
- data->replayGainInfo) ==
- OUTPUT_BUFFER_DC_STOP)
- return -1;
-
- return 0;
+ enum dc_action ret = ob_send(data->chunk, data->chunk_length,
+ data->time, data->bitRate,
+ data->replayGainInfo);
+ data->chunk_length = 0;
+ return ret;
}
#endif /* HAVE_FLAC || HAVE_OGGFLAC */
diff --git a/src/inputPlugins/aac_plugin.c b/src/inputPlugins/aac_plugin.c
index 6e53c6420..98329a4b3 100644
--- a/src/inputPlugins/aac_plugin.c
+++ b/src/inputPlugins/aac_plugin.c
@@ -339,9 +339,8 @@ static int aac_decode(char *path)
return -1;
}
- dc.audioFormat.bits = 16;
-
- dc.totalTime = totalTime;
+ dc.audio_format.bits = 16;
+ dc.total_time = totalTime;
file_time = 0.0;
@@ -372,13 +371,8 @@ static int aac_decode(char *path)
sampleRate = frameInfo.samplerate;
#endif
- if (dc.state != DECODE_STATE_DECODE) {
- dc.audioFormat.channels = frameInfo.channels;
- dc.audioFormat.sampleRate = sampleRate;
- getOutputAudioFormat(&(dc.audioFormat),
- &(ob.audioFormat));
- dc.state = DECODE_STATE_DECODE;
- }
+ dc.audio_format.channels = frameInfo.channels;
+ dc.audio_format.sampleRate = sampleRate;
advanceAacBuffer(&b, frameInfo.bytesconsumed);
@@ -395,34 +389,24 @@ static int aac_decode(char *path)
sampleBufferLen = sampleCount * 2;
- ob_send(NULL, 0, sampleBuffer,
- sampleBufferLen, file_time,
- bitRate, NULL);
- if (dc.seek) {
- dc.seekError = 1;
- dc.seek = 0;
- decoder_wakeup_player();
- } else if (dc.stop) {
- eof = 1;
+ switch (ob_send(sampleBuffer, sampleBufferLen,
+ file_time, bitRate, NULL)) {
+ case DC_ACTION_NONE: break;
+ case DC_ACTION_SEEK:
+ /*
+ * this plugin doesn't support seek because nobody
+ * has bothered, yet...
+ */
+ dc_action_seek_fail(DC_SEEK_ERROR);
break;
+ default: eof = 1;
}
}
- ob_flush();
-
faacDecClose(decoder);
if (b.buffer)
free(b.buffer);
- if (dc.state != DECODE_STATE_DECODE)
- return -1;
-
- if (dc.seek) {
- dc.seekError = 1;
- dc.seek = 0;
- decoder_wakeup_player();
- }
-
return 0;
}
diff --git a/src/inputPlugins/audiofile_plugin.c b/src/inputPlugins/audiofile_plugin.c
index 558731dd3..8178972ff 100644
--- a/src/inputPlugins/audiofile_plugin.c
+++ b/src/inputPlugins/audiofile_plugin.c
@@ -67,42 +67,39 @@ static int audiofile_decode(char *path)
afSetVirtualSampleFormat(af_fp, AF_DEFAULT_TRACK,
AF_SAMPFMT_TWOSCOMP, 16);
afGetVirtualSampleFormat(af_fp, AF_DEFAULT_TRACK, &fs, &bits);
- dc.audioFormat.bits = (mpd_uint8)bits;
- dc.audioFormat.sampleRate =
+ dc.audio_format.bits = (mpd_uint8)bits;
+ dc.audio_format.sampleRate =
(unsigned int)afGetRate(af_fp, AF_DEFAULT_TRACK);
- dc.audioFormat.channels =
+ dc.audio_format.channels =
(mpd_uint8)afGetVirtualChannels(af_fp, AF_DEFAULT_TRACK);
- getOutputAudioFormat(&(dc.audioFormat), &(ob.audioFormat));
frame_count = afGetFrameCount(af_fp, AF_DEFAULT_TRACK);
- dc.totalTime =
- ((float)frame_count / (float)dc.audioFormat.sampleRate);
+ dc.total_time = ((float)frame_count /
+ (float)dc.audio_format.sampleRate);
- bitRate = (mpd_uint16)(st.st_size * 8.0 / dc.totalTime / 1000.0 + 0.5);
+ bitRate = (mpd_uint16)(st.st_size * 8.0 / dc.total_time / 1000.0 + 0.5);
- if (dc.audioFormat.bits != 8 && dc.audioFormat.bits != 16) {
+ if (dc.audio_format.bits != 8 && dc.audio_format.bits != 16) {
ERROR("Only 8 and 16-bit files are supported. %s is %i-bit\n",
- path, dc.audioFormat.bits);
+ path, dc.audio_format.bits);
afCloseFile(af_fp);
return -1;
}
fs = (int)afGetVirtualFrameSize(af_fp, AF_DEFAULT_TRACK, 1);
- dc.state = DECODE_STATE_DECODE;
{
int ret, eof = 0, current = 0;
char chunk[CHUNK_SIZE];
while (!eof) {
- if (dc.seek) {
- ob_clear();
- current = dc.seekWhere *
- dc.audioFormat.sampleRate;
+ if (dc_seek()) {
+ dc_action_begin();
+ current = dc.seek_where *
+ dc.audio_format.sampleRate;
afSeekFrame(af_fp, AF_DEFAULT_TRACK, current);
- dc.seek = 0;
- decoder_wakeup_player();
+ dc_action_end();
}
ret =
@@ -112,20 +109,15 @@ static int audiofile_decode(char *path)
eof = 1;
else {
current += ret;
- ob_send(NULL,
- 1,
- chunk,
- ret * fs,
- (float)current /
- (float)dc.audioFormat.
- sampleRate, bitRate,
- NULL);
- if (dc.stop)
+ ob_send(chunk, ret * fs,
+ (float)current /
+ (float)dc.audio_format.sampleRate,
+ bitRate,
+ NULL);
+ if (dc_intr())
break;
}
}
-
- ob_flush();
}
afCloseFile(af_fp);
diff --git a/src/inputPlugins/flac_plugin.c b/src/inputPlugins/flac_plugin.c
index 38131bac9..4a4ac627c 100644
--- a/src/inputPlugins/flac_plugin.c
+++ b/src/inputPlugins/flac_plugin.c
@@ -41,14 +41,14 @@ static flac_read_status flacRead(const flac_decoder * flacDec,
while (1) {
r = readFromInputStream(data->inStream, (void *)buf, 1, *bytes);
- if (r == 0 && !inputStreamAtEOF(data->inStream) && !dc.stop)
- my_usleep(10000);
+ if (r == 0 && !inputStreamAtEOF(data->inStream) && !dc_intr())
+ my_usleep(10000); /* FIXME */
else
break;
}
*bytes = r;
- if (r == 0 && !dc.stop) {
+ if (r == 0 && !dc_intr()) {
if (inputStreamAtEOF(data->inStream))
return flac_read_status_eof;
else
@@ -247,7 +247,7 @@ static FLAC__StreamDecoderWriteStatus flacWrite(const flac_decoder *dec,
FLAC__uint32 samples = frame->header.blocksize;
unsigned int c_samp;
const unsigned int num_channels = frame->header.channels;
- const unsigned int bytes_per_sample = (dc.audioFormat.bits / 8);
+ const unsigned int bytes_per_sample = (dc.audio_format.bits / 8);
const unsigned int bytes_per_channel =
bytes_per_sample * frame->header.channels;
const unsigned int max_samples = FLAC_CHUNK_SIZE / bytes_per_channel;
@@ -255,7 +255,7 @@ static FLAC__StreamDecoderWriteStatus flacWrite(const flac_decoder *dec,
float timeChange;
FLAC__uint64 newPosition = 0;
- assert(dc.audioFormat.bits > 0);
+ assert(dc.audio_format.bits > 0);
timeChange = ((float)samples) / frame->header.sample_rate;
data->time += timeChange;
@@ -286,14 +286,12 @@ static FLAC__StreamDecoderWriteStatus flacWrite(const flac_decoder *dec,
c_samp, c_samp + num_samples);
data->chunk_length = num_samples * bytes_per_channel;
- if (flacSendChunk(data) < 0) {
- return
- FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
- }
- data->chunk_length = 0;
- if (dc.seek) {
- return
- FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+ switch (flacSendChunk(data)) {
+ case DC_ACTION_STOP:
+ return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+ case DC_ACTION_SEEK:
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+ default: break; /* compilers are complainers */
}
}
@@ -419,36 +417,35 @@ static int flac_decode_internal(InputStream * inStream, int is_ogg)
}
}
- dc.state = DECODE_STATE_DECODE;
-
while (1) {
if (!flac_process_single(flacDec))
break;
if (flac_get_state(flacDec) == flac_decoder_eof)
break;
- if (dc.seek) {
- FLAC__uint64 sampleToSeek = dc.seekWhere *
- dc.audioFormat.sampleRate + 0.5;
+ if (dc_seek()) {
+ FLAC__uint64 sampleToSeek;
+ dc_action_begin();
+ assert(dc.action == DC_ACTION_SEEK);
+ sampleToSeek = dc.seek_where *
+ dc.audio_format.sampleRate + 0.5;
if (flac_seek_absolute(flacDec, sampleToSeek)) {
- ob_clear();
data.time = ((float)sampleToSeek) /
- dc.audioFormat.sampleRate;
+ dc.audio_format.sampleRate;
data.position = 0;
- } else
- dc.seekError = 1;
- dc.seek = 0;
- decoder_wakeup_player();
+ data.chunk_length = 0;
+ } else {
+ dc.seek_where = DC_SEEK_ERROR;
+ }
+ dc_action_end();
}
}
- if (!dc.stop) {
+ if (!dc_intr()) {
flacPrintErroredState(flac_get_state(flacDec));
flac_finish(flacDec);
}
/* send last little bit */
- if (data.chunk_length > 0 && !dc.stop) {
+ if (data.chunk_length > 0 && !dc_intr())
flacSendChunk(&data);
- ob_flush();
- }
fail:
if (data.replayGainInfo)
diff --git a/src/inputPlugins/mod_plugin.c b/src/inputPlugins/mod_plugin.c
index 23b16fa23..cfa7d6e3d 100644
--- a/src/inputPlugins/mod_plugin.c
+++ b/src/inputPlugins/mod_plugin.c
@@ -179,25 +179,20 @@ static int mod_decode(char *path)
return -1;
}
- dc.totalTime = 0;
- dc.audioFormat.bits = 16;
- dc.audioFormat.sampleRate = 44100;
- dc.audioFormat.channels = 2;
- getOutputAudioFormat(&(dc.audioFormat), &(ob.audioFormat));
+ dc.total_time = 0;
+ dc.audio_format.bits = 16;
+ dc.audio_format.sampleRate = 44100;
+ dc.audio_format.channels = 2;
secPerByte =
- 1.0 / ((dc.audioFormat.bits * dc.audioFormat.channels / 8.0) *
- (float)dc.audioFormat.sampleRate);
+ 1.0 / ((dc.audio_format.bits * dc.audio_format.channels / 8.0) *
+ (float)dc.audio_format.sampleRate);
- dc.state = DECODE_STATE_DECODE;
while (1) {
- if (dc.seek) {
- dc.seekError = 1;
- dc.seek = 0;
- decoder_wakeup_player();
- }
+ if (dc_seek())
+ dc_action_seek_fail(DC_SEEK_ERROR);
- if (dc.stop)
+ if (dc_intr())
break;
if (!Player_Active())
@@ -205,13 +200,8 @@ static int mod_decode(char *path)
ret = VC_WriteBytes(data->audio_buffer, MIKMOD_FRAME_SIZE);
total_time += ret * secPerByte;
- ob_send(NULL, 0,
- (char *)data->audio_buffer, ret,
- total_time, 0, NULL);
+ ob_send((char *)data->audio_buffer, ret, total_time, 0, NULL);
}
-
- ob_flush();
-
mod_close(data);
MikMod_Exit();
diff --git a/src/inputPlugins/mp3_plugin.c b/src/inputPlugins/mp3_plugin.c
index dcfc25cdc..10a914a91 100644
--- a/src/inputPlugins/mp3_plugin.c
+++ b/src/inputPlugins/mp3_plugin.c
@@ -314,10 +314,8 @@ static void mp3_parseId3Tag(mp3DecodeData * data, size_t tagsize,
len = readFromInputStream(data->inStream,
allocated + count, (size_t) 1,
tagsize - count);
- if (len <= 0 && inputStreamAtEOF(data->inStream)) {
+ if (len <= 0 && inputStreamAtEOF(data->inStream))
break;
- } else if (len <= 0)
- my_usleep(10000);
else
count += len;
}
@@ -689,13 +687,13 @@ static int decodeFirstFrame(mp3DecodeData * data,
while (1) {
while ((ret = decodeNextFrameHeader(data, tag, replayGainInfo)) == DECODE_CONT &&
- !dc.stop);
- if (ret == DECODE_BREAK || dc.stop) return -1;
+ !dc_intr());
+ if (ret == DECODE_BREAK || dc_intr()) return -1;
if (ret == DECODE_SKIP) continue;
while ((ret = decodeNextFrame(data)) == DECODE_CONT &&
- !dc.stop);
- if (ret == DECODE_BREAK || dc.stop) return -1;
+ !dc_intr());
+ if (ret == DECODE_BREAK || dc_intr()) return -1;
if (ret == DECODE_OK) break;
}
@@ -813,6 +811,35 @@ static int openMp3FromInputStream(InputStream * inStream, mp3DecodeData * data,
return 0;
}
+static float frame_time(mp3DecodeData * data, long j)
+{
+ return (((float)mad_timer_count(data->times[j],
+ MAD_UNITS_MILLISECONDS)) / 1000);
+}
+
+static void mp3Read_seek(mp3DecodeData * data)
+{
+ long j = 0;
+ data->muteFrame = MUTEFRAME_SEEK;
+
+ assert(pthread_equal(pthread_self(), dc.thread));
+ assert(dc.action == DC_ACTION_SEEK);
+
+ while (j < data->highestFrame && dc.seek_where > frame_time(data, j))
+ j++;
+ if (j < data->highestFrame) {
+ dc_action_begin();
+ if (seekMp3InputBuffer(data, data->frameOffset[j]) < 0) {
+ dc.seek_where = DC_SEEK_ERROR;
+ } else {
+ data->outputPtr = data->outputBuffer;
+ data->currentFrame = j;
+ }
+ data->muteFrame = 0;
+ dc_action_end();
+ }
+}
+
static int mp3Read(mp3DecodeData * data, ReplayGainInfo ** replayGainInfo)
{
int samplesPerFrame;
@@ -821,6 +848,8 @@ static int mp3Read(mp3DecodeData * data, ReplayGainInfo ** replayGainInfo)
int ret;
int skip;
+ assert(pthread_equal(pthread_self(), dc.thread));
+
if (data->currentFrame >= data->highestFrame) {
mad_timer_add(&data->timer, (data->frame).header.duration);
data->bitRate = (data->frame).header.bitrate;
@@ -851,13 +880,13 @@ static int mp3Read(mp3DecodeData * data, ReplayGainInfo ** replayGainInfo)
data->muteFrame = 0;
break;
case MUTEFRAME_SEEK:
- if (dc.seekWhere <= data->elapsedTime) {
+ dc_action_begin();
+ assert(dc.action == DC_ACTION_SEEK);
+ if (dc.seek_where <= data->elapsedTime) {
data->outputPtr = data->outputBuffer;
- ob_clear();
data->muteFrame = 0;
- dc.seek = 0;
- decoder_wakeup_player();
}
+ dc_action_end();
break;
default:
mad_synth_frame(&data->synth, &data->frame);
@@ -928,53 +957,33 @@ static int mp3Read(mp3DecodeData * data, ReplayGainInfo ** replayGainInfo)
}
if (data->outputPtr >= data->outputBufferEnd) {
- ret = ob_send(data->inStream,
- data->inStream->seekable,
- data->outputBuffer,
- data->outputPtr - data->outputBuffer,
- data->elapsedTime,
- data->bitRate / 1000,
- (replayGainInfo != NULL) ? *replayGainInfo : NULL);
- if (ret == OUTPUT_BUFFER_DC_STOP) {
+ enum dc_action action = ob_send(
+ data->outputBuffer,
+ data->outputPtr -
+ data->outputBuffer,
+ data->elapsedTime,
+ data->bitRate / 1000,
+ replayGainInfo ? *replayGainInfo
+ : NULL);
+
+ if (action == DC_ACTION_STOP) {
data->flush = 0;
return DECODE_BREAK;
}
-
data->outputPtr = data->outputBuffer;
- if (ret == OUTPUT_BUFFER_DC_SEEK)
+ if (action == DC_ACTION_SEEK)
break;
}
}
data->decodedFirstFrame = 1;
- if (dc.seek && data->inStream->seekable) {
- long j = 0;
- data->muteFrame = MUTEFRAME_SEEK;
- while (j < data->highestFrame && dc.seekWhere >
- ((float)mad_timer_count(data->times[j],
- MAD_UNITS_MILLISECONDS))
- / 1000) {
- j++;
- }
- if (j < data->highestFrame) {
- if (seekMp3InputBuffer(data,
- data->frameOffset[j]) ==
- 0) {
- data->outputPtr = data->outputBuffer;
- ob_clear();
- data->currentFrame = j;
- } else
- dc.seekError = 1;
- data->muteFrame = 0;
- dc.seek = 0;
- decoder_wakeup_player();
- }
- } else if (dc.seek && !data->inStream->seekable) {
- dc.seek = 0;
- dc.seekError = 1;
- decoder_wakeup_player();
+ if (dc_seek()) {
+ if (data->inStream->seekable)
+ mp3Read_seek(data);
+ else
+ dc_action_seek_fail(DC_SEEK_ERROR);
}
}
@@ -983,22 +992,22 @@ static int mp3Read(mp3DecodeData * data, ReplayGainInfo ** replayGainInfo)
while ((ret =
decodeNextFrameHeader(data, NULL,
replayGainInfo)) == DECODE_CONT
- && !dc.stop) ;
- if (ret == DECODE_BREAK || dc.stop || dc.seek)
+ && dc_intr()) ;
+ if (ret == DECODE_BREAK || dc_intr() || dc_seek())
break;
else if (ret == DECODE_SKIP)
skip = 1;
if (!data->muteFrame) {
while ((ret = decodeNextFrame(data)) == DECODE_CONT &&
- !dc.stop && !dc.seek) ;
- if (ret == DECODE_BREAK || dc.stop || dc.seek)
+ !dc_intr() && dc_seek()) ;
+ if (ret == DECODE_BREAK || dc_intr() || dc_seek())
break;
}
if (!skip && ret == DECODE_OK)
break;
}
- if (dc.stop)
+ if (dc_intr())
return DECODE_BREAK;
return ret;
@@ -1018,9 +1027,8 @@ static int mp3_decode(InputStream * inStream)
MpdTag *tag = NULL;
ReplayGainInfo *replayGainInfo = NULL;
- if (openMp3FromInputStream(inStream, &data, &tag, &replayGainInfo) <
- 0) {
- if (!dc.stop) {
+ if (openMp3FromInputStream(inStream, &data, &tag, &replayGainInfo) < 0) {
+ if (!dc_intr()) {
ERROR
("Input does not appear to be a mp3 bit stream.\n");
return -1;
@@ -1028,10 +1036,9 @@ static int mp3_decode(InputStream * inStream)
return 0;
}
- initAudioFormatFromMp3DecodeData(&data, &(dc.audioFormat));
- getOutputAudioFormat(&(dc.audioFormat), &(ob.audioFormat));
+ initAudioFormatFromMp3DecodeData(&data, &(dc.audio_format));
- dc.totalTime = data.totalTime;
+ dc.total_time = data.totalTime;
if (inStream->metaTitle) {
if (tag)
@@ -1058,31 +1065,16 @@ static int mp3_decode(InputStream * inStream)
freeMpdTag(tag);
}
- dc.state = DECODE_STATE_DECODE;
-
while (mp3Read(&data, &replayGainInfo) != DECODE_BREAK) ;
- /* send last little bit if not dc.stop */
- if (!dc.stop && data.outputPtr != data.outputBuffer && data.flush) {
- ob_send(NULL,
- data.inStream->seekable,
- data.outputBuffer,
- data.outputPtr - data.outputBuffer,
- data.elapsedTime, data.bitRate / 1000,
- replayGainInfo);
+ /* send last little bit if not dc_intr() */
+ if (!dc_intr() && data.outputPtr != data.outputBuffer && data.flush) {
+ ob_send(data.outputBuffer, data.outputPtr - data.outputBuffer,
+ data.elapsedTime, data.bitRate / 1000, replayGainInfo);
}
if (replayGainInfo)
freeReplayGainInfo(replayGainInfo);
-
- if (dc.seek && data.muteFrame == MUTEFRAME_SEEK) {
- ob_clear();
- dc.seek = 0;
- decoder_wakeup_player();
- }
-
- ob_flush();
mp3DecodeDataFinalize(&data);
-
return 0;
}
diff --git a/src/inputPlugins/mp4_plugin.c b/src/inputPlugins/mp4_plugin.c
index 7f13ca344..bf200c534 100644
--- a/src/inputPlugins/mp4_plugin.c
+++ b/src/inputPlugins/mp4_plugin.c
@@ -145,7 +145,7 @@ static int mp4_decode(InputStream * inStream)
#endif
faacDecSetConfiguration(decoder, config);
- dc.audioFormat.bits = 16;
+ dc.audio_format.bits = 16;
mp4Buffer = NULL;
mp4BufferSize = 0;
@@ -160,8 +160,8 @@ static int mp4_decode(InputStream * inStream)
return -1;
}
- dc.audioFormat.sampleRate = sampleRate;
- dc.audioFormat.channels = channels;
+ dc.audio_format.sampleRate = sampleRate;
+ dc.audio_format.channels = channels;
file_time = mp4ff_get_track_duration_use_offsets(mp4fh, track);
scale = mp4ff_time_scale(mp4fh, track);
@@ -175,7 +175,7 @@ static int mp4_decode(InputStream * inStream)
free(mp4cb);
return -1;
}
- dc.totalTime = ((float)file_time) / scale;
+ dc.total_time = ((float)file_time) / scale;
numSamples = mp4ff_num_samples(mp4fh, track);
@@ -184,13 +184,16 @@ static int mp4_decode(InputStream * inStream)
seekTable = xmalloc(sizeof(float) * numSamples);
for (sampleId = 0; sampleId < numSamples && !eof; sampleId++) {
- if (dc.seek)
+ if (!seeking && dc_seek()) {
+ dc_action_begin();
+ assert(dc.action == DC_ACTION_SEEK);
seeking = 1;
+ }
if (seeking && seekTableEnd > 1 &&
- seekTable[seekTableEnd] >= dc.seekWhere) {
+ seekTable[seekTableEnd] >= dc.seek_where) {
int i = 2;
- while (seekTable[i] < dc.seekWhere)
+ while (seekTable[i] < dc.seek_where)
i++;
sampleId = i - 1;
file_time = seekTable[sampleId];
@@ -212,15 +215,14 @@ static int mp4_decode(InputStream * inStream)
dur -= offset;
file_time += ((float)dur) / scale;
- if (seeking && file_time > dc.seekWhere)
+ if (seeking && file_time > dc.seek_where)
seekPositionFound = 1;
if (seeking && seekPositionFound) {
seekPositionFound = 0;
- ob_clear();
seeking = 0;
- dc.seek = 0;
- decoder_wakeup_player();
+ assert(dc.action == DC_ACTION_SEEK);
+ dc_action_end();
}
if (seeking)
@@ -247,17 +249,12 @@ static int mp4_decode(InputStream * inStream)
break;
}
- if (dc.state != DECODE_STATE_DECODE) {
- channels = frameInfo.channels;
+ channels = frameInfo.channels;
#ifdef HAVE_FAACDECFRAMEINFO_SAMPLERATE
- scale = frameInfo.samplerate;
+ scale = frameInfo.samplerate;
#endif
- dc.audioFormat.sampleRate = scale;
- dc.audioFormat.channels = frameInfo.channels;
- getOutputAudioFormat(&(dc.audioFormat),
- &(ob.audioFormat));
- dc.state = DECODE_STATE_DECODE;
- }
+ dc.audio_format.sampleRate = scale;
+ dc.audio_format.channels = frameInfo.channels;
if (channels * (unsigned long)(dur + offset) > frameInfo.samples) {
dur = frameInfo.samples / channels;
@@ -277,10 +274,9 @@ static int mp4_decode(InputStream * inStream)
sampleBuffer += offset * channels * 2;
- ob_send(inStream, 1, sampleBuffer,
- sampleBufferLen, file_time,
- bitRate, NULL);
- if (dc.stop) {
+ ob_send(sampleBuffer, sampleBufferLen, file_time,
+ bitRate, NULL);
+ if (dc_intr()) {
eof = 1;
break;
}
@@ -291,15 +287,10 @@ static int mp4_decode(InputStream * inStream)
mp4ff_close(mp4fh);
free(mp4cb);
- if (dc.state != DECODE_STATE_DECODE)
- return -1;
-
- if (dc.seek && seeking) {
- ob_clear();
- dc.seek = 0;
- decoder_wakeup_player();
+ if (seeking) {
+ dc.seek_where = DC_SEEK_ERROR;
+ dc_action_end();
}
- ob_flush();
return 0;
}
diff --git a/src/inputPlugins/mpc_plugin.c b/src/inputPlugins/mpc_plugin.c
index 1003f15d5..116e471ff 100644
--- a/src/inputPlugins/mpc_plugin.c
+++ b/src/inputPlugins/mpc_plugin.c
@@ -42,8 +42,8 @@ static mpc_int32_t mpc_read_cb(void *vdata, void *ptr, mpc_int32_t size)
while (1) {
ret = readFromInputStream(data->inStream, ptr, 1, size);
- if (ret == 0 && !inputStreamAtEOF(data->inStream) && !dc.stop)
- my_usleep(10000);
+ if (ret == 0 && !inputStreamAtEOF(data->inStream) && !dc_intr())
+ my_usleep(10000); /* FIXME */
else
break;
}
@@ -147,7 +147,7 @@ static int mpc_decode(InputStream * inStream)
mpc_streaminfo_init(&info);
if ((ret = mpc_streaminfo_read(&info, &reader)) != ERROR_CODE_OK) {
- if (!dc.stop) {
+ if (!dc_intr()) {
ERROR("Not a valid musepack stream\n");
return -1;
}
@@ -157,20 +157,18 @@ static int mpc_decode(InputStream * inStream)
mpc_decoder_setup(&decoder, &reader);
if (!mpc_decoder_initialize(&decoder, &info)) {
- if (!dc.stop) {
+ if (!dc_intr()) {
ERROR("Not a valid musepack stream\n");
return -1;
}
return 0;
}
- dc.totalTime = mpc_streaminfo_get_length(&info);
+ dc.total_time = mpc_streaminfo_get_length(&info);
- dc.audioFormat.bits = 16;
- dc.audioFormat.channels = info.channels;
- dc.audioFormat.sampleRate = info.sample_freq;
-
- getOutputAudioFormat(&(dc.audioFormat), &(ob.audioFormat));
+ dc.audio_format.bits = 16;
+ dc.audio_format.channels = info.channels;
+ dc.audio_format.sampleRate = info.sample_freq;
replayGainInfo = newReplayGainInfo();
replayGainInfo->albumGain = info.gain_album * 0.01;
@@ -178,19 +176,16 @@ static int mpc_decode(InputStream * inStream)
replayGainInfo->trackGain = info.gain_title * 0.01;
replayGainInfo->trackPeak = info.peak_title / 32767.0;
- dc.state = DECODE_STATE_DECODE;
-
while (!eof) {
- if (dc.seek) {
- samplePos = dc.seekWhere * dc.audioFormat.sampleRate;
+ if (dc_seek()) {
+ dc_action_begin();
+ samplePos = dc.seek_where * dc.audio_format.sampleRate;
if (mpc_decoder_seek_sample(&decoder, samplePos)) {
- ob_clear();
s16 = (mpd_sint16 *) chunk;
chunkpos = 0;
} else
- dc.seekError = 1;
- dc.seek = 0;
- decoder_wakeup_player();
+ dc.seek_where = DC_SEEK_ERROR;
+ dc_action_end();
}
vbrUpdateAcc = 0;
@@ -198,7 +193,7 @@ static int mpc_decode(InputStream * inStream)
ret = mpc_decoder_decode(&decoder, sample_buffer,
&vbrUpdateAcc, &vbrUpdateBits);
- if (ret <= 0 || dc.stop) {
+ if (ret <= 0 || dc_intr()) {
eof = 1;
break;
}
@@ -216,20 +211,17 @@ static int mpc_decode(InputStream * inStream)
if (chunkpos >= MPC_CHUNK_SIZE) {
total_time = ((float)samplePos) /
- dc.audioFormat.sampleRate;
+ dc.audio_format.sampleRate;
bitRate = vbrUpdateBits *
- dc.audioFormat.sampleRate / 1152 / 1000;
+ dc.audio_format.sampleRate / 1152 / 1000;
- ob_send(inStream,
- inStream->seekable,
- chunk, chunkpos,
- total_time,
- bitRate, replayGainInfo);
+ ob_send(chunk, chunkpos, total_time,
+ bitRate, replayGainInfo);
chunkpos = 0;
s16 = (mpd_sint16 *) chunk;
- if (dc.stop) {
+ if (dc_intr()) {
eof = 1;
break;
}
@@ -237,19 +229,14 @@ static int mpc_decode(InputStream * inStream)
}
}
- if (!dc.stop && chunkpos > 0) {
- total_time = ((float)samplePos) / dc.audioFormat.sampleRate;
+ if (!dc_intr() && chunkpos > 0) {
+ total_time = ((float)samplePos) / dc.audio_format.sampleRate;
bitRate =
- vbrUpdateBits * dc.audioFormat.sampleRate / 1152 / 1000;
+ vbrUpdateBits * dc.audio_format.sampleRate / 1152 / 1000;
- ob_send(NULL, inStream->seekable,
- chunk, chunkpos, total_time, bitRate,
- replayGainInfo);
+ ob_send(chunk, chunkpos, total_time, bitRate, replayGainInfo);
}
-
- ob_flush();
-
freeReplayGainInfo(replayGainInfo);
return 0;
diff --git a/src/inputPlugins/oggflac_plugin.c b/src/inputPlugins/oggflac_plugin.c
index 379ff6466..6c5998afe 100644
--- a/src/inputPlugins/oggflac_plugin.c
+++ b/src/inputPlugins/oggflac_plugin.c
@@ -55,15 +55,14 @@ static OggFLAC__SeekableStreamDecoderReadStatus of_read_cb(const
while (1) {
r = readFromInputStream(data->inStream, (void *)buf, 1, *bytes);
- if (r == 0 && !inputStreamAtEOF(data->inStream) &&
- !dc.stop)
- my_usleep(10000);
+ if (r == 0 && !inputStreamAtEOF(data->inStream) && !dc_intr())
+ my_usleep(10000); /* FIXME */
else
break;
}
*bytes = r;
- if (r == 0 && !inputStreamAtEOF(data->inStream) && !dc.stop)
+ if (r == 0 && !inputStreamAtEOF(data->inStream) && !dc_intr())
return OggFLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR;
return OggFLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
@@ -193,16 +192,17 @@ static FLAC__StreamDecoderWriteStatus oggflacWrite(const
c_chan++) {
u16 = buf[c_chan][c_samp];
uc = (unsigned char *)&u16;
- for (i = 0; i < (dc.audioFormat.bits / 8); i++) {
+ for (i = 0; i < (dc.audio_format.bits / 8); i++) {
if (data->chunk_length >= FLAC_CHUNK_SIZE) {
- if (flacSendChunk(data) < 0) {
- return
- FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
- }
- data->chunk_length = 0;
- if (dc.seek) {
- return
- FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+ /* FIXME: line wrapping */
+ switch (flacSendChunk(data)) {
+ case DC_ACTION_STOP:
+ return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+ case DC_ACTION_SEEK:
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+ default:
+ /* compilers are complainers */
+ break;
}
}
data->chunk[data->chunk_length++] = *(uc++);
@@ -349,40 +349,39 @@ static int oggflac_decode(InputStream * inStream)
goto fail;
}
- dc.state = DECODE_STATE_DECODE;
-
while (1) {
OggFLAC__seekable_stream_decoder_process_single(decoder);
if (OggFLAC__seekable_stream_decoder_get_state(decoder) !=
OggFLAC__SEEKABLE_STREAM_DECODER_OK) {
break;
}
- if (dc.seek) {
- FLAC__uint64 sampleToSeek = dc.seekWhere *
- dc.audioFormat.sampleRate + 0.5;
+ if (dc_seek()) {
+ FLAC__uint64 sampleToSeek;
+ dc_action_begin();
+ assert(dc.action == DC_ACTION_SEEK);
+ sampleToSeek = dc.seek_where *
+ dc.audio_format.sampleRate + 0.5;
if (OggFLAC__seekable_stream_decoder_seek_absolute
(decoder, sampleToSeek)) {
- ob_clear();
data.time = ((float)sampleToSeek) /
- dc.audioFormat.sampleRate;
+ dc.audio_format.sampleRate;
data.position = 0;
- } else
- dc.seekError = 1;
- dc.seek = 0;
- decoder_wakeup_player();
+ data.chunk_length = 0;
+ } else {
+ dc.seek_where = DC_SEEK_ERROR;
+ }
+ dc_action_end();
}
}
- if (!dc.stop) {
+ if (!dc_intr()) {
oggflacPrintErroredState
(OggFLAC__seekable_stream_decoder_get_state(decoder));
OggFLAC__seekable_stream_decoder_finish(decoder);
}
/* send last little bit */
- if (data.chunk_length > 0 && !dc.stop) {
+ if (data.chunk_length > 0 && !dc_intr())
flacSendChunk(&data);
- ob_flush();
- }
fail:
oggflac_cleanup(&data, decoder);
diff --git a/src/inputPlugins/oggvorbis_plugin.c b/src/inputPlugins/oggvorbis_plugin.c
index 16040b388..fcedda54a 100644
--- a/src/inputPlugins/oggvorbis_plugin.c
+++ b/src/inputPlugins/oggvorbis_plugin.c
@@ -66,8 +66,8 @@ static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *vdata)
while (1) {
ret = readFromInputStream(data->inStream, ptr, size, nmemb);
if (ret == 0 && !inputStreamAtEOF(data->inStream) &&
- !dc.stop) {
- my_usleep(10000);
+ !dc_intr()) {
+ my_usleep(10000); /* FIXME */
} else
break;
}
@@ -80,7 +80,7 @@ static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *vdata)
static int ogg_seek_cb(void *vdata, ogg_int64_t offset, int whence)
{
const OggCallbackData *data = (const OggCallbackData *) vdata;
- if (dc.stop)
+ if (dc_intr())
return -1;
return seekInputStream(data->inStream, offset, whence);
}
@@ -240,7 +240,7 @@ static int oggvorbis_decode(InputStream * inStream)
callbacks.close_func = ogg_close_cb;
callbacks.tell_func = ogg_tell_cb;
if ((ret = ov_open_callbacks(&data, &vf, NULL, 0, callbacks)) < 0) {
- if (!dc.stop) {
+ if (!dc_intr()) {
switch (ret) {
case OV_EREAD:
errorStr = "read error";
@@ -267,20 +267,19 @@ static int oggvorbis_decode(InputStream * inStream)
}
return 0;
}
- dc.totalTime = ov_time_total(&vf, -1);
- if (dc.totalTime < 0)
- dc.totalTime = 0;
- dc.audioFormat.bits = 16;
+ dc.total_time = ov_time_total(&vf, -1);
+ if (dc.total_time < 0)
+ dc.total_time = 0;
+ dc.audio_format.bits = 16;
while (1) {
- if (dc.seek) {
- if (0 == ov_time_seek_page(&vf, dc.seekWhere)) {
- ob_clear();
+ if (dc_seek()) {
+ dc_action_begin();
+ if (0 == ov_time_seek_page(&vf, dc.seek_where))
chunkpos = 0;
- } else
- dc.seekError = 1;
- dc.seek = 0;
- decoder_wakeup_player();
+ else
+ dc.seek_where = DC_SEEK_ERROR;
+ dc_action_end();
}
ret = ov_read(&vf, chunk + chunkpos,
OGG_CHUNK_SIZE - chunkpos,
@@ -288,13 +287,8 @@ static int oggvorbis_decode(InputStream * inStream)
if (current_section != prev_section) {
/*printf("new song!\n"); */
vorbis_info *vi = ov_info(&vf, -1);
- dc.audioFormat.channels = vi->channels;
- dc.audioFormat.sampleRate = vi->rate;
- if (dc.state == DECODE_STATE_START) {
- getOutputAudioFormat(&(dc.audioFormat),
- &(ob.audioFormat));
- dc.state = DECODE_STATE_DECODE;
- }
+ dc.audio_format.channels = vi->channels;
+ dc.audio_format.sampleRate = vi->rate;
comments = ov_comment(&vf, -1)->user_comments;
putOggCommentsIntoOutputBuffer(inStream->metaName,
comments);
@@ -316,23 +310,18 @@ static int oggvorbis_decode(InputStream * inStream)
if ((test = ov_bitrate_instant(&vf)) > 0) {
bitRate = test / 1000;
}
- ob_send(inStream,
- inStream->seekable,
- chunk, chunkpos,
- ov_pcm_tell(&vf) /
- dc.audioFormat.sampleRate,
- bitRate, replayGainInfo);
+ ob_send(chunk, chunkpos,
+ ov_pcm_tell(&vf) / dc.audio_format.sampleRate,
+ bitRate, replayGainInfo);
chunkpos = 0;
- if (dc.stop)
+ if (dc_intr())
break;
}
}
- if (!dc.stop && chunkpos > 0) {
- ob_send(NULL, inStream->seekable,
- chunk, chunkpos,
- ov_time_tell(&vf), bitRate,
- replayGainInfo);
+ if (!dc_intr() && chunkpos > 0) {
+ ob_send(chunk, chunkpos, ov_time_tell(&vf), bitRate,
+ replayGainInfo);
}
if (replayGainInfo)
@@ -340,8 +329,6 @@ static int oggvorbis_decode(InputStream * inStream)
ov_clear(&vf);
- ob_flush();
-
return 0;
}
diff --git a/src/inputPlugins/wavpack_plugin.c b/src/inputPlugins/wavpack_plugin.c
index 7a7145141..ef02712d7 100644
--- a/src/inputPlugins/wavpack_plugin.c
+++ b/src/inputPlugins/wavpack_plugin.c
@@ -139,12 +139,12 @@ static void wavpack_decode(WavpackContext *wpc, int canseek,
int position, outsamplesize;
int Bps;
- dc.audioFormat.sampleRate = WavpackGetSampleRate(wpc);
- dc.audioFormat.channels = WavpackGetReducedChannels(wpc);
- dc.audioFormat.bits = WavpackGetBitsPerSample(wpc);
+ dc.audio_format.sampleRate = WavpackGetSampleRate(wpc);
+ dc.audio_format.channels = WavpackGetReducedChannels(wpc);
+ dc.audio_format.bits = WavpackGetBitsPerSample(wpc);
- if (dc.audioFormat.bits > 16)
- dc.audioFormat.bits = 16;
+ if (dc.audio_format.bits > 16)
+ dc.audio_format.bits = 16;
if ((WavpackGetMode(wpc) & MODE_FLOAT) == MODE_FLOAT)
format_samples = format_samples_float;
@@ -162,40 +162,33 @@ static void wavpack_decode(WavpackContext *wpc, int canseek,
outsamplesize = Bps;
if (outsamplesize > 2)
outsamplesize = 2;
- outsamplesize *= dc.audioFormat.channels;
+ outsamplesize *= dc.audio_format.channels;
- samplesreq = sizeof(chunk) / (4 * dc.audioFormat.channels);
+ samplesreq = sizeof(chunk) / (4 * dc.audio_format.channels);
- getOutputAudioFormat(&(dc.audioFormat), &(ob.audioFormat));
-
- dc.totalTime = (float)allsamples / dc.audioFormat.sampleRate;
- dc.state = DECODE_STATE_DECODE;
- dc.seekable = canseek;
+ dc.total_time = (float)allsamples / dc.audio_format.sampleRate;
position = 0;
do {
- if (dc.seek) {
+ if (dc_seek()) {
+ dc_action_begin();
+ assert(dc.action == DC_ACTION_SEEK);
if (canseek) {
int where;
-
- ob_clear();
-
- where = dc.seekWhere *
- dc.audioFormat.sampleRate;
+ where = dc.seek_where *
+ dc.audio_format.sampleRate;
if (WavpackSeekSample(wpc, where))
position = where;
else
- dc.seekError = 1;
+ dc.seek_where = DC_SEEK_ERROR;
} else {
- dc.seekError = 1;
+ dc.seek_where = DC_SEEK_ERROR;
}
-
- dc.seek = 0;
- decoder_wakeup_player();
+ dc_action_end();
}
- if (dc.stop)
+ if (dc_intr())
break;
samplesgot = WavpackUnpackSamples(wpc,
@@ -205,19 +198,15 @@ static void wavpack_decode(WavpackContext *wpc, int canseek,
1000 + 0.5);
position += samplesgot;
file_time = (float)position /
- dc.audioFormat.sampleRate;
+ dc.audio_format.sampleRate;
format_samples(Bps, chunk,
- samplesgot * dc.audioFormat.channels);
+ samplesgot * dc.audio_format.channels);
- ob_send(NULL, 0, chunk,
- samplesgot * outsamplesize,
- file_time, bitrate,
- replayGainInfo);
+ ob_send(chunk, samplesgot * outsamplesize,
+ file_time, bitrate, replayGainInfo);
}
} while (samplesgot == samplesreq);
-
- ob_flush();
}
static char *wavpack_tag(WavpackContext *wpc, char *key)
@@ -398,6 +387,7 @@ static int can_seek(void *id)
return ((InputStreamPlus *)id)->is->seekable;
}
+/* FIXME: remove C99 initializers */
static WavpackStreamReader mpd_is_reader = {
.read_bytes = read_bytes,
.get_pos = get_pos,
@@ -453,38 +443,27 @@ static int wavpack_streamdecode(InputStream *is)
int canseek;
/* Try to find wvc */
+ /* wvc being the "correction" file to supplement the original .wv */
do {
char tmp[MPD_PATH_MAX];
const char *utf8url;
size_t len;
err = 1;
- /*
- * As we use dc.utf8url, this function will be bad for
- * single files. utf8url is not absolute file path :/
- */
- utf8url = get_song_url(tmp, dc.current_song);
- if (utf8url == NULL) {
- break;
- }
-
- len = strlen(utf8url);
- if (!len) {
+ /* This is the only reader of dc.current_song */
+ if (!(utf8url = get_song_url(tmp, dc.current_song)))
break;
- }
- wvc_url = (char *)xmalloc(len + 2); /* +2: 'c' and EOS */
- if (wvc_url == NULL) {
+ if (!(len = strlen(utf8url)))
break;
- }
+ wvc_url = (char *)xmalloc(len + sizeof("c"));
memcpy(wvc_url, utf8url, len);
wvc_url[len] = 'c';
wvc_url[len + 1] = '\0';
- if (openInputStream(&is_wvc, wvc_url)) {
+ if (openInputStream(&is_wvc, wvc_url))
break;
- }
/*
* And we try to buffer in order to get know
@@ -500,17 +479,17 @@ static int wavpack_streamdecode(InputStream *is)
break;
}
+ /* FIXME: replace with future "peek" function */
if (bufferInputStream(&is_wvc) >= 0) {
err = 0;
break;
}
- if (dc.stop) {
+ if (dc_intr())
break;
- }
/* Save some CPU */
- my_usleep(1000);
+ my_usleep(1000); /* FIXME: remove */
}
if (err) {
closeInputStream(&is_wvc);
diff --git a/src/inputStream_http.c b/src/inputStream_http.c
index 48972ac8b..a6c715459 100644
--- a/src/inputStream_http.c
+++ b/src/inputStream_http.c
@@ -252,7 +252,7 @@ static int trigger_action(struct http_data *data,
goto out;
}
if (nonblocking)
- cond_timedwait(&data->action_cond, 1);
+ cond_timedwait(&data->action_cond, 100);
else
cond_wait(&data->action_cond);
ret = 0;
@@ -279,7 +279,7 @@ static int take_action(struct http_data *data)
xclose(data->fd);
data->fd = -1;
data->action = CONN_ACTION_NONE;
- cond_signal_sync(&data->action_cond);
+ cond_signal(&data->action_cond);
cond_leave(&data->action_cond);
return 1;
}
@@ -436,7 +436,7 @@ static void await_buffer_space(struct http_data *data)
static void feed_starved(struct http_data *data)
{
assert(pthread_equal(data->io_thread, pthread_self()));
- cond_signal_async(&data->empty_cond);
+ cond_signal(&data->empty_cond);
}
static int starved_wait(struct http_data *data, const long sec)
@@ -449,7 +449,7 @@ static int awaken_buffer_task(struct http_data *data)
{
assert(!pthread_equal(data->io_thread, pthread_self()));
- return ! cond_signal_async(&data->full_cond);
+ return ! cond_signal_trysync(&data->full_cond);
}
static ssize_t buffer_data(InputStream *is)
diff --git a/src/interface.c b/src/interface.c
index db5959f3a..45b81a2f7 100644
--- a/src/interface.c
+++ b/src/interface.c
@@ -495,10 +495,7 @@ int doIOForInterfaces(void)
registered_IO_add_fds(&fdmax, &rfds, &wfds, &efds);
- main_notify_lock();
selret = select(fdmax + 1, &rfds, &wfds, &efds, NULL);
- main_notify_unlock();
-
if (selret < 0 && errno == EINTR)
break;
diff --git a/src/log.c b/src/log.c
index 8a2d48410..aac7349d1 100644
--- a/src/log.c
+++ b/src/log.c
@@ -75,6 +75,7 @@ static void do_log(FILE *fp, const char *fmt, va_list args)
{
if (!stdout_mode)
fwrite(log_date(), LOG_DATE_LEN, 1, fp);
+ fprintf(fp, "%08x: ", pthread_self());
vfprintf(fp, fmt, args);
}
diff --git a/src/main.c b/src/main.c
index a838d0349..e02085294 100644
--- a/src/main.c
+++ b/src/main.c
@@ -20,7 +20,6 @@
#include "command.h"
#include "playlist.h"
#include "directory.h"
-#include "player.h"
#include "listen.h"
#include "conf.h"
#include "path.h"
@@ -45,6 +44,7 @@
#include "zeroconf.h"
#include "main_notify.h"
#include "os_compat.h"
+#include "outputBuffer.h"
#define SYSTEM_CONFIG_FILE_LOCATION "/etc/mpd.conf"
#define USER_CONFIG_FILE_LOCATION "/.mpdconf"
@@ -431,8 +431,7 @@ int main(int argc, char *argv[])
initZeroconf();
openVolumeDevice();
- decoderInit();
- playerInit();
+ decoder_init();
read_state_file();
while (COMMAND_RETURN_KILL != doIOForInterfaces() &&
@@ -443,7 +442,7 @@ int main(int argc, char *argv[])
}
write_state_file();
- playerKill();
+ ob_trigger_action(OB_ACTION_PAUSE_SET);
finishZeroconf();
freeAllInterfaces();
closeAllListenSockets();
diff --git a/src/main_notify.c b/src/main_notify.c
index 978a401ad..2c546633d 100644
--- a/src/main_notify.c
+++ b/src/main_notify.c
@@ -19,7 +19,6 @@
*/
#include "main_notify.h"
-#include "notify.h"
#include "utils.h"
#include "ioops.h"
#include "gcc.h"
@@ -27,11 +26,6 @@
static struct ioOps main_notify_IO;
static int main_pipe[2];
-static pthread_t main_task;
-static pthread_cond_t main_wakeup = PTHREAD_COND_INITIALIZER;
-static pthread_mutex_t main_wakeup_mutex = PTHREAD_MUTEX_INITIALIZER;
-static volatile int pending;
-static pthread_mutex_t select_mutex = PTHREAD_MUTEX_INITIALIZER;
static int ioops_fdset(fd_set * rfds,
mpd_unused fd_set * wfds, mpd_unused fd_set * efds)
@@ -40,20 +34,19 @@ static int ioops_fdset(fd_set * rfds,
return main_pipe[0];
}
-static void consume_pipe(void)
-{
- char buffer[2];
- ssize_t r = read(main_pipe[0], buffer, sizeof(buffer));
-
- if (r < 0 && errno != EAGAIN && errno != EINTR)
- FATAL("error reading from pipe: %s\n", strerror(errno));
-}
-
static int ioops_consume(int fd_count, fd_set * rfds,
mpd_unused fd_set * wfds, mpd_unused fd_set * efds)
{
+ char buffer[4096];
+ ssize_t r;
+
if (FD_ISSET(main_pipe[0], rfds)) {
- consume_pipe();
+ do {
+ r = read(main_pipe[0], buffer, sizeof(buffer));
+ } while (r > 0);
+
+ if (r < 0 && errno != EAGAIN && errno != EINTR)
+ FATAL("error reading from pipe: %s\n", strerror(errno));
FD_CLR(main_pipe[0], rfds);
fd_count--;
}
@@ -62,65 +55,17 @@ static int ioops_consume(int fd_count, fd_set * rfds,
void init_main_notify(void)
{
- if (pipe(main_pipe) < 0)
- FATAL("Couldn't open pipe: %s", strerror(errno));
- if (set_nonblocking(main_pipe[0]) < 0)
- FATAL("Couldn't set non-blocking on main_notify fd: %s",
- strerror(errno));
- if (set_nonblocking(main_pipe[1]) < 0)
- FATAL("Couldn't set non-blocking on main_notify fd: %s",
- strerror(errno));
+ init_async_pipe(main_pipe);
main_notify_IO.fdset = ioops_fdset;
main_notify_IO.consume = ioops_consume;
registerIO(&main_notify_IO);
- main_task = pthread_self();
-}
-
-static int wakeup_via_pipe(void)
-{
- int ret = pthread_mutex_trylock(&select_mutex);
- if (ret == EBUSY) {
- ssize_t w = write(main_pipe[1], "", 1);
- if (w < 0 && errno != EAGAIN && errno != EINTR)
- FATAL("error writing to pipe: %s\n",
- strerror(errno));
- return 1;
- } else {
- pthread_mutex_unlock(&select_mutex);
- return 0;
- }
}
void wakeup_main_task(void)
{
- assert(!pthread_equal(main_task, pthread_self()));
-
- pending = 1;
+ ssize_t w = write(main_pipe[1], "", 1);
- if (!wakeup_via_pipe())
- pthread_cond_signal(&main_wakeup);
-}
-
-void main_notify_lock(void)
-{
- assert(pthread_equal(main_task, pthread_self()));
- pthread_mutex_lock(&select_mutex);
-}
-
-void main_notify_unlock(void)
-{
- assert(pthread_equal(main_task, pthread_self()));
- pthread_mutex_unlock(&select_mutex);
-}
-
-void wait_main_task(void)
-{
- assert(pthread_equal(main_task, pthread_self()));
-
- pthread_mutex_lock(&main_wakeup_mutex);
- if (!pending)
- pthread_cond_wait(&main_wakeup, &main_wakeup_mutex);
- pending = 0;
- pthread_mutex_unlock(&main_wakeup_mutex);
+ if (w < 0 && errno != EAGAIN && errno != EINTR)
+ FATAL("error writing to pipe: %s\n",
+ strerror(errno));
}
-
diff --git a/src/main_notify.h b/src/main_notify.h
index c7bba4440..db36042a7 100644
--- a/src/main_notify.h
+++ b/src/main_notify.h
@@ -25,10 +25,4 @@ void init_main_notify(void);
void wakeup_main_task(void);
-void wait_main_task(void);
-
-void main_notify_lock(void);
-
-void main_notify_unlock(void);
-
#endif /* MAIN_NOTIFY_H */
diff --git a/src/myfprintf.c b/src/myfprintf.c
index 0732caf02..7e4f4678d 100644
--- a/src/myfprintf.c
+++ b/src/myfprintf.c
@@ -41,7 +41,7 @@ static void blockingWrite(const int fd, const char *string, size_t len)
void vfdprintf(const int fd, const char *fmt, va_list args)
{
- static char buffer[BUFFER_LENGTH];
+ char buffer[BUFFER_LENGTH];
char *buf = buffer;
size_t len;
diff --git a/src/notify.c b/src/notify.c
deleted file mode 100644
index ed46829ae..000000000
--- a/src/notify.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/* 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
- */
-
-#include "notify.h"
-
-int notify_init(Notify *notify)
-{
- int ret;
-
- ret = pthread_mutex_init(&notify->mutex, NULL);
- if (ret != 0)
- return ret;
-
- ret = pthread_cond_init(&notify->cond, NULL);
- if (ret != 0) {
- pthread_mutex_destroy(&notify->mutex);
- return ret;
- }
-
- notify->pending = 0;
-
- return 0;
-}
-
-void notify_enter(Notify *notify)
-{
- pthread_mutex_lock(&notify->mutex);
-}
-
-void notify_leave(Notify *notify)
-{
- pthread_mutex_unlock(&notify->mutex);
-}
-
-void notify_wait(Notify *notify)
-{
- if (!notify->pending)
- pthread_cond_wait(&notify->cond, &notify->mutex);
- notify->pending = 0;
-}
-
-void notify_signal(Notify *notify)
-{
- notify->pending = 1;
- pthread_cond_signal(&notify->cond);
-}
-
-void notify_signal_sync(Notify *notify)
-{
- pthread_mutex_lock(&notify->mutex);
- notify_signal(notify);
- pthread_mutex_unlock(&notify->mutex);
-}
diff --git a/src/notify.h b/src/notify.h
deleted file mode 100644
index 0fbc74479..000000000
--- a/src/notify.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* 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 NOTIFY_H
-#define NOTIFY_H
-
-#include "os_compat.h"
-
-typedef struct _Notify {
- pthread_mutex_t mutex;
- pthread_cond_t cond;
- int pending;
-} Notify;
-
-int notify_init(Notify *notify);
-
-/**
- * The thread which shall be notified by this object must call this
- * function before any notify_wait() invocation. It locks the mutex.
- */
-void notify_enter(Notify *notify);
-
-/**
- * Neutralize notify_leave().
- */
-void notify_leave(Notify *notify);
-
-/**
- * Wait for a notification. Return immediately if we have already
- * been notified since we last returned from notify_wait().
- */
-void notify_wait(Notify *notify);
-
-/**
- * Notify the thread. This function never blocks.
- */
-void notify_signal(Notify *notify);
-
-/**
- * Notify the thread synchonously, i.e. wait until it has received the
- * notification.
- */
-void notify_signal_sync(Notify *notify);
-
-#endif
diff --git a/src/outputBuffer.c b/src/outputBuffer.c
index b0cfc00df..26e6077f2 100644
--- a/src/outputBuffer.c
+++ b/src/outputBuffer.c
@@ -20,252 +20,543 @@
#include "utils.h"
#include "normalize.h"
-#include "playerData.h"
-
-void ob_init(unsigned int size)
+#include "ringbuf.h"
+#include "condition.h"
+#include "song.h"
+#include "main_notify.h"
+#include "player_error.h"
+#include "log.h"
+#include "action_status.h"
+
+/* typically have 2048-4096 of these structs, so pack tightly */
+struct ob_chunk {
+ mpd_uint16 len; /* 0: skip this chunk */
+ mpd_uint16 bit_rate;
+ float time;
+ mpd_uint8 seq; /* see seq_ok() for explanation */
+ char data[CHUNK_SIZE];
+};
+
+enum ob_xfade_state {
+ XFADE_DISABLED = 0,
+ XFADE_ENABLED
+};
+
+static struct condition ob_action_cond = STATIC_COND_INITIALIZER;
+static struct condition ob_halt_cond = STATIC_COND_INITIALIZER;
+static struct condition ob_seq_cond = STATIC_COND_INITIALIZER;
+
+struct output_buffer {
+ struct ringbuf *index; /* index for chunks */
+ struct ob_chunk *chunks;
+ size_t nr_bpp; /* nr (chunks) buffered before play */
+ enum ob_state state; /* protected by ob_action_cond */
+ enum ob_action action; /* protected by ob_action_cond */
+ enum ob_xfade_state xfade_state; /* thread-internal */
+ int sw_vol;
+ int bit_rate;
+ float total_time;
+ float elapsed_time;
+ AudioFormat audio_format;
+ size_t xfade_cur;
+ size_t xfade_max;
+ float xfade_time;
+ void *conv_buf;
+ size_t conv_buf_len;
+ pthread_t thread;
+ ConvState conv_state;
+ unsigned int seq_drop;
+ unsigned int seq_player; /* only gets changed by ob.thread */
+ mpd_uint8 seq_decoder; /* only gets changed by dc.thread */
+ struct ringbuf preseek_index;
+ enum ob_state preseek_state;
+ mpd_uint16 *preseek_len;
+};
+
+static struct output_buffer ob;
+
+#include "outputBuffer_xfade.h"
+#include "outputBuffer_accessors.h"
+
+static void ob_free(void)
{
- assert(size > 0);
-
- memset(&ob.convState, 0, sizeof(ConvState));
- ob.chunks = xmalloc(size * sizeof(*ob.chunks));
- ob.size = size;
- ob.begin = 0;
- ob.end = 0;
- ob.lazy = 0;
- ob.chunks[0].chunkSize = 0;
+ free(ob.chunks);
+ ringbuf_free(ob.index);
}
-void ob_free(void)
+static enum action_status ob_do_stop(void);
+static void stop_playback(void)
{
- assert(ob.chunks != NULL);
- free(ob.chunks);
+ assert(pthread_equal(pthread_self(), ob.thread));
+ cond_enter(&ob_action_cond);
+ ob_do_stop();
+ cond_leave(&ob_action_cond);
}
-void ob_clear(void)
+void ob_trigger_action(enum ob_action action)
{
- ob.end = ob.begin;
- ob.chunks[ob.end].chunkSize = 0;
+ /*
+ * This can be called by both dc.thread and main_thread, but only one
+ * action can be in progress at once. So we use this private mutex
+ * to protect against simultaneous invocations stepping over
+ * each other
+ */
+ static pthread_mutex_t trigger_lock = PTHREAD_MUTEX_INITIALIZER;
+ pthread_mutex_lock(&trigger_lock);
+ DEBUG(__FILE__": %d action: %d\n", __LINE__, action);
+ assert(!pthread_equal(pthread_self(), ob.thread));
+ DEBUG(__FILE__":%s %d\n", __func__, __LINE__);
+ cond_enter(&ob_action_cond);
+ DEBUG(__FILE__":%s %d\n", __func__, __LINE__);
+ assert(ob.action == OB_ACTION_NONE);
+
+ if (pthread_equal(pthread_self(), dc.thread))
+ assert(action == OB_ACTION_PLAY ||
+ action == OB_ACTION_SEEK_START ||
+ action == OB_ACTION_SEEK_FINISH);
+ else
+ assert(action != OB_ACTION_PLAY &&
+ action != OB_ACTION_SEEK_START &&
+ action != OB_ACTION_SEEK_FINISH);
+ ob.action = action;
+ do {
+ switch (ob.state) {
+ case OB_STATE_PAUSE:
+ case OB_STATE_STOP:
+ case OB_STATE_SEEK:
+ DEBUG(__FILE__":%s %d\n", __func__, __LINE__);
+ cond_signal_sync(&ob_halt_cond);
+ DEBUG(__FILE__":%s %d\n", __func__, __LINE__);
+ break;
+ default: break;
+ }
+ DEBUG(__FILE__":%s %d\n", __func__, __LINE__);
+ cond_wait(&ob_action_cond);
+ } while (ob.action != OB_ACTION_NONE);
+ DEBUG(__FILE__":%s %d\n", __func__, __LINE__);
+ assert(ob.action == OB_ACTION_NONE);
+ cond_leave(&ob_action_cond);
+ DEBUG(__FILE__":%s %d\n", __func__, __LINE__);
+ pthread_mutex_unlock(&trigger_lock);
}
-/** return the index of the chunk after i */
-static inline unsigned successor(unsigned i)
+static enum action_status ob_finalize_action(void)
{
- assert(i <= ob.size);
-
- ++i;
- return i == ob.size ? 0 : i;
+ assert(pthread_equal(pthread_self(), ob.thread));
+ ob.action = OB_ACTION_NONE;
+ /* DEBUG(__FILE__ ":%s signaling %d\n", __func__, __LINE__); */
+ cond_signal(&ob_action_cond);
+ cond_leave(&ob_action_cond);
+ return AS_COMPLETE;
}
-/**
- * Mark the tail chunk as "full" and wake up the player if is waiting
- * for the decoder.
- */
-static void output_buffer_expand(unsigned i)
+/* marks all buffered chunks with sequence number matching `seq' as invalid */
+static enum action_status ob_do_drop(void)
{
- int was_empty = !ob.lazy || ob_is_empty();
-
- assert(i == (ob.end + 1) % ob.size);
- assert(i != ob.end);
-
- ob.end = i;
- ob.chunks[i].chunkSize = 0;
- if (was_empty)
- /* if the buffer was empty, the player thread might be
- waiting for us; wake it up now that another decoded
- buffer has become available. */
- decoder_wakeup_player();
+ struct iovec vec[2];
+ long i;
+ unsigned int seq_drop;
+
+ cond_enter(&ob_seq_cond);
+ seq_drop = ob.seq_drop;
+ /* drop the audio that we've already pushed to the device, too */
+ if (seq_drop == ob.seq_player)
+ dropBufferedAudio();
+ cond_leave(&ob_seq_cond);
+
+ assert(pthread_equal(pthread_self(), ob.thread));
+ for (i = (long)ringbuf_get_read_vector(ob.index, vec); --i >= 0; ) {
+ struct ob_chunk *c = get_chunk(vec, i);
+ assert(c);
+ if (c->seq == seq_drop)
+ c->len = 0;
+ }
+ return ob_finalize_action();
}
-void ob_flush(void)
+static void close_audio_devices(void);
+static enum action_status ob_do_pause(void)
{
- ob_chunk *chunk = ob_get_chunk(ob.end);
-
- if (chunk->chunkSize > 0) {
- unsigned int next = successor(ob.end);
- if (next == ob.begin)
- /* all buffers are full; we have to wait for
- the player to free one, so don't flush
- right now */
- return;
+ assert(pthread_equal(pthread_self(), ob.thread));
+ ob.xfade_state = XFADE_DISABLED;
+ /*
+ * This will eventually set certain outputs (like shout) into 'pause'
+ * state where it'll just play silence instead of disconnecting
+ * listeners
+ */
+ close_audio_devices();
+ ob.state = OB_STATE_PAUSE;
+ return AS_INPROGRESS;
+}
- output_buffer_expand(next);
+static void reader_reset_buffer(void)
+{
+ struct iovec vec[2];
+ size_t nr;
+ long i;
+
+ assert(pthread_equal(pthread_self(), ob.thread));
+ nr = ringbuf_get_read_vector(ob.index, vec);
+ for (i = nr; --i >= 0; ) {
+ struct ob_chunk *c = get_chunk(vec, i);
+ assert(c);
+ c->len = 0;
}
+ ringbuf_read_advance(ob.index, nr);
}
-void ob_set_lazy(int lazy)
+static void ob_seq_player_set(unsigned int seq_num)
{
- ob.lazy = lazy;
+ cond_enter(&ob_seq_cond);
+ ob.seq_player = seq_num;
+ cond_signal(&ob_seq_cond);
+ cond_leave(&ob_seq_cond);
}
-int ob_is_empty(void)
+static enum action_status ob_do_stop(void)
{
- return ob.begin == ob.end;
+ assert(pthread_equal(pthread_self(), ob.thread));
+ if (ob.state != OB_STATE_STOP) {
+ ob.elapsed_time = 0;
+ reader_reset_buffer();
+ ob.xfade_state = XFADE_DISABLED;
+ close_audio_devices();
+ ob.state = OB_STATE_STOP;
+ ob_seq_player_set((unsigned int)ob.seq_decoder);
+ }
+ return AS_INPROGRESS;
}
-void ob_shift(void)
+/*
+ * we need to reset the buffer *before* we seek because the decoder
+ * _may_ try to flush out the last remnants of the previously decoded audio,
+ * so we need to ensure there is space available for that
+ */
+static enum action_status ob_do_seek_start(void)
{
- assert(ob.begin != ob.end);
- assert(ob.begin < ob.size);
-
- ob.begin = successor(ob.begin);
+ int i;
+
+ assert(pthread_equal(pthread_self(), ob.thread));
+
+ /* preserve pre-seek ringbuf and state information */
+ memcpy(&ob.preseek_index, ob.index, sizeof(struct ringbuf));
+ for (i = ob.preseek_index.size; --i >= 0; )
+ ob.preseek_len[i] = ob.chunks[i].len;
+ ob.preseek_state = ob.state;
+ ob.state = OB_STATE_SEEK;
+ reader_reset_buffer();
+ return AS_INPROGRESS;
}
-unsigned int ob_relative(const unsigned i)
+static enum action_status ob_do_seek_finish(void)
{
- if (i >= ob.begin)
- return i - ob.begin;
- else
- return i + ob.size - ob.begin;
+ assert(pthread_equal(pthread_self(), ob.thread));
+ assert(ob.state == OB_STATE_SEEK);
+ ob.state = ob.preseek_state;
+ if (dc.seek_where < 0) {
+ int i;
+ assert(dc.seek_where == DC_SEEK_MISMATCH ||
+ dc.seek_where == DC_SEEK_ERROR);
+
+ /* restore the old ringbuf index if we failed to seek */
+ memcpy(ob.index, &ob.preseek_index, sizeof(struct ringbuf));
+ for (i = ob.preseek_index.size; --i >= 0; )
+ ob.chunks[i].len = ob.preseek_len[i];
+ } else {
+ assert(dc.seek_where >= 0);
+ ob.xfade_state = XFADE_DISABLED;
+ ob.elapsed_time = dc.seek_where;
+ ob.total_time = dc.total_time;
+ reader_reset_buffer();
+ dropBufferedAudio();
+ ob_seq_player_set((unsigned int)ob.seq_decoder);
+ }
+ return ob_finalize_action();
}
-unsigned ob_available(void)
+static enum action_status ob_take_action(void)
{
- return ob_relative(ob.end);
+ assert(pthread_equal(pthread_self(), ob.thread));
+ if (mpd_likely(ob.action == OB_ACTION_NONE))
+ return AS_COMPLETE;
+ DEBUG(__FILE__": %s %d\n", __func__, __LINE__);
+ cond_enter(&ob_action_cond);
+ DEBUG(__FILE__": %s %d action: %d\n", __func__, __LINE__, ob.action);
+ switch (ob.action) {
+ case OB_ACTION_NONE: return ob_finalize_action();
+ case OB_ACTION_PLAY: ob.state = OB_STATE_PLAY; break;
+ case OB_ACTION_DROP: return ob_do_drop();
+ case OB_ACTION_SEEK_START: return ob_do_seek_start();
+ case OB_ACTION_SEEK_FINISH: return ob_do_seek_finish();
+ case OB_ACTION_PAUSE_SET:
+ if (ob.state == OB_STATE_PLAY)
+ return ob_do_pause();
+ ob.state = OB_STATE_PAUSE;
+ break;
+ case OB_ACTION_PAUSE_UNSET:
+ if (ob.state == OB_STATE_PAUSE)
+ ob.state = OB_STATE_PLAY;
+ break;
+ case OB_ACTION_PAUSE_FLIP:
+ switch (ob.state) {
+ case OB_STATE_PLAY: return ob_do_pause();
+ case OB_STATE_PAUSE: ob.state = OB_STATE_PLAY; break;
+ default: break;
+ }
+ break;
+ case OB_ACTION_STOP: return ob_do_stop();
+ case OB_ACTION_QUIT:
+ close_audio_devices();
+ ob.state = OB_STATE_QUIT;
+ return AS_INPROGRESS;
+ }
+ return ob_finalize_action();
}
-int ob_absolute(const unsigned relative)
+/*
+ * looks up the chunk given by index `i', returns NULL if `i' is beyond
+ * the end of the buffer. This allows us to treat our chunks array
+ * like an infinite, rotating buffer. The first available chunk
+ * is always indexed as `0', the second one as `1', and so on...
+ */
+static struct ob_chunk *get_chunk(struct iovec vec[2], size_t i)
{
- unsigned i, max;
-
- max = ob.end;
- if (max < ob.begin)
- max += ob.size;
- i = (unsigned)ob.begin + relative;
- if (i >= max)
- return -1;
+ if (vec[0].iov_len > i)
+ return &ob.chunks[vec[0].iov_base + i - ob.index->buf];
+ if (vec[1].iov_base) {
+ assert(vec[0].iov_len > 0);
+ i -= vec[0].iov_len;
+ if (vec[1].iov_len > i)
+ return &ob.chunks[vec[1].iov_base + i - ob.index->buf];
+ }
+ return NULL;
+}
- if (i >= ob.size)
- i -= ob.size;
+static void prevent_buffer_underrun(void)
+{
+ static const char silence[CHUNK_SIZE];
+ if (playAudio(silence, sizeof(silence)) < 0)
+ stop_playback();
+}
- return (int)i;
+/* causes ob_do_drop() to be called (and waits for completion) */
+void ob_drop_audio(enum ob_drop_type type)
+{
+ assert(!pthread_equal(pthread_self(), ob.thread));
+ assert(!pthread_equal(pthread_self(), dc.thread));
+ assert(dc.state == DC_STATE_STOP); /* not needed, just a good idea */
+ cond_enter(&ob_seq_cond);
+ switch (type) {
+ case OB_DROP_DECODED: ob.seq_drop = ob.seq_decoder; break;
+ case OB_DROP_PLAYING: ob.seq_drop = ob.seq_player; break;
+ }
+ cond_leave(&ob_seq_cond);
+ /* DEBUG("dropping %u\n", ob.seq_drop); */
+ ob_trigger_action(OB_ACTION_DROP);
+ /* DEBUG("done dropping %u\n", ob.seq_drop); */
}
-ob_chunk * ob_get_chunk(const unsigned i)
+void ob_wait_sync(void)
{
- assert(i < ob.size);
+ assert(!pthread_equal(pthread_self(), dc.thread));
+ assert(!pthread_equal(pthread_self(), ob.thread));
+
+ /* DEBUG(__FILE__": %s %d\n", __func__, __LINE__); */
+ cond_enter(&ob_seq_cond);
+ while (ob.seq_player != ob.seq_decoder) {
+ /* DEBUG("%s %d seq_player:%u seq_decoder:%u\n", */
+ /* __func__, __LINE__, ob.seq_player, ob.seq_decoder); */
+ cond_wait(&ob_seq_cond);
+ /* DEBUG("%s %d seq_player:%u seq_decoder:%u\n", */
+ /* __func__, __LINE__, ob.seq_player, ob.seq_decoder); */
+ }
+ cond_leave(&ob_seq_cond);
+ /* DEBUG(__FILE__": %s %d\n", __func__, __LINE__); */
+}
- return &ob.chunks[i];
+/* call this exactly once before decoding each song */
+void ob_advance_sequence(void)
+{
+ assert(pthread_equal(pthread_self(), dc.thread));
+ DEBUG(__FILE__": %s %d\n", __func__, __LINE__);
+ cond_enter(&ob_seq_cond);
+ ++ob.seq_decoder;
+ cond_leave(&ob_seq_cond);
+ DEBUG(__FILE__": %s %d\n", __func__, __LINE__);
+ DEBUG("ob.seq_decoder: %d\n", ob.seq_decoder);
}
-/**
- * Return the tail chunk which has room for additional data. If there
- * is no room in the queue, this function blocks until the player
- * thread has finished playing its current chunk.
- *
- * @return the positive index of the new chunk; OUTPUT_BUFFER_DC_SEEK
- * if another thread requested seeking; OUTPUT_BUFFER_DC_STOP if
- * another thread requested stopping the decoder.
+/*
+ * Returns true if output buffer is playing the song we're decoding
*/
-static int tailChunk(InputStream * inStream,
- int seekable, float data_time, mpd_uint16 bitRate)
+int ob_synced(void)
{
- unsigned int next;
- ob_chunk *chunk;
-
- chunk = ob_get_chunk(ob.end);
- assert(chunk->chunkSize <= sizeof(chunk->data));
- if (chunk->chunkSize == sizeof(chunk->data)) {
- /* this chunk is full; allocate a new chunk */
- next = successor(ob.end);
- while (ob.begin == next) {
- /* all chunks are full of decoded data; wait
- for the player to free one */
-
- if (dc.stop)
- return OUTPUT_BUFFER_DC_STOP;
-
- if (dc.seek) {
- if (seekable) {
- return OUTPUT_BUFFER_DC_SEEK;
- } else {
- dc.seekError = 1;
- dc.seek = 0;
- decoder_wakeup_player();
- }
- }
- if (!inStream || bufferInputStream(inStream) <= 0) {
- decoder_sleep();
- }
- }
+ int ret;
+ assert(!pthread_equal(pthread_self(), dc.thread));
+ assert(!pthread_equal(pthread_self(), ob.thread));
+ /* assert(pthread_equal(pthread_self(), main_thread)); */
+ cond_enter(&ob_seq_cond);
+ ret = (ob.seq_decoder == ob.seq_player);
+ cond_leave(&ob_seq_cond);
+ return ret;
+}
+
+static void new_song_chunk(struct ob_chunk *a)
+{
+ assert(pthread_equal(pthread_self(), ob.thread));
+ ob.xfade_state = XFADE_DISABLED;
+ ob.total_time = dc.total_time;
+ /* DEBUG("ob.total_time: %f\n", ob.total_time); */
+ ob_seq_player_set((unsigned int)a->seq);
+ wakeup_main_task(); /* sync playlist */
+}
+
+#include "outputBuffer_audio.h"
- output_buffer_expand(next);
- chunk = ob_get_chunk(next);
- assert(chunk->chunkSize == 0);
+static void play_next_chunk(void)
+{
+ struct iovec vec[2];
+ struct ob_chunk *a;
+ size_t nr;
+ static float last_time;
+
+ assert(pthread_equal(pthread_self(), ob.thread));
+
+ nr = ringbuf_get_read_vector(ob.index, vec);
+ if (mpd_unlikely(!nr &&
+ (dc.state == DC_STATE_STOP) &&
+ ! playlist_playing())) {
+ stop_playback();
+ return;
+ }
+ if (nr < ((ob.xfade_time <= 0) ? ob.nr_bpp : xfade_chunks_needed(vec)))
+ {
+ prevent_buffer_underrun();
+ return;
}
- if (chunk->chunkSize == 0) {
- /* if the chunk is empty, nobody has set bitRate and
- times yet */
+ a = get_chunk(vec, 0);
+ assert(a);
+ if (! a->len)
+ goto out;
- chunk->bitRate = bitRate;
- chunk->times = data_time;
+ if (nr > 1 && ob.xfade_state == XFADE_ENABLED) {
+ struct ob_chunk *b = get_chunk(vec, ob.xfade_max);
+ xfade_mix(a, b);
}
- return ob.end;
+ last_time = ob.elapsed_time = a->time;
+ ob.bit_rate = a->bit_rate;
+
+ if (mpd_unlikely(ob.seq_player != a->seq)) {
+ if (open_audio_devices(1) < 0)
+ return;
+ new_song_chunk(a);
+ }
+ /* pcm_volumeChange(a->data, a->len, &ob.audio_format, ob.sw_vol); */
+ if (playAudio(a->data, a->len) < 0)
+ stop_playback();
+ a->len = 0; /* mark the chunk as empty for ob_send() */
+out:
+ ringbuf_read_advance(ob.index, 1);
+
+ /* unblock ob_send() if it was waiting on a full buffer */
+ dc_try_unhalt();
}
-int ob_send(InputStream * inStream,
- int seekable, void *dataIn,
- size_t dataInLen, float data_time, mpd_uint16 bitRate,
- ReplayGainInfo * replayGainInfo)
+static void * ob_task(mpd_unused void *arg)
{
- size_t dataToSend;
- char *data;
- size_t datalen;
- static char *convBuffer;
- static size_t convBufferLen;
- ob_chunk *chunk = NULL;
-
- if (cmpAudioFormat(&(ob.audioFormat), &(dc.audioFormat)) == 0) {
- data = dataIn;
- datalen = dataInLen;
- } else {
- datalen = pcm_sizeOfConvBuffer(&(dc.audioFormat), dataInLen,
- &(ob.audioFormat));
- if (datalen > convBufferLen) {
- if (convBuffer != NULL)
- free(convBuffer);
- convBuffer = xmalloc(datalen);
- convBufferLen = datalen;
+ enum action_status as;
+
+ assert(pthread_equal(pthread_self(), ob.thread));
+ cond_enter(&ob_halt_cond);
+ while (1) {
+ as = ob_take_action();
+ switch (ob.state) {
+ case OB_STATE_PLAY:
+ assert(as == AS_COMPLETE);
+ if (open_audio_devices(0) >= 0)
+ play_next_chunk();
+ break;
+ case OB_STATE_STOP:
+ case OB_STATE_PAUSE:
+ case OB_STATE_SEEK:
+ assert(as != AS_DEFERRED);
+ if (as == AS_INPROGRESS)
+ ob_finalize_action();
+ cond_wait(&ob_halt_cond);
+ break;
+ case OB_STATE_QUIT: goto out;
}
- data = convBuffer;
- datalen = pcm_convertAudioFormat(&(dc.audioFormat), dataIn,
- dataInLen, &(ob.audioFormat),
- data, &(ob.convState));
}
+out:
+ cond_leave(&ob_halt_cond);
+ assert(ob.state == OB_STATE_QUIT);
+ assert(as == AS_INPROGRESS);
+ ob_finalize_action();
+ return NULL;
+}
- if (replayGainInfo && (replayGainState != REPLAYGAIN_OFF))
- doReplayGain(replayGainInfo, data, datalen, &ob.audioFormat);
- else if (normalizationEnabled)
- normalizeData(data, datalen, &ob.audioFormat);
-
- while (datalen) {
- int chunk_index = tailChunk(inStream, seekable,
- data_time, bitRate);
- if (chunk_index < 0)
- return chunk_index;
-
- chunk = ob_get_chunk(chunk_index);
-
- dataToSend = sizeof(chunk->data) - chunk->chunkSize;
- if (dataToSend > datalen)
- dataToSend = datalen;
-
- memcpy(chunk->data + chunk->chunkSize, data, dataToSend);
- chunk->chunkSize += dataToSend;
- datalen -= dataToSend;
- data += dataToSend;
- }
+void ob_init(size_t size)
+{
+ pthread_attr_t attr;
+ assert(size > 0 && !ob.index && !ob.chunks);
+ ob.index = ringbuf_create(size);
+ ob.chunks = xcalloc(ob.index->size, sizeof(struct ob_chunk));
+ ob.preseek_len = xmalloc(ob.index->size * sizeof(ob.chunks[0].len));
+ ob.nr_bpp = 1;
+ ob.state = OB_STATE_STOP;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (pthread_create(&ob.thread, &attr, ob_task, NULL))
+ FATAL("Failed to spawn player task: %s\n", strerror(errno));
+ atexit(ob_free);
+}
- if (chunk != NULL && chunk->chunkSize == sizeof(chunk->data))
- ob_flush();
+void ob_seek_start(void)
+{
+ assert(pthread_equal(pthread_self(), dc.thread));
+ assert(dc.seek_where >= 0);
+ ob_trigger_action(OB_ACTION_SEEK_START);
+}
- return 0;
+void ob_seek_finish(void)
+{
+ assert(pthread_equal(pthread_self(), dc.thread));
+ ob_trigger_action(OB_ACTION_SEEK_FINISH);
}
-void ob_skip(unsigned num)
+/*
+ * if there are any partially written chunk, flush them out to
+ * the output process _before_ decoding the next track
+ */
+void ob_flush(void)
{
- int i = ob_absolute(num);
- if (i >= 0)
- ob.begin = i;
+ struct iovec vec[2];
+
+ assert(pthread_equal(pthread_self(), dc.thread));
+ /* DEBUG(__FILE__":%s %d\n", __func__, __LINE__); */
+
+ if (ringbuf_get_write_vector(ob.index, vec)) {
+ /* DEBUG(__FILE__":%s %d\n", __func__, __LINE__); */
+ struct ob_chunk *c = get_chunk(vec, 0);
+ assert(c);
+ if (c->len) {
+ assert(ob.seq_decoder == c->seq);
+ switch (ob.state) {
+ case OB_STATE_SEEK:
+ assert(0);
+ case OB_STATE_PLAY:
+ case OB_STATE_PAUSE: /* should we assert if paused? */
+ ringbuf_write_advance(ob.index, 1);
+ break;
+ case OB_STATE_STOP:
+ case OB_STATE_QUIT:
+ c->len = 0;
+ }
+ }
+ }
}
+
+#include "outputBuffer_ob_send.h"
diff --git a/src/outputBuffer.h b/src/outputBuffer.h
index 4aea59120..833a29e77 100644
--- a/src/outputBuffer.h
+++ b/src/outputBuffer.h
@@ -25,89 +25,84 @@
#include "inputStream.h"
#include "replayGain.h"
-#define OUTPUT_BUFFER_DC_STOP -1
-#define OUTPUT_BUFFER_DC_SEEK -2
-
-/* pick 1020 since its devisible for 8,16,24, and 32-bit audio */
-#define CHUNK_SIZE 1020
-
-typedef struct _OutputBufferChunk {
- mpd_uint16 chunkSize;
- mpd_uint16 bitRate;
- float times;
- char data[CHUNK_SIZE];
-} ob_chunk;
-
-/**
- * A ring set of buffers where the decoder appends data after the end,
- * and the player consumes data from the beginning.
+/*
+ * As far as audio output is concerned, `stop' is a superset of `pause'
+ * That is, `stop' will drop decoded audio chunks from the buffer
+ * and `pause' will not. Both will stop audio playback immediately
+ * and close audio playback devices (TODO: close mixer devices).
*/
-typedef struct _OutputBuffer {
- ob_chunk *chunks;
-
- unsigned int size;
-
- /** the index of the first decoded chunk */
- unsigned int volatile begin;
-
- /** the index after the last decoded chunk */
- unsigned int volatile end;
-
- /** non-zero if the player thread should only we woken up if
- the buffer becomes non-empty */
- int lazy;
-
- AudioFormat audioFormat;
- ConvState convState;
-} OutputBuffer;
-
-void ob_init(unsigned int size);
+enum ob_action {
+ OB_ACTION_NONE = 0,
+ OB_ACTION_PLAY,
+ OB_ACTION_DROP,
+ OB_ACTION_SEEK_START,
+ OB_ACTION_SEEK_FINISH,
+ OB_ACTION_PAUSE_SET,
+ OB_ACTION_PAUSE_UNSET,
+ OB_ACTION_PAUSE_FLIP,
+ OB_ACTION_STOP,
+ OB_ACTION_QUIT
+};
+
+/* 1020 bytes since its divisible for 8, 16, 24, and 32-bit audio */
+#define CHUNK_SIZE 1020
-void ob_free(void);
+void ob_init(size_t size);
-void ob_clear(void);
+enum ob_drop_type { OB_DROP_DECODED, OB_DROP_PLAYING };
+void ob_drop_audio(enum ob_drop_type type);
-void ob_flush(void);
+/*
+ * Returns true if output buffer is playing the song we're decoding
+ */
+int ob_synced(void);
-/**
- * When a chunk is decoded, we wake up the player thread to tell him
- * about it. In "lazy" mode, we only wake him up when the buffer was
- * previously empty, i.e. when the player thread has really been
- * waiting for us.
+/*
+ * analogous to send(2) or write(2), it will put @data into
+ * the output buffer (like writing to a pipe, the consumer of
+ * which will read and play the contents of the output buffer
+ *
+ * Future direction:
+ * zero-copy functions using vectors:
+ * vec = ob_getv(); decode_to(&vec, ...); ob_vmsplice(&vec, ...);
*/
-void ob_set_lazy(int lazy);
+enum dc_action ob_send(void *data, size_t len, float time,
+ mpd_uint16 bit_rate, ReplayGainInfo *rgi);
-/** is the buffer empty? */
-int ob_is_empty(void);
+/* synchronous and blocking (the only way it should be) */
+void ob_trigger_action(enum ob_action action);
-void ob_shift(void);
+/* synchronous and blocking, called from dc.thread */
+void ob_seek_start(void);
+void ob_seek_finish(void);
-/**
- * what is the position of the specified chunk number, relative to
- * the first chunk in use?
- */
-unsigned int ob_relative(const unsigned i);
+/* boring accessor functions, only called by main-thread */
+unsigned long ob_get_elapsed_time(void);
+unsigned long ob_get_total_time(void);
+unsigned int ob_get_channels(void);
+unsigned int ob_get_bit_rate(void);
+unsigned int ob_get_sample_rate(void);
+unsigned int ob_get_bits(void);
+void ob_set_sw_volume(int volume);
+void ob_set_xfade(float xfade_seconds);
+float ob_get_xfade(void);
-/** determine the number of decoded chunks */
-unsigned ob_available(void);
+enum ob_state {
+ OB_STATE_PLAY = 0,
+ OB_STATE_STOP,
+ OB_STATE_PAUSE,
+ OB_STATE_SEEK,
+ OB_STATE_QUIT
+};
-/**
- * Get the absolute index of the nth used chunk after the first one.
- * Returns -1 if there is no such chunk.
- */
-int ob_absolute(const unsigned relative);
+enum ob_state ob_get_state(void);
-ob_chunk * ob_get_chunk(const unsigned i);
+AudioFormat *ob_audio_format(void);
-/* we send inStream for buffering the inputStream while waiting to
- send the next chunk */
-int ob_send(InputStream * inStream,
- int seekable,
- void *data,
- size_t datalen,
- float data_time,
- mpd_uint16 bitRate, ReplayGainInfo * replayGainInfo);
+void ob_advance_sequence(void);
-void ob_skip(unsigned num);
+void ob_wait_sync(void);
+
+void ob_flush(void);
#endif
diff --git a/src/outputBuffer_accessors.h b/src/outputBuffer_accessors.h
new file mode 100644
index 000000000..11c8887c8
--- /dev/null
+++ b/src/outputBuffer_accessors.h
@@ -0,0 +1,77 @@
+/* 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
+ */
+
+/*
+ * Some of the most boring code in the world that I didn't want cluttering
+ * up outputBuffer.c
+ */
+
+unsigned long ob_get_elapsed_time(void)
+{
+ return (unsigned long)(ob.elapsed_time + 0.5);
+}
+
+unsigned long ob_get_total_time(void)
+{
+ return (unsigned long)(ob.total_time + 0.5);
+}
+
+unsigned int ob_get_bit_rate(void)
+{
+ return (unsigned int)ob.bit_rate;
+}
+
+unsigned int ob_get_channels(void)
+{
+ return (unsigned int)ob.audio_format.channels;
+}
+
+unsigned int ob_get_sample_rate(void)
+{
+ return (unsigned int)ob.audio_format.sampleRate;
+}
+
+unsigned int ob_get_bits(void)
+{
+ return (unsigned int)ob.audio_format.bits;
+}
+
+void ob_set_sw_volume(int volume)
+{
+ ob.sw_vol = (volume > 1000) ? 1000 : (volume < 0 ? 0 : volume);
+}
+
+void ob_set_xfade(float xfade_sec)
+{
+ ob.xfade_time = (xfade_sec < 0) ? 0 : xfade_sec;
+}
+
+float ob_get_xfade(void)
+{
+ return ob.xfade_time;
+}
+
+enum ob_state ob_get_state(void)
+{
+ return ob.state;
+}
+
+AudioFormat *ob_audio_format(void)
+{
+ return &ob.audio_format;
+}
diff --git a/src/outputBuffer_audio.h b/src/outputBuffer_audio.h
new file mode 100644
index 000000000..2af40c95f
--- /dev/null
+++ b/src/outputBuffer_audio.h
@@ -0,0 +1,35 @@
+/* This is where audio devices are managed inside the output buffer thread */
+
+static int audio_opened;
+
+/*
+ * reopen is set when we get a new song and there's a difference
+ * in audio format
+ */
+static int open_audio_devices(int reopen)
+{
+ assert(pthread_equal(pthread_self(), ob.thread));
+
+ if (!reopen && audio_opened)
+ return 0;
+ if (openAudioDevice(&ob.audio_format) >= 0) {
+ audio_opened = 1;
+ return 0;
+ }
+ audio_opened = 0;
+ stop_playback();
+ player_seterror(PLAYER_ERROR_AUDIO, NULL);
+ ERROR("problems opening audio device\n");
+ return -1;
+}
+
+static void close_audio_devices(void)
+{
+ assert(pthread_equal(pthread_self(), ob.thread));
+ DEBUG(__FILE__":%s %d\n", __func__, __LINE__);
+ dropBufferedAudio();
+ closeAudioDevice();
+ audio_opened = 0;
+ /* DEBUG(__FILE__":%s %d\n", __func__, __LINE__); */
+}
+
diff --git a/src/outputBuffer_ob_send.h b/src/outputBuffer_ob_send.h
new file mode 100644
index 000000000..2a4a84763
--- /dev/null
+++ b/src/outputBuffer_ob_send.h
@@ -0,0 +1,133 @@
+/*
+ * This file only contains ob_send() and private functions
+ * needed to implement ob_send()
+ */
+
+/*
+ * This is one of two places where dc.thread can block,
+ * the other is readFromInputStream
+ */
+static enum dc_action await_buffer_space(void)
+{
+ assert(pthread_equal(pthread_self(), dc.thread));
+ /* DEBUG("Waiting for buffer space\n"); */
+ assert(dc.state != DC_STATE_STOP);
+
+ dc_halt();
+ /* DEBUG("done waiting for buffer space\n"); */
+ return dc.action;
+}
+
+/* This will modify its input */
+static void do_audio_conversion(void **data, size_t *len)
+{
+ size_t newlen;
+
+ assert(pthread_equal(pthread_self(), dc.thread));
+ newlen = pcm_sizeOfConvBuffer(&dc.audio_format, *len, &ob.audio_format);
+ if (newlen > ob.conv_buf_len) {
+ ob.conv_buf = xrealloc(ob.conv_buf, newlen);
+ ob.conv_buf_len = newlen;
+ }
+ *len = pcm_convertAudioFormat(&dc.audio_format, *data, *len,
+ &ob.audio_format, ob.conv_buf,
+ &ob.conv_state);
+ *data = ob.conv_buf;
+}
+
+static void ensure_audio_format_sanity(void **data, size_t *len)
+{
+ static mpd_uint8 seq_last;
+
+ assert(pthread_equal(pthread_self(), dc.thread));
+ if (mpd_unlikely(seq_last != ob.seq_decoder)) {
+ seq_last = ob.seq_decoder;
+ if (cmpAudioFormat(&dc.audio_format, &ob.audio_format))
+ getOutputAudioFormat(&dc.audio_format,
+ &ob.audio_format);
+ }
+ if (cmpAudioFormat(&ob.audio_format, &dc.audio_format))
+ do_audio_conversion(data, len);
+}
+
+static void start_playback(void)
+{
+ assert(pthread_equal(pthread_self(), dc.thread));
+ if (mpd_unlikely(ob.state == OB_STATE_STOP &&
+ player_errno == PLAYER_ERROR_NONE)) {
+ ob_trigger_action(OB_ACTION_PLAY);
+ }
+}
+
+enum dc_action
+ob_send(void *data, size_t len,
+ float decode_time, mpd_uint16 bit_rate, ReplayGainInfo * rgi)
+{
+ struct iovec vec[2];
+ struct ob_chunk *c;
+ size_t idx;
+ size_t i, j;
+
+ assert(pthread_equal(pthread_self(), dc.thread));
+
+ ensure_audio_format_sanity(&data, &len);
+
+ if (rgi && (replayGainState != REPLAYGAIN_OFF))
+ doReplayGain(rgi, data, len, &ob.audio_format);
+ else if (normalizationEnabled)
+ normalizeData(data, len, &ob.audio_format);
+
+ while (1) {
+ /* full buffer, loop check in case of spurious wakeups */
+ while (!ringbuf_get_write_vector(ob.index, vec)) {
+ enum dc_action rv = await_buffer_space();
+ if (rv != DC_ACTION_NONE)
+ return rv;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(vec); i++) {
+ for (j = 0; j < vec[i].iov_len; j++) {
+ size_t c_len;
+ idx = vec[i].iov_base - ob.index->buf + j;
+ c = &(ob.chunks[idx]);
+
+ if (!c->len) { /* populate empty chunk */
+ c->seq = ob.seq_decoder;
+ c->time = decode_time;
+ c->bit_rate = bit_rate;
+ c_len = len > CHUNK_SIZE ? CHUNK_SIZE
+ : len;
+ c->len = (mpd_uint16)c_len;
+ memcpy(c->data, data, c_len);
+ } else { /* partially filled chunk */
+ size_t max = CHUNK_SIZE - c->len;
+ assert(c->seq == ob.seq_decoder);
+ c_len = len > max ? max : len;
+ assert(c_len <= CHUNK_SIZE);
+ memcpy(c->data + c->len, data, c_len);
+ c->len += c_len;
+ assert(c->len <= CHUNK_SIZE);
+ }
+
+ /*
+ * feed ob.thread ASAP, otherwise ob.thread
+ * will just play silence
+ */
+ if (c->len == CHUNK_SIZE)
+ ringbuf_write_advance(ob.index, 1);
+
+ assert(len >= c_len);
+ len -= c_len;
+ if (!len) {
+ start_playback();
+ return dc.action;
+ }
+ data += c_len;
+ }
+ }
+ }
+ assert(__FILE__ && __LINE__ && "We should never get here" && 0);
+ return DC_ACTION_NONE;
+}
+
+
diff --git a/src/outputBuffer_sequence.h b/src/outputBuffer_sequence.h
new file mode 100644
index 000000000..25181425f
--- /dev/null
+++ b/src/outputBuffer_sequence.h
@@ -0,0 +1,30 @@
+/* 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
+ */
+
+#ifndef OUTPUT_BUFFER_SEQUENCE_H
+#define OUTPUT_BUFFER_SEQUENCE_H
+
+/*
+ * prevent the decoder thread from being more than one song
+ * ahead of the output buffer
+ */
+void ob_seq_enter(void);
+void ob_seq_leave(void);
+void ob_seq_wait(void);
+
+#endif /* OUTPUT_BUFFER_SEQUENCE_H */
diff --git a/src/outputBuffer_xfade.h b/src/outputBuffer_xfade.h
new file mode 100644
index 000000000..0f3a4d5a7
--- /dev/null
+++ b/src/outputBuffer_xfade.h
@@ -0,0 +1,105 @@
+#ifndef OUTPUT_BUFFER_XFADE_H
+#define OUTPUT_BUFFER_XFADE_H
+
+#include "os_compat.h"
+#include "audio.h"
+#include "pcm_utils.h"
+
+static struct ob_chunk *get_chunk(struct iovec vec[2], size_t i);
+static size_t calculate_xfade_chunks(struct iovec vec[2])
+{
+ size_t chunks;
+ struct ob_chunk *c;
+ size_t nr;
+ AudioFormat *af = &ob.audio_format;
+
+ assert(pthread_equal(ob.thread, pthread_self()));
+
+ if (!ob.total_time ||
+ (ob.elapsed_time + ob.xfade_time) < ob.total_time ||
+ !isCurrentAudioFormat(af))
+ return ob.nr_bpp; /* too early, don't enable xfade yet */
+
+ assert(af->bits > 0);
+ assert(af->channels > 0);
+ assert(af->sampleRate > 0);
+
+ chunks = af->sampleRate * af->bits * af->channels / 8.0 / CHUNK_SIZE;
+ chunks = chunks * (ob.xfade_time + 0.5);
+
+ assert(ob.index->size >= ob.nr_bpp);
+ if (chunks > (ob.index->size - ob.nr_bpp))
+ chunks = ob.index->size - ob.nr_bpp;
+ DEBUG("calculated xfade chunks: %d\n", chunks);
+ nr = vec[0].iov_len + vec[1].iov_len;
+
+ if (chunks > nr)
+ return chunks; /* not enough work with */
+
+ c = get_chunk(vec, chunks);
+ assert(c);
+ if (c->seq == ob.seq_player) {
+ do {
+ if (!(c = get_chunk(vec, ++chunks)))
+ return chunks; /* not enough to work with */
+ } while (c->seq == ob.seq_player);
+ } else {
+ do {
+ c = get_chunk(vec, --chunks);
+ assert(c);
+ } while (c->seq == ob.seq_decoder);
+ assert((c = get_chunk(vec, chunks)));
+ assert(c->seq != ob.seq_decoder);
+ ++chunks;
+ assert((c = get_chunk(vec, chunks)));
+ assert(c->seq == ob.seq_decoder);
+ }
+ DEBUG("adjusted xfade chunks: %d\n", chunks);
+
+ ob.xfade_cur = chunks;
+ ob.xfade_max = chunks;
+ assert(ob.xfade_state == XFADE_DISABLED);
+ ob.xfade_state = XFADE_ENABLED;
+ return chunks;
+}
+
+static size_t xfade_chunks_needed(struct iovec vec[2])
+{
+ assert(pthread_equal(ob.thread, pthread_self()));
+
+ if (ob.xfade_state == XFADE_DISABLED)
+ return calculate_xfade_chunks(vec);
+ assert(ob.xfade_state == XFADE_ENABLED);
+ return ob.xfade_max;
+}
+
+static void xfade_mix(struct ob_chunk *a, struct ob_chunk *b)
+{
+ assert(pthread_equal(ob.thread, pthread_self()));
+ assert(ob.xfade_state == XFADE_ENABLED);
+ assert(ob.xfade_cur <= ob.xfade_max);
+ assert(b);
+ assert(a != b);
+ assert(a->len <= CHUNK_SIZE);
+ assert(b->len <= CHUNK_SIZE);
+ if (b->seq == a->seq) {
+ /* deal with small rounding errors */
+ DEBUG("seq_match: %d == %d\n", a->seq, b->seq);
+ return;
+ }
+
+ /* as xfade_cur increases, b is scaled more and a is scaled less */
+ pcm_mix(a->data, b->data, a->len, b->len,
+ &ob.audio_format, ((float)ob.xfade_cur) / ob.xfade_max);
+
+ /* next time we fade more until we have nothing to fade */
+ if (ob.xfade_cur)
+ --ob.xfade_cur;
+ if (b->len > a->len) {
+ DEBUG("reassign len: %d => %d\n", a->len, b->len);
+ a->len = b->len;
+ }
+ b->len = 0; /* invalidate the chunk we already mixed in */
+}
+
+#endif /* OUTPUT_BUFFER_XFADE_H */
diff --git a/src/player.c b/src/player.c
deleted file mode 100644
index 4d7762a0f..000000000
--- a/src/player.c
+++ /dev/null
@@ -1,345 +0,0 @@
-/* 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.h"
-#include "path.h"
-#include "decode.h"
-#include "command.h"
-#include "interface.h"
-#include "playlist.h"
-#include "ls.h"
-#include "listen.h"
-#include "log.h"
-#include "utils.h"
-#include "directory.h"
-#include "volume.h"
-#include "playerData.h"
-#include "permission.h"
-#include "ack.h"
-#include "os_compat.h"
-#include "main_notify.h"
-
-static void playerCloseAudio(void);
-
-void wakeup_player_nb(void)
-{
- notify_signal(&pc.notify);
-}
-
-static void wakeup_player(void)
-{
- notify_signal(&pc.notify);
- wait_main_task();
-}
-
-void player_sleep(void)
-{
- notify_wait(&pc.notify);
-}
-
-static void * player_task(mpd_unused void *arg)
-{
- notify_enter(&pc.notify);
-
- while (1) {
- if (pc.play) {
- decode();
- continue; /* decode() calls wakeup_main_task */
- } else if (pc.stop) {
- pc.stop = 0;
- } else if (pc.seek) {
- pc.seek = 0;
- } else if (pc.pause) {
- pc.pause = 0;
- } else if (pc.closeAudio) {
- closeAudioDevice();
- pc.closeAudio = 0;
- } else if (pc.lockQueue) {
- pc.queueLockState = PLAYER_QUEUE_LOCKED;
- pc.lockQueue = 0;
- } else if (pc.unlockQueue) {
- pc.queueLockState = PLAYER_QUEUE_UNLOCKED;
- pc.unlockQueue = 0;
- } else {
- player_sleep();
- continue;
- }
- /* we did something, tell the main task about it */
- wakeup_main_task();
- }
- 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)
- return -1;
-
- playerCloseAudio();
-
- return 0;
-}
-
-static void set_current_song(Song *song)
-{
- pc.fileTime = song->tag ? song->tag->time : 0;
- pc.current_song = song;
-}
-
-int playerPlay(int fd, Song * song)
-{
- if (playerStop(fd) < 0)
- return -1;
-
- set_current_song(song);
-
- pc.play = 1;
- /* FIXME: _nb() variant is probably wrong here, and everywhere... */
- do { wakeup_player_nb(); } while (pc.play);
-
- return 0;
-}
-
-int playerStop(int fd)
-{
- if (pc.state != PLAYER_STATE_STOP) {
- pc.stop = 1;
- do { wakeup_player(); } while (pc.stop);
- }
-
- pc.queueState = PLAYER_QUEUE_BLANK;
- playerQueueUnlock();
-
- return 0;
-}
-
-void playerKill(void) /* deprecated */
-{
- playerPause(STDERR_FILENO);
-}
-
-int playerPause(int fd)
-{
- if (pc.state != PLAYER_STATE_STOP) {
- pc.pause = 1;
- do { wakeup_player(); } while (pc.pause);
- }
-
- return 0;
-}
-
-int playerSetPause(int fd, int pause_flag)
-{
- switch (pc.state) {
- case PLAYER_STATE_PLAY:
- if (pause_flag)
- playerPause(fd);
- break;
- case PLAYER_STATE_PAUSE:
- if (!pause_flag)
- playerPause(fd);
- break;
- }
-
- return 0;
-}
-
-int getPlayerElapsedTime(void)
-{
- return (int)(pc.elapsedTime + 0.5);
-}
-
-unsigned long getPlayerBitRate(void)
-{
- return pc.bitRate;
-}
-
-int getPlayerTotalTime(void)
-{
- return (int)(pc.totalTime + 0.5);
-}
-
-int getPlayerState(void)
-{
- return pc.state;
-}
-
-void clearPlayerError(void)
-{
- pc.error = 0;
-}
-
-int getPlayerError(void)
-{
- return pc.error;
-}
-
-char *getPlayerErrorStr(void)
-{
- /* static OK here, only one user in main task */
- static char error[MPD_PATH_MAX + 64]; /* still too much */
- static const size_t errorlen = sizeof(error);
- char path_max_tmp[MPD_PATH_MAX];
- *error = '\0'; /* likely */
-
- switch (pc.error) {
- case PLAYER_ERROR_FILENOTFOUND:
- snprintf(error, errorlen,
- "file \"%s\" does not exist or is inaccessible",
- get_song_url(path_max_tmp, pc.errored_song));
- break;
- case PLAYER_ERROR_FILE:
- snprintf(error, errorlen, "problems decoding \"%s\"",
- get_song_url(path_max_tmp, pc.errored_song));
- break;
- case PLAYER_ERROR_AUDIO:
- strcpy(error, "problems opening audio device");
- break;
- case PLAYER_ERROR_SYSTEM:
- strcpy(error, "system error occured");
- break;
- case PLAYER_ERROR_UNKTYPE:
- snprintf(error, errorlen, "file type of \"%s\" is unknown",
- get_song_url(path_max_tmp, pc.errored_song));
- }
- return *error ? error : NULL;
-}
-
-static void playerCloseAudio(void)
-{
- if (playerStop(STDERR_FILENO) < 0)
- return;
- pc.closeAudio = 1;
- do { wakeup_player(); } while (pc.closeAudio);
-}
-
-int queueSong(Song * song)
-{
- if (pc.queueState == PLAYER_QUEUE_BLANK) {
- set_current_song(song);
- pc.queueState = PLAYER_QUEUE_FULL;
- return 0;
- }
-
- return -1;
-}
-
-int getPlayerQueueState(void)
-{
- return pc.queueState;
-}
-
-void setQueueState(int queueState)
-{
- pc.queueState = queueState;
- wakeup_player_nb();
-}
-
-void playerQueueLock(void)
-{
- if (pc.queueLockState == PLAYER_QUEUE_UNLOCKED) {
- pc.lockQueue = 1;
- do { wakeup_player(); } while (pc.lockQueue);
- }
-}
-
-void playerQueueUnlock(void)
-{
- if (pc.queueLockState == PLAYER_QUEUE_LOCKED) {
- pc.unlockQueue = 1;
- do { wakeup_player(); } while (pc.unlockQueue);
- }
-}
-
-int playerSeek(int fd, Song * song, float seek_time)
-{
- assert(song != NULL);
-
- if (pc.state == PLAYER_STATE_STOP) {
- commandError(fd, ACK_ERROR_PLAYER_SYNC,
- "player not currently playing");
- return -1;
- }
-
- if (pc.current_song != song)
- set_current_song(song);
-
- if (pc.error == PLAYER_ERROR_NOERROR) {
- pc.seekWhere = seek_time;
- pc.seek = 1;
- /* FIXME: _nb() is probably wrong here, too */
- do { wakeup_player_nb(); } while (pc.seek);
- }
-
- return 0;
-}
-
-float getPlayerCrossFade(void)
-{
- return pc.crossFade;
-}
-
-void setPlayerCrossFade(float crossFadeInSeconds)
-{
- if (crossFadeInSeconds < 0)
- crossFadeInSeconds = 0;
- pc.crossFade = crossFadeInSeconds;
-}
-
-void setPlayerSoftwareVolume(int volume)
-{
- volume = (volume > 1000) ? 1000 : (volume < 0 ? 0 : volume);
- pc.softwareVolume = volume;
-}
-
-double getPlayerTotalPlayTime(void)
-{
- return pc.totalPlayTime;
-}
-
-unsigned int getPlayerSampleRate(void)
-{
- return pc.sampleRate;
-}
-
-int getPlayerBits(void)
-{
- return pc.bits;
-}
-
-int getPlayerChannels(void)
-{
- return pc.channels;
-}
-
-/* this actually creates a dupe of the current metadata */
-Song *playerCurrentDecodeSong(void)
-{
- return NULL;
-}
diff --git a/src/player.h b/src/player.h
deleted file mode 100644
index 623ae1fb0..000000000
--- a/src/player.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/* 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
- */
-
-#ifndef PLAYER_H
-#define PLAYER_H
-
-#include "decode.h"
-#include "mpd_types.h"
-#include "song.h"
-#include "os_compat.h"
-
-#define PLAYER_STATE_STOP 0
-#define PLAYER_STATE_PAUSE 1
-#define PLAYER_STATE_PLAY 2
-
-#define PLAYER_ERROR_NOERROR 0
-#define PLAYER_ERROR_FILE 1
-#define PLAYER_ERROR_AUDIO 2
-#define PLAYER_ERROR_SYSTEM 3
-#define PLAYER_ERROR_UNKTYPE 4
-#define PLAYER_ERROR_FILENOTFOUND 5
-
-/* 0->1->2->3->5 regular playback
- * ->4->0 don't play queued song
- */
-#define PLAYER_QUEUE_BLANK 0
-#define PLAYER_QUEUE_FULL 1
-#define PLAYER_QUEUE_DECODE 2
-#define PLAYER_QUEUE_PLAY 3
-#define PLAYER_QUEUE_STOP 4
-#define PLAYER_QUEUE_EMPTY 5
-
-#define PLAYER_QUEUE_UNLOCKED 0
-#define PLAYER_QUEUE_LOCKED 1
-
-typedef struct _PlayerControl {
- Notify notify;
- volatile mpd_sint8 stop;
- volatile mpd_sint8 play;
- volatile mpd_sint8 pause;
- volatile mpd_sint8 state;
- volatile mpd_sint8 closeAudio;
- volatile mpd_sint8 error;
- volatile mpd_uint16 bitRate;
- volatile mpd_sint8 bits;
- volatile mpd_sint8 channels;
- volatile mpd_uint32 sampleRate;
- volatile float totalTime;
- volatile float elapsedTime;
- volatile float fileTime;
- Song *current_song;
- Song *errored_song;
- volatile mpd_sint8 queueState;
- volatile mpd_sint8 queueLockState;
- volatile mpd_sint8 lockQueue;
- volatile mpd_sint8 unlockQueue;
- volatile mpd_sint8 seek;
- volatile double seekWhere;
- volatile float crossFade;
- volatile mpd_uint16 softwareVolume;
- volatile double totalPlayTime;
-} PlayerControl;
-
-void wakeup_player_nb(void);
-
-void player_sleep(void);
-
-int playerPlay(int fd, Song * song);
-
-int playerSetPause(int fd, int pause_flag);
-
-int playerPause(int fd);
-
-int playerStop(int fd);
-
-void playerKill(void);
-
-int getPlayerTotalTime(void);
-
-int getPlayerElapsedTime(void);
-
-unsigned long getPlayerBitRate(void);
-
-int getPlayerState(void);
-
-void clearPlayerError(void);
-
-char *getPlayerErrorStr(void);
-
-int getPlayerError(void);
-
-int playerWait(int fd);
-
-int queueSong(Song * song);
-
-int getPlayerQueueState(void);
-
-void setQueueState(int queueState);
-
-void playerQueueLock(void);
-
-void playerQueueUnlock(void);
-
-int playerSeek(int fd, Song * song, float seek_time);
-
-void setPlayerCrossFade(float crossFadeInSeconds);
-
-float getPlayerCrossFade(void);
-
-void setPlayerSoftwareVolume(int volume);
-
-double getPlayerTotalPlayTime(void);
-
-unsigned int getPlayerSampleRate(void);
-
-int getPlayerBits(void);
-
-int getPlayerChannels(void);
-
-Song *playerCurrentDecodeSong(void);
-
-void playerInit(void);
-
-#endif
diff --git a/src/playerData.c b/src/playerData.c
index 3934d0c6f..113a71f35 100644
--- a/src/playerData.c
+++ b/src/playerData.c
@@ -24,19 +24,14 @@
#define DEFAULT_BUFFER_SIZE 2048
#define DEFAULT_BUFFER_BEFORE_PLAY 10
-unsigned int buffered_before_play;
-PlayerControl pc;
-DecoderControl dc;
-OutputBuffer ob;
-
void initPlayerData(void)
{
float perc = DEFAULT_BUFFER_BEFORE_PLAY;
char *test;
- int crossfade = 0;
size_t bufferSize = DEFAULT_BUFFER_SIZE;
unsigned int buffered_chunks;
ConfigParam *param;
+ unsigned int buffered_before_play;
param = getConfigParam(CONF_AUDIO_BUFFER_SIZE);
@@ -73,18 +68,6 @@ void initPlayerData(void)
}
ob_init(buffered_chunks);
-
- notify_init(&pc.notify);
- pc.error = PLAYER_ERROR_NOERROR;
- pc.state = PLAYER_STATE_STOP;
- pc.queueState = PLAYER_QUEUE_BLANK;
- pc.queueLockState = PLAYER_QUEUE_UNLOCKED;
- pc.crossFade = crossfade;
- pc.softwareVolume = 1000;
-
- notify_init(&dc.notify);
- dc.state = DECODE_STATE_STOP;
- dc.error = DECODE_ERROR_NOERROR;
}
diff --git a/src/playerData.h b/src/playerData.h
index cf7f72cf0..ea8ea13e8 100644
--- a/src/playerData.h
+++ b/src/playerData.h
@@ -20,16 +20,10 @@
#define PLAYER_DATA_H
#include "audio.h"
-#include "player.h"
#include "decode.h"
#include "mpd_types.h"
#include "outputBuffer.h"
-extern unsigned int buffered_before_play;
-extern PlayerControl pc;
-extern DecoderControl dc;
-extern OutputBuffer ob;
-
void initPlayerData(void);
#endif
diff --git a/src/player_error.c b/src/player_error.c
new file mode 100644
index 000000000..4c7f7b9de
--- /dev/null
+++ b/src/player_error.c
@@ -0,0 +1,53 @@
+#include "player_error.h"
+#include "os_compat.h"
+#include "log.h"
+#include "path.h"
+
+enum player_error player_errno;
+Song *player_errsong;
+
+void player_clearerror(void)
+{
+ player_errno = PLAYER_ERROR_NONE;
+ player_errsong = NULL;
+}
+
+void player_seterror(enum player_error err, Song *song)
+{
+ if (player_errno)
+ ERROR("Clobbering existing error: %s\n", player_strerror());
+ player_errno = err;
+ player_errsong = song;
+}
+
+const char *player_strerror(void)
+{
+ /* static OK here, only one user in main task */
+ static char error[MPD_PATH_MAX + 64]; /* still too much */
+ char path_max_tmp[MPD_PATH_MAX];
+ *error = '\0'; /* likely */
+
+ switch (player_errno) {
+ case PLAYER_ERROR_NONE: break;
+ case PLAYER_ERROR_FILE:
+ snprintf(error, sizeof(error), "problems decoding \"%s\"",
+ get_song_url(path_max_tmp, player_errsong));
+ break;
+ case PLAYER_ERROR_AUDIO:
+ strcpy(error, "problems opening audio device");
+ break;
+ case PLAYER_ERROR_SYSTEM:
+ strcpy(error, "system error occured");
+ break;
+ case PLAYER_ERROR_UNKTYPE:
+ snprintf(error, sizeof(error), "file type of \"%s\" is unknown",
+ get_song_url(path_max_tmp, player_errsong));
+ case PLAYER_ERROR_FILENOTFOUND:
+ snprintf(error, sizeof(error),
+ "file \"%s\" does not exist or is inaccessible",
+ get_song_url(path_max_tmp, player_errsong));
+ break;
+ }
+ return *error ? error : NULL;
+}
+
diff --git a/src/player_error.h b/src/player_error.h
new file mode 100644
index 000000000..c90c98420
--- /dev/null
+++ b/src/player_error.h
@@ -0,0 +1,40 @@
+/* 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
+ */
+
+#ifndef PLAYER_ERROR_H
+#define PLAYER_ERROR_H
+
+#include "song.h"
+
+enum player_error {
+ PLAYER_ERROR_NONE = 0,
+ PLAYER_ERROR_FILE,
+ PLAYER_ERROR_AUDIO,
+ PLAYER_ERROR_SYSTEM,
+ PLAYER_ERROR_UNKTYPE,
+ PLAYER_ERROR_FILENOTFOUND
+};
+
+extern enum player_error player_errno;
+extern Song *player_errsong;
+
+void player_clearerror(void);
+void player_seterror(enum player_error err, Song *song);
+const char *player_strerror(void);
+
+#endif /* PLAYER_ERROR_H */
diff --git a/src/playlist.c b/src/playlist.c
index ce029cc65..abe151488 100644
--- a/src/playlist.c
+++ b/src/playlist.c
@@ -17,7 +17,7 @@
*/
#include "playlist.h"
-#include "player.h"
+#include "player_error.h"
#include "command.h"
#include "ls.h"
#include "tag.h"
@@ -31,9 +31,28 @@
#include "ack.h"
#include "myfprintf.h"
#include "os_compat.h"
-
-#define PLAYLIST_STATE_STOP 0
-#define PLAYLIST_STATE_PLAY 1
+#include "main_notify.h"
+
+enum _playlist_state {
+ PLAYLIST_STATE_STOP = 0,
+ PLAYLIST_STATE_PLAY = 1
+};
+static enum _playlist_state playlist_state;
+
+struct _playlist {
+ Song **songs;
+ /* holds version a song was modified on */
+ mpd_uint32 *songMod;
+ int *order;
+ int *positionToId;
+ int *idToPosition;
+ int length;
+ int current;
+ int queued; /* to be decoded */
+ int repeat;
+ int random;
+ mpd_uint32 version;
+};
#define PLAYLIST_PREV_UNLESS_ELAPSED 10
@@ -57,13 +76,19 @@
#define DEFAULT_PLAYLIST_MAX_LENGTH (1024*16)
#define DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS 0
-static Playlist playlist;
-static int playlist_state = PLAYLIST_STATE_STOP;
+static struct _playlist playlist;
int playlist_max_length = DEFAULT_PLAYLIST_MAX_LENGTH;
static int playlist_stopOnError;
static int playlist_errorCount;
-static int playlist_queueError;
-static int playlist_noGoToNext;
+
+/*
+ * queue_lock is to prevent ourselves from modifying playlist.queued
+ * while the decoder is decoding the song. The main_thread in mpd is
+ * the only modifier of playlist.queued. However, we may modify
+ * playlist.queued "in-place" without locking if it points to the same
+ * song (during move or shuffle).
+ */
+static pthread_mutex_t queue_lock = PTHREAD_MUTEX_INITIALIZER;
int playlist_saveAbsolutePaths = DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS;
@@ -238,8 +263,8 @@ void savePlaylistState(FILE *fp)
fprintf(fp, "%s", PLAYLIST_STATE_FILE_STATE);
switch (playlist_state) {
case PLAYLIST_STATE_PLAY:
- switch (getPlayerState()) {
- case PLAYER_STATE_PAUSE:
+ switch (ob_get_state()) {
+ case OB_STATE_PAUSE:
fprintf(fp, "%s\n", PLAYLIST_STATE_FILE_STATE_PAUSE);
break;
default:
@@ -247,8 +272,8 @@ void savePlaylistState(FILE *fp)
}
fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_CURRENT,
playlist.order[playlist.current]);
- fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_TIME,
- getPlayerElapsedTime());
+ fprintf(fp, "%s%lu\n", PLAYLIST_STATE_FILE_TIME,
+ ob_get_elapsed_time());
break;
default:
fprintf(fp, "%s\n", PLAYLIST_STATE_FILE_STATE_STOP);
@@ -257,7 +282,7 @@ void savePlaylistState(FILE *fp)
fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_RANDOM, playlist.random);
fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_REPEAT, playlist.repeat);
fprintf(fp, "%s%i\n", PLAYLIST_STATE_FILE_CROSSFADE,
- (int)(getPlayerCrossFade()));
+ (int)(ob_get_xfade()));
fprintf(fp, "%s\n", PLAYLIST_STATE_FILE_PLAYLIST_BEGIN);
fflush(fp);
showPlaylist(fileno(fp));
@@ -265,7 +290,8 @@ void savePlaylistState(FILE *fp)
}
static void loadPlaylistFromStateFile(FILE *fp, char *buffer,
- int state, int current, int seek_time)
+ enum ob_state state,
+ int current, int seek_time)
{
char *temp;
int song;
@@ -281,14 +307,9 @@ static void loadPlaylistFromStateFile(FILE *fp, char *buffer,
state_file_fatal();
if (!addToPlaylist(STDERR_FILENO, temp, NULL)
&& current == song) {
- if (state != PLAYER_STATE_STOP) {
- playPlaylist(STDERR_FILENO,
- playlist.length - 1, 0);
- }
- if (state == PLAYER_STATE_PAUSE) {
- playerPause(STDERR_FILENO);
- }
- if (state != PLAYER_STATE_STOP) {
+ if (state == OB_STATE_PAUSE)
+ ob_trigger_action(OB_ACTION_PAUSE_SET);
+ if (state != OB_STATE_STOP) {
seekSongInPlaylist(STDERR_FILENO,
playlist.length - 1,
seek_time);
@@ -303,7 +324,7 @@ void readPlaylistState(FILE *fp)
{
int current = -1;
int seek_time = 0;
- int state = PLAYER_STATE_STOP;
+ enum ob_state state = OB_STATE_STOP;
char buffer[PLAYLIST_BUFFER_SIZE];
while (myFgets(buffer, PLAYLIST_BUFFER_SIZE, fp)) {
@@ -311,13 +332,13 @@ void readPlaylistState(FILE *fp)
strlen(PLAYLIST_STATE_FILE_STATE)) == 0) {
if (strcmp(&(buffer[strlen(PLAYLIST_STATE_FILE_STATE)]),
PLAYLIST_STATE_FILE_STATE_PLAY) == 0) {
- state = PLAYER_STATE_PLAY;
+ state = OB_STATE_PLAY;
} else
if (strcmp
(&(buffer[strlen(PLAYLIST_STATE_FILE_STATE)]),
PLAYLIST_STATE_FILE_STATE_PAUSE)
== 0) {
- state = PLAYER_STATE_PAUSE;
+ state = OB_STATE_PAUSE;
}
} else if (strncmp(buffer, PLAYLIST_STATE_FILE_TIME,
strlen(PLAYLIST_STATE_FILE_TIME)) == 0) {
@@ -337,9 +358,7 @@ void readPlaylistState(FILE *fp)
if (strncmp
(buffer, PLAYLIST_STATE_FILE_CROSSFADE,
strlen(PLAYLIST_STATE_FILE_CROSSFADE)) == 0) {
- setPlayerCrossFade(atoi
- (&
- (buffer
+ ob_set_xfade(atoi(&(buffer
[strlen
(PLAYLIST_STATE_FILE_CROSSFADE)])));
} else
@@ -368,7 +387,7 @@ void readPlaylistState(FILE *fp)
(buffer, PLAYLIST_STATE_FILE_PLAYLIST_BEGIN,
strlen(PLAYLIST_STATE_FILE_PLAYLIST_BEGIN)
) == 0) {
- if (state == PLAYER_STATE_STOP)
+ if (state == OB_STATE_STOP)
current = -1;
loadPlaylistFromStateFile(fp, buffer, state,
current, seek_time);
@@ -483,95 +502,95 @@ static void swapSongs(int song1, int song2)
playlist.positionToId[song2] = iTemp;
}
-static void queueNextSongInPlaylist(void)
+static Song *song_at(int order_num)
{
- char path_max_tmp[MPD_PATH_MAX];
+ if (order_num >= 0 && order_num < playlist.length) {
+ assert(playlist.songs[playlist.order[order_num]]);
+ return playlist.songs[playlist.order[order_num]];
+ }
+ return NULL;
+}
+static int next_order_num(void)
+{
if (playlist.current < playlist.length - 1) {
- playlist.queued = playlist.current + 1;
- DEBUG("playlist: queue song %i:\"%s\"\n",
- playlist.queued,
- get_song_url(path_max_tmp,
- playlist.
- songs[playlist.order[playlist.queued]]));
- if (queueSong(playlist.songs[playlist.order[playlist.queued]]) <
- 0) {
- playlist.queued = -1;
- playlist_queueError = 1;
- }
+ return playlist.current + 1;
} else if (playlist.length && playlist.repeat) {
- if (playlist.length > 1 && playlist.random) {
+ if (playlist.length > 1 && playlist.random)
randomizeOrder(0, playlist.length - 1);
- }
- playlist.queued = 0;
- DEBUG("playlist: queue song %i:\"%s\"\n",
- playlist.queued,
- get_song_url(path_max_tmp,
- playlist.
- songs[playlist.order[playlist.queued]]));
- if (queueSong(playlist.songs[playlist.order[playlist.queued]]) <
- 0) {
- playlist.queued = -1;
- playlist_queueError = 1;
- }
+ return 0;
}
+ return -1;
}
-static void syncPlaylistWithQueue(int queue)
+static void queueNextSongInPlaylist(void)
{
- if (queue && getPlayerQueueState() == PLAYER_QUEUE_BLANK) {
- queueNextSongInPlaylist();
- } else if (getPlayerQueueState() == PLAYER_QUEUE_DECODE) {
- if (playlist.queued != -1)
- setQueueState(PLAYER_QUEUE_PLAY);
- else
- setQueueState(PLAYER_QUEUE_STOP);
- } else if (getPlayerQueueState() == PLAYER_QUEUE_EMPTY) {
- setQueueState(PLAYER_QUEUE_BLANK);
- if (playlist.queued >= 0) {
- DEBUG("playlist: now playing queued song\n");
- playlist.current = playlist.queued;
- }
- playlist.queued = -1;
- if (queue)
- queueNextSongInPlaylist();
+ assert(playlist_state == PLAYLIST_STATE_PLAY);
+ /* DEBUG("%s:%d\n", __func__, __LINE__); */
+ if (pthread_mutex_trylock(&queue_lock) == EBUSY)
+ return; /* still decoding */
+ DEBUG("%s:%d\n", __func__, __LINE__);
+ playlist.queued = next_order_num();
+ pthread_mutex_unlock(&queue_lock);
+ if (playlist.queued < 0) {
+ playlist_state = PLAYLIST_STATE_STOP;
+ } else if (dc.state == DC_STATE_STOP) {
+ /* DEBUG("%s:%d (%d)\n", __func__, __LINE__, playlist.queued);*/
+ dc_trigger_action(DC_ACTION_START, 0);
}
}
-static void lockPlaylistInteraction(void)
+static void syncPlaylistWithQueue(void)
{
- if (getPlayerQueueState() == PLAYER_QUEUE_PLAY ||
- getPlayerQueueState() == PLAYER_QUEUE_FULL) {
- playerQueueLock();
- syncPlaylistWithQueue(0);
+ assert(playlist_state == PLAYLIST_STATE_PLAY);
+
+ if (!ob_synced())
+ return;
+
+ if (playlist.queued >= 0 &&
+ playlist.current != playlist.queued) {
+ DEBUG("playlist: now playing queued song\n");
+ DEBUG("%s:%d queued: %d\n",__func__,__LINE__,playlist.queued);
+ playlist.current = playlist.queued;
}
+ queueNextSongInPlaylist();
}
-static void unlockPlaylistInteraction(void)
+void playlist_queue_next(void)
{
- playerQueueUnlock();
+ assert(pthread_equal(pthread_self(), dc.thread));
+ pthread_mutex_unlock(&queue_lock);
+ wakeup_main_task();
}
-static void clearPlayerQueue(void)
+Song *playlist_queued_song(void)
{
- playlist.queued = -1;
- switch (getPlayerQueueState()) {
- case PLAYER_QUEUE_FULL:
- 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;
- }
+ assert(pthread_equal(pthread_self(), dc.thread));
+ pthread_mutex_lock(&queue_lock);
+ return song_at(playlist.queued);
}
-static void clearPlayerQueueLocked(void)
+static void queue_song_locked(int order_num)
+{
+ pthread_mutex_lock(&queue_lock);
+ playlist.queued = order_num;
+ pthread_mutex_unlock(&queue_lock);
+}
+
+/*
+ * stops decoder iff we're decoding a song we haven't played yet
+ * Returns the currently queued song, -1 if we cleared the queue
+ * This will not affect the currently playing song
+ */
+static int clear_queue(void)
{
- lockPlaylistInteraction();
- clearPlayerQueue();
- unlockPlaylistInteraction();
+ if (playlist.queued >= 0 && playlist.current != playlist.queued) {
+ dc_trigger_action(DC_ACTION_STOP, 0);
+ assert(dc.state == DC_STATE_STOP);
+ ob_drop_audio(OB_DROP_DECODED);
+ queue_song_locked(-1);
+ }
+ return playlist.queued;
}
int addToPlaylist(int fd, char *url, int *added_id)
@@ -633,7 +652,7 @@ int addSongToPlaylist(int fd, Song * song, int *added_id)
if (playlist_state == PLAYLIST_STATE_PLAY) {
if (playlist.queued >= 0
&& playlist.current == playlist.length - 1)
- clearPlayerQueueLocked();
+ clear_queue();
}
id = getNextId();
@@ -649,8 +668,7 @@ int addSongToPlaylist(int fd, Song * song, int *added_id)
if (playlist.random) {
int swap;
int start;
- /*if(playlist_state==PLAYLIST_STATE_STOP) start = 0;
- else */ if (playlist.queued >= 0)
+ if (playlist.queued >= 0)
start = playlist.queued + 1;
else
start = playlist.current + 1;
@@ -689,11 +707,13 @@ int swapSongsInPlaylist(int fd, int song1, int song2)
if (playlist.queued >= 0) {
queuedSong = playlist.order[playlist.queued];
}
+ assert(playlist.current >= 0 &&
+ playlist.current < playlist.length);
currentSong = playlist.order[playlist.current];
if (queuedSong == song1 || queuedSong == song2
|| currentSong == song1 || currentSong == song2)
- clearPlayerQueueLocked();
+ clear_queue();
}
swapSongs(song1, song2);
@@ -742,6 +762,8 @@ int deleteFromPlaylist(int fd, int song)
{
int i;
int songOrder;
+ int stopped_current = 0;
+ int prev_queued = playlist.queued;
if (song < 0 || song >= playlist.length) {
commandError(fd, ACK_ERROR_NO_EXIST,
@@ -750,10 +772,12 @@ int deleteFromPlaylist(int fd, int song)
}
if (playlist_state == PLAYLIST_STATE_PLAY) {
- if (playlist.queued >= 0
- && (playlist.order[playlist.queued] == song
- || playlist.order[playlist.current] == song))
- clearPlayerQueueLocked();
+ if (prev_queued >= 0
+ && (playlist.order[prev_queued] == song
+ || playlist.order[playlist.current] == song)) {
+ /* DEBUG(__FILE__": %d (clearing)\n", __LINE__); */
+ clear_queue();
+ }
}
if (playlist.songs[song]->type == SONG_TYPE_URL) {
@@ -786,12 +810,15 @@ int deleteFromPlaylist(int fd, int song)
incrPlaylistVersion();
+ /* DEBUG("current: %d, songOrder: %d\n", playlist.current, songOrder); */
+ /* DEBUG("playlist_state: %d\n", playlist_state); */
if (playlist_state != PLAYLIST_STATE_STOP
&& playlist.current == songOrder) {
- /*if(playlist.current>=playlist.length) return playerStop(fd);
- else return playPlaylistOrderNumber(fd,playlist.current); */
- playerWait(STDERR_FILENO);
- playlist_noGoToNext = 1;
+ dc_trigger_action(DC_ACTION_STOP, 0);
+ /* drop all audio, but don't actually close the device */
+ ob_drop_audio(OB_DROP_DECODED);
+ ob_drop_audio(OB_DROP_PLAYING);
+ stopped_current = 1;
}
if (playlist.current > songOrder) {
@@ -799,9 +826,15 @@ int deleteFromPlaylist(int fd, int song)
} else if (playlist.current >= playlist.length) {
incrPlaylistCurrent();
}
-
- if (playlist.queued > songOrder) {
- playlist.queued--;
+ if (stopped_current) {
+ /* DEBUG(__FILE__": %d\n", __LINE__); */
+ if (playlist.current >= 0)
+ playPlaylistOrderNumber(STDERR_FILENO,playlist.current);
+ else
+ playlist_state = PLAYLIST_STATE_STOP;
+ } else {
+ /* DEBUG(__FILE__": %d\n", __LINE__); */
+ queueNextSongInPlaylist();
}
return 0;
@@ -831,11 +864,18 @@ void deleteASongFromPlaylist(Song * song)
int stopPlaylist(int fd)
{
DEBUG("playlist: stop\n");
- if (playerWait(fd) < 0)
- return -1;
- playlist.queued = -1;
+
+ DEBUG("%s:%d\n", __func__, __LINE__);
+ dc_trigger_action(DC_ACTION_STOP, 0);
+ DEBUG("%s:%d\n", __func__, __LINE__);
+ assert(dc.state == DC_STATE_STOP);
+ DEBUG("%s:%d\n", __func__, __LINE__);
+ ob_trigger_action(OB_ACTION_STOP);
+ assert(ob_get_state() == OB_STATE_STOP);
+
+ DEBUG("%s:%d\n", __func__, __LINE__);
+ queue_song_locked(-1);
playlist_state = PLAYLIST_STATE_STOP;
- playlist_noGoToNext = 0;
if (playlist.random)
randomizeOrder(0, playlist.length - 1);
return 0;
@@ -843,27 +883,21 @@ int stopPlaylist(int fd)
static int playPlaylistOrderNumber(int fd, int orderNum)
{
- char path_max_tmp[MPD_PATH_MAX];
-
- if (playerStop(fd) < 0)
- return -1;
+ char path[MPD_PATH_MAX];
playlist_state = PLAYLIST_STATE_PLAY;
- playlist_noGoToNext = 0;
- playlist.queued = -1;
- playlist_queueError = 0;
+ assert(orderNum >= 0);
DEBUG("playlist: play %i:\"%s\"\n", orderNum,
- get_song_url(path_max_tmp,
- playlist.songs[playlist.order[orderNum]]));
-
- if (playerPlay(fd, (playlist.songs[playlist.order[orderNum]])) < 0) {
- stopPlaylist(fd);
- return -1;
- }
-
+ get_song_url(path, song_at(orderNum)));
+ dc_trigger_action(DC_ACTION_STOP, 0);
+ ob_drop_audio(OB_DROP_DECODED);
+ queue_song_locked(orderNum);
+ ob_drop_audio(OB_DROP_PLAYING);
+ ob_trigger_action(OB_ACTION_PAUSE_UNSET);
+ dc_trigger_action(DC_ACTION_START, 0);
+ ob_wait_sync(); /* HACK: REMOVE */
playlist.current = orderNum;
-
return 0;
}
@@ -871,14 +905,17 @@ int playPlaylist(int fd, int song, int stopOnError)
{
int i = song;
- clearPlayerError();
+ DEBUG("%s %d song(%d)\n", __func__, __LINE__, song);
+
+ player_clearerror();
if (song == -1) {
if (playlist.length == 0)
return 0;
if (playlist_state == PLAYLIST_STATE_PLAY) {
- return playerSetPause(fd, 0);
+ ob_trigger_action(OB_ACTION_PAUSE_UNSET);
+ return 0;
}
if (playlist.current >= 0 && playlist.current < playlist.length) {
i = playlist.current;
@@ -892,14 +929,12 @@ int playPlaylist(int fd, int song, int stopOnError)
}
if (playlist.random) {
- if (song == -1 && playlist_state == PLAYLIST_STATE_PLAY) {
+ if (song == -1) {
randomizeOrder(0, playlist.length - 1);
} else {
if (song >= 0)
for (i = 0; song != playlist.order[i]; i++) ;
- if (playlist_state == PLAYLIST_STATE_STOP) {
- playlist.current = 0;
- }
+ playlist.current = 0;
swapOrder(i, playlist.current);
i = playlist.current;
}
@@ -908,6 +943,7 @@ int playPlaylist(int fd, int song, int stopOnError)
playlist_stopOnError = stopOnError;
playlist_errorCount = 0;
+ ERROR(__FILE__ ": %d current:%d\n", __LINE__, playlist.current);
return playPlaylistOrderNumber(fd, i);
}
@@ -924,7 +960,7 @@ int playPlaylistById(int fd, int id, int stopOnError)
static void syncCurrentPlayerDecodeMetadata(void)
{
- Song *songPlayer = playerCurrentDecodeSong();
+ Song *songPlayer = song_at(playlist.current);
Song *song;
int songNum;
char path_max_tmp[MPD_PATH_MAX];
@@ -953,72 +989,31 @@ void syncPlayerAndPlaylist(void)
{
if (playlist_state != PLAYLIST_STATE_PLAY)
return;
-
- if (getPlayerState() == PLAYER_STATE_STOP)
- playPlaylistIfPlayerStopped();
- else
- syncPlaylistWithQueue(!playlist_queueError);
-
- syncCurrentPlayerDecodeMetadata();
-}
-
-static int currentSongInPlaylist(int fd)
-{
- if (playlist_state != PLAYLIST_STATE_PLAY)
- return 0;
-
- playlist_stopOnError = 0;
-
- syncPlaylistWithQueue(0);
-
- if (playlist.current >= 0 && playlist.current < playlist.length) {
- return playPlaylistOrderNumber(fd, playlist.current);
- } else
- return stopPlaylist(fd);
+ syncPlaylistWithQueue();
+ /* DEBUG("queued:%d current:%d\n", playlist.queued, playlist.current); */
+ if (playlist_state == PLAYLIST_STATE_PLAY &&
+ playlist.queued >= 0 &&
+ playlist.queued != playlist.current &&
+ ob_synced() &&
+ dc.state == DC_STATE_STOP &&
+ ob_get_state() != OB_STATE_PAUSE) {
+ dc_trigger_action(DC_ACTION_START, 0);
+ }
}
int nextSongInPlaylist(int fd)
{
+ int next;
if (playlist_state != PLAYLIST_STATE_PLAY)
return 0;
-
- syncPlaylistWithQueue(0);
-
playlist_stopOnError = 0;
-
- if (playlist.current < playlist.length - 1) {
- return playPlaylistOrderNumber(fd, playlist.current + 1);
- } else if (playlist.length && playlist.repeat) {
- if (playlist.random)
- randomizeOrder(0, playlist.length - 1);
- return playPlaylistOrderNumber(fd, 0);
- } else {
+ next = next_order_num();
+ if (next < 0) {
+ /* we were already at last song w/o repeat: */
incrPlaylistCurrent();
return stopPlaylist(fd);
}
-}
-
-void playPlaylistIfPlayerStopped(void)
-{
- if (getPlayerState() == PLAYER_STATE_STOP) {
- int error = getPlayerError();
-
- if (error == PLAYER_ERROR_NOERROR)
- playlist_errorCount = 0;
- else
- playlist_errorCount++;
-
- if (playlist_state == PLAYLIST_STATE_PLAY
- && ((playlist_stopOnError && error != PLAYER_ERROR_NOERROR)
- || error == PLAYER_ERROR_AUDIO
- || error == PLAYER_ERROR_SYSTEM
- || playlist_errorCount >= playlist.length)) {
- stopPlaylist(STDERR_FILENO);
- } else if (playlist_noGoToNext)
- currentSongInPlaylist(STDERR_FILENO);
- else
- nextSongInPlaylist(STDERR_FILENO);
- }
+ return playPlaylistOrderNumber(fd, next);
}
int getPlaylistRepeatStatus(void)
@@ -1040,7 +1035,7 @@ int setPlaylistRepeatStatus(int fd, int status)
if (playlist_state == PLAYLIST_STATE_PLAY) {
if (playlist.repeat && !status && playlist.queued == 0)
- clearPlayerQueueLocked();
+ clear_queue();
}
playlist.repeat = status;
@@ -1090,7 +1085,7 @@ int moveSongInPlaylist(int fd, int from, int to)
queuedSong = playlist.order[playlist.queued];
if (queuedSong == from || queuedSong == to
|| currentSong == from || currentSong == to)
- clearPlayerQueueLocked();
+ clear_queue();
}
tmpSong = playlist.songs[from];
@@ -1120,28 +1115,16 @@ int moveSongInPlaylist(int fd, int from, int to)
playlist.order[i] = to;
}
}
- }
- else
- {
- if (playlist.current == from)
+ } else {
+ if (playlist.current == from) {
playlist.current = to;
- else if (playlist.current > from && playlist.current <= to) {
+ } else if (playlist.current > from && playlist.current <= to) {
playlist.current--;
} else if (playlist.current >= to && playlist.current < from) {
playlist.current++;
}
-
- /* this first if statement isn't necessary since the queue
- * would have been cleared out if queued == from */
- if (playlist.queued == from)
- playlist.queued = to;
- else if (playlist.queued > from && playlist.queued <= to) {
- playlist.queued--;
- } else if (playlist.queued>= to && playlist.queued < from) {
- playlist.queued++;
- }
}
-
+ queueNextSongInPlaylist();
incrPlaylistVersion();
return 0;
@@ -1157,20 +1140,19 @@ int moveSongInPlaylistById(int fd, int id1, int to)
static void orderPlaylist(void)
{
int i;
+ int queued_is_current = (playlist.queued == playlist.current);
+ if (!queued_is_current &&
+ playlist_state == PLAYLIST_STATE_PLAY &&
+ playlist.queued >= 0)
+ clear_queue();
if (playlist.current >= 0 && playlist.current < playlist.length) {
playlist.current = playlist.order[playlist.current];
+ if (queued_is_current)
+ playlist.queued = playlist.current;
}
-
- if (playlist_state == PLAYLIST_STATE_PLAY) {
- if (playlist.queued >= 0)
- clearPlayerQueueLocked();
- }
-
- for (i = 0; i < playlist.length; i++) {
+ for (i = 0; i < playlist.length; i++)
playlist.order[i] = i;
- }
-
}
static void swapOrder(int a, int b)
@@ -1184,13 +1166,16 @@ static void randomizeOrder(int start, int end)
{
int i;
int ri;
+ int queued_is_current = (playlist.queued == playlist.current);
DEBUG("playlist: randomize from %i to %i\n", start, end);
+ DEBUG("%s:%d current: %d\n", __func__, __LINE__, playlist.current);
- if (playlist_state == PLAYLIST_STATE_PLAY) {
- if (playlist.queued >= start && playlist.queued <= end)
- clearPlayerQueueLocked();
- }
+ if (!queued_is_current &&
+ playlist_state == PLAYLIST_STATE_PLAY &&
+ playlist.queued >= start &&
+ playlist.queued <= end)
+ clear_queue();
for (i = start; i <= end; i++) {
ri = random() % (end - start + 1) + start;
@@ -1200,6 +1185,9 @@ static void randomizeOrder(int start, int end)
playlist.current = ri;
swapOrder(i, ri);
}
+ if (queued_is_current)
+ playlist.queued = playlist.current;
+ DEBUG("%s:%d current: %d\n", __func__, __LINE__, playlist.current);
}
int setPlaylistRandomStatus(int fd, int status)
@@ -1214,19 +1202,15 @@ int setPlaylistRandomStatus(int fd, int status)
playlist.random = status;
if (status != statusWas) {
- if (playlist.random) {
- /*if(playlist_state==PLAYLIST_STATE_PLAY) {
- randomizeOrder(playlist.current+1,
- playlist.length-1);
- }
- else */ randomizeOrder(0, playlist.length - 1);
- if (playlist.current >= 0 &&
- playlist.current < playlist.length) {
- swapOrder(playlist.current, 0);
- playlist.current = 0;
- }
- } else
+ if (playlist.random)
+ randomizeOrder(0, playlist.length - 1);
+ else
orderPlaylist();
+ if (playlist_state == PLAYLIST_STATE_PLAY) {
+ queueNextSongInPlaylist();
+ DEBUG("%s:%d queued: %d\n",
+ __func__,__LINE__,playlist.queued);
+ }
}
return 0;
@@ -1236,36 +1220,40 @@ int previousSongInPlaylist(int fd)
{
static time_t lastTime;
time_t diff = time(NULL) - lastTime;
+ int prev_order_num;
lastTime += diff;
if (playlist_state != PLAYLIST_STATE_PLAY)
return 0;
- syncPlaylistWithQueue(0);
+ syncPlaylistWithQueue();
- if (diff && getPlayerElapsedTime() > PLAYLIST_PREV_UNLESS_ELAPSED) {
- return playPlaylistOrderNumber(fd, playlist.current);
+ if (diff && ob_get_elapsed_time() > PLAYLIST_PREV_UNLESS_ELAPSED) {
+ prev_order_num = playlist.current;
} else {
- if (playlist.current > 0) {
- return playPlaylistOrderNumber(fd,
- playlist.current - 1);
- } else if (playlist.repeat) {
- return playPlaylistOrderNumber(fd, playlist.length - 1);
- } else {
- return playPlaylistOrderNumber(fd, playlist.current);
- }
+ if (playlist.current > 0)
+ prev_order_num = playlist.current - 1;
+ else if (playlist.repeat)
+ prev_order_num = playlist.length - 1;
+ else
+ prev_order_num = playlist.current;
}
+ return playPlaylistOrderNumber(fd, prev_order_num);
}
int shufflePlaylist(int fd)
{
int i;
int ri;
+ int playing_queued = 0;
if (playlist.length > 1) {
if (playlist_state == PLAYLIST_STATE_PLAY) {
- clearPlayerQueueLocked();
+ if (playlist.queued == playlist.current)
+ playing_queued = 1;
+ else
+ clear_queue();
/* put current playing song first */
swapSongs(0, playlist.order[playlist.current]);
if (playlist.random) {
@@ -1274,6 +1262,8 @@ int shufflePlaylist(int fd)
playlist.current = j;
} else
playlist.current = 0;
+ if (playing_queued)
+ playlist.queued = playlist.current;
i = 1;
} else {
i = 0;
@@ -1284,8 +1274,9 @@ int shufflePlaylist(int fd)
ri = random() % (playlist.length - 1) + 1;
swapSongs(i, ri);
}
-
incrPlaylistVersion();
+ if (playlist_state == PLAYLIST_STATE_PLAY)
+ queueNextSongInPlaylist();
}
return 0;
@@ -1356,6 +1347,7 @@ int savePlaylist(int fd, char *utf8file)
int getPlaylistCurrentSong(void)
{
+ DEBUG("%s:%d current: %d\n", __func__, __LINE__, playlist.current);
if (playlist.current >= 0 && playlist.current < playlist.length) {
return playlist.order[playlist.current];
}
@@ -1373,9 +1365,14 @@ int getPlaylistLength(void)
return playlist.length;
}
+/*
+ * This command will always return 0 regardless of whether or
+ * not the seek succeeded (it's always been the case, apparently)
+ */
int seekSongInPlaylist(int fd, int song, float seek_time)
{
int i = song;
+ char path[MPD_PATH_MAX];
if (song < 0 || song >= playlist.length) {
commandError(fd, ACK_ERROR_NO_EXIST,
@@ -1386,22 +1383,42 @@ int seekSongInPlaylist(int fd, int song, float seek_time)
if (playlist.random)
for (i = 0; song != playlist.order[i]; i++) ;
- clearPlayerError();
+ player_clearerror();
playlist_stopOnError = 1;
playlist_errorCount = 0;
- if (playlist_state == PLAYLIST_STATE_PLAY) {
- if (playlist.queued >= 0)
- clearPlayerQueueLocked();
- } else if (playPlaylistOrderNumber(fd, i) < 0)
- return -1;
-
- if (playlist.current != i) {
- if (playPlaylistOrderNumber(fd, i) < 0)
- return -1;
+ if (playlist_state == PLAYLIST_STATE_PLAY &&
+ (playlist.current == i && playlist.queued == i)) {
+ dc_trigger_action(DC_ACTION_SEEK, seek_time);
+ if (dc.seek_where != DC_SEEK_MISMATCH)
+ return 0;
+ /*
+ * if near end of decoding can cause seek to fail (since we're
+ * already on another song) (leading to DC_SEEK_MISMATCH),
+ * so fall through to restarting the decoder below.
+ */
}
- return playerSeek(fd, playlist.songs[playlist.order[i]], seek_time);
+ DEBUG("playlist: seek %i:\"%s\"\n", i, get_song_url(path, song_at(i)));
+ playlist_state = PLAYLIST_STATE_PLAY;
+ DEBUG("%s:%d\n", __func__, __LINE__);
+ dc_trigger_action(DC_ACTION_STOP, 0);
+ DEBUG("%s:%d\n", __func__, __LINE__);
+ ob_drop_audio(OB_DROP_DECODED);
+ DEBUG("%s:%d\n", __func__, __LINE__);
+ queue_song_locked(i);
+ DEBUG("%s:%d\n", __func__, __LINE__);
+ ob_drop_audio(OB_DROP_PLAYING);
+ DEBUG("%s:%d\n", __func__, __LINE__);
+ DEBUG("seek_time: %f\n", seek_time);
+ dc_trigger_action(DC_ACTION_SEEK, seek_time);
+ DEBUG("%s:%d\n", __func__, __LINE__);
+ if (dc.seek_where >= 0) {
+ playlist.current = i;
+ DEBUG("%s:%d\n", __func__, __LINE__);
+ ob_wait_sync();
+ }
+ return 0;
}
int seekSongInPlaylistById(int fd, int id, float seek_time)
@@ -1538,4 +1555,7 @@ int valid_playlist_name(int err_fd, const char *utf8path)
return 1;
}
-
+int playlist_playing(void)
+{
+ return (playlist_state == PLAYLIST_STATE_PLAY);
+}
diff --git a/src/playlist.h b/src/playlist.h
index db5243ba9..2ba28f9e4 100644
--- a/src/playlist.h
+++ b/src/playlist.h
@@ -24,21 +24,6 @@
#define PLAYLIST_FILE_SUFFIX "m3u"
#define PLAYLIST_COMMENT '#'
-typedef struct _Playlist {
- Song **songs;
- /* holds version a song was modified on */
- mpd_uint32 *songMod;
- int *order;
- int *positionToId;
- int *idToPosition;
- int length;
- int current;
- int queued;
- int repeat;
- int random;
- mpd_uint32 version;
-} Playlist;
-
extern int playlist_saveAbsolutePaths;
extern int playlist_max_length;
@@ -71,6 +56,12 @@ int playlistInfo(int fd, int song);
int playlistId(int fd, int song);
+Song *playlist_queued_song(void);
+
+void playlist_queue_next(void);
+
+int playlist_playing(void);
+
int stopPlaylist(int fd);
int playPlaylist(int fd, int song, int stopOnError);
@@ -119,8 +110,6 @@ int getPlaylistLength(void);
unsigned long getPlaylistVersion(void);
-void playPlaylistIfPlayerStopped(void);
-
int seekSongInPlaylist(int fd, int song, float seek_time);
int seekSongInPlaylistById(int fd, int id, float seek_time);
diff --git a/src/ringbuf.h b/src/ringbuf.h
index d4baffdbd..43ba83a7c 100644
--- a/src/ringbuf.h
+++ b/src/ringbuf.h
@@ -175,7 +175,8 @@ void ringbuf_reset(struct ringbuf * rb);
/**
* Reset the write pointer to the read pointer, making an empty buffer.
*
- * This should only be called by the writer
+ * This should only be called by the writer and only very carefully
+ * (it is subject to race conditions with vectors or peek)
*
* @param rb a pointer to the ringbuffer structure.
*/
@@ -184,7 +185,8 @@ void ringbuf_writer_reset(struct ringbuf * rb);
/**
* Reset the read pointer to the write pointer, making an empty buffer.
*
- * This should only be called by the reader
+ * This should only be called by the writer and only very carefully
+ * (it is subject to race conditions with vectors or peek)
*
* @param rb a pointer to the ringbuffer structure.
*/
diff --git a/src/sig_handlers.c b/src/sig_handlers.c
index 88159f24f..0ad35b779 100644
--- a/src/sig_handlers.c
+++ b/src/sig_handlers.c
@@ -23,7 +23,6 @@
#include "command.h"
#include "signal_check.h"
#include "log.h"
-#include "player.h"
#include "decode.h"
int handlePendingSignals(void)
diff --git a/src/stats.c b/src/stats.c
index 254595472..39ba39e9a 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -20,7 +20,7 @@
#include "directory.h"
#include "myfprintf.h"
-#include "player.h"
+#include "outputBuffer.h"
#include "tagTracker.h"
#include "os_compat.h"
@@ -39,7 +39,7 @@ int printStats(int fd)
fdprintf(fd, "songs: %i\n", stats.numberOfSongs);
fdprintf(fd, "uptime: %li\n", time(NULL) - stats.daemonStart);
fdprintf(fd, "playtime: %li\n",
- (long)(getPlayerTotalPlayTime() + 0.5));
+ (long)(ob_get_total_time() + 0.5));
fdprintf(fd, "db_playtime: %li\n", stats.dbPlayTime);
fdprintf(fd, "db_update: %li\n", getDbModTime());
return 0;
diff --git a/src/volume.c b/src/volume.c
index 5b720651a..0da7d0360 100644
--- a/src/volume.c
+++ b/src/volume.c
@@ -20,7 +20,6 @@
#include "command.h"
#include "conf.h"
#include "log.h"
-#include "player.h"
#include "gcc.h"
#include "utils.h"
#include "ack.h"
@@ -492,7 +491,7 @@ static int changeSoftwareVolume(int fd, int change, int rel)
new =
1000.0 * (exp(new / 25.0) - 1) / (54.5981500331F - 1) + 0.5;
- setPlayerSoftwareVolume(new);
+ ob_set_sw_volume(new);
return 0;
}