aboutsummaryrefslogtreecommitdiff
path: root/src/decode.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/decode.c')
-rw-r--r--src/decode.c456
1 files changed, 456 insertions, 0 deletions
diff --git a/src/decode.c b/src/decode.c
new file mode 100644
index 00000000..8f8b0da6
--- /dev/null
+++ b/src/decode.c
@@ -0,0 +1,456 @@
+/* the Music Player Daemon (MPD)
+ * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
+ * 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 "decode.h"
+#include "player.h"
+#include "playerData.h"
+#include "utils.h"
+#include "pcm_utils.h"
+#include "audio.h"
+#include "path.h"
+#include "log.h"
+
+#ifdef HAVE_MAD
+#include "mp3_decode.h"
+#endif
+#ifdef HAVE_OGG
+#include "ogg_decode.h"
+#endif
+#ifdef HAVE_FLAC
+#include "flac_decode.h"
+#endif
+#ifdef HAVE_AUDIOFILE
+#include "audiofile_decode.h"
+#endif
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <string.h>
+
+#define FADE_CHUNKS 1024
+
+int decode_pid = 0;
+
+void decodeSigHandler(int sig) {
+ if(sig==SIGCHLD) {
+ int status;
+ if(decode_pid==wait3(&status,WNOHANG,NULL)) {
+ if(WIFSIGNALED(status) && WTERMSIG(status)!=SIGTERM) {
+ ERROR("decode process died from a "
+ "non-TERM signal: %i\n",
+ WTERMSIG(status));
+ }
+ decode_pid = 0;
+ }
+ }
+ else if(sig==SIGTERM) {
+ int pid = decode_pid;
+ if(pid>0) kill(pid,SIGTERM);
+ exit(0);
+ }
+}
+
+void stopDecode(DecoderControl * dc) {
+ if(decode_pid>0 && (dc->start || dc->state==DECODE_STATE_DECODE)) {
+ dc->stop = 1;
+ while(decode_pid>0 && dc->stop) usleep(10);
+ }
+}
+
+void quitDecode(PlayerControl * pc, DecoderControl * dc) {
+ stopDecode(dc);
+ pc->state = PLAYER_STATE_STOP;
+ pc->play = 0;
+ pc->stop = 0;
+ pc->pause = 0;
+ kill(getppid(),SIGUSR1);
+}
+
+int calculateCrossFadeChunks(PlayerControl * pc, AudioFormat * af) {
+ int chunks;
+
+ if(pc->crossFade<=0) return 0;
+
+ chunks = (af->sampleRate*af->bits*af->channels/8.0/CHUNK_SIZE);
+ chunks = (chunks*pc->crossFade+0.5);
+
+ if(chunks>BUFFERED_CHUNKS-buffered_before_play) {
+ chunks = BUFFERED_CHUNKS-buffered_before_play;
+ }
+
+ return chunks;
+}
+
+int waitOnDecode(PlayerControl * pc, AudioFormat * af, DecoderControl * dc,
+ Buffer * cb)
+{
+ while(decode_pid>0 && dc->start) usleep(10);
+
+ if(dc->start || dc->error!=DECODE_ERROR_NOERROR) {
+ strcpy(pc->erroredFile,pc->file);
+ pc->error = PLAYER_ERROR_FILE;
+ quitDecode(pc,dc);
+ return -1;
+ }
+
+ if(initAudio(af)<0) {
+ strcpy(pc->erroredFile,pc->file);
+ pc->error = PLAYER_ERROR_AUDIO;
+ quitDecode(pc,dc);
+ return -1;
+ }
+
+ pc->elapsedTime = 0;
+ pc->bitRate = 0;
+ pc->totalTime = cb->totalTime;
+
+ return 0;
+}
+
+void decodeSeek(PlayerControl * pc, AudioFormat * af, DecoderControl * dc,
+ Buffer * cb)
+{
+ if(decode_pid>0) {
+ cb->next = -1;
+ if(dc->state!=DECODE_STATE_DECODE || dc->error ||
+ strcmp(dc->file,pc->file)!=0)
+ {
+ stopDecode(dc);
+ cb->end = 0;
+ dc->error = 0;
+ dc->start = 1;
+ dc->error = 0;
+ waitOnDecode(pc,af,dc,cb);
+ }
+ if(decode_pid>0 && dc->state==DECODE_STATE_DECODE) {
+ dc->seekWhere = pc->seekWhere > pc->totalTime-1 ?
+ pc->totalTime-1 : pc->seekWhere;
+ dc->seekWhere = 1 > dc->seekWhere ? 1 : dc->seekWhere;
+ cb->begin = 0;
+ dc->seek = 1;
+ pc->elapsedTime = dc->seekWhere;
+ pc->bitRate = 0;
+ while(decode_pid>0 && dc->seek) usleep(10);
+ }
+ }
+ pc->seek = 0;
+}
+
+#define processDecodeInput() \
+ if(pc->lockQueue) { \
+ pc->queueLockState = PLAYER_QUEUE_LOCKED; \
+ pc->lockQueue = 0; \
+ } \
+ if(pc->unlockQueue) { \
+ pc->queueLockState = PLAYER_QUEUE_UNLOCKED; \
+ pc->unlockQueue = 0; \
+ } \
+ if(pc->pause) { \
+ pause = !pause; \
+ if(pause) pc->state = PLAYER_STATE_PAUSE; \
+ else pc->state = PLAYER_STATE_PLAY; \
+ pc->pause = 0; \
+ kill(getppid(),SIGUSR1); \
+ } \
+ if(pc->seek) { \
+ pc->totalPlayTime+= pc->elapsedTime-pc->beginTime; \
+ decodeSeek(pc,af,dc,cb); \
+ pc->beginTime = pc->elapsedTime; \
+ doCrossFade = 0; \
+ nextChunk = -1; \
+ bbp = 0; \
+ } \
+ if(pc->stop) { \
+ pc->totalPlayTime+= pc->elapsedTime-pc->beginTime; \
+ quitDecode(pc,dc); \
+ return; \
+ }
+
+int decoderInit(PlayerControl * pc, Buffer * cb, AudioFormat *af,
+ DecoderControl * dc) {
+ decode_pid = fork();
+
+ if(decode_pid==0) {
+ /* CHILD */
+
+ while(1) {
+ if(dc->start) {
+ strcpy(dc->file,pc->file);
+ switch(pc->decodeType) {
+#ifdef HAVE_MAD
+ case DECODE_TYPE_MP3:
+ dc->error = mp3_decode(cb,af,dc);
+ break;
+#endif
+#ifdef HAVE_OGG
+ case DECODE_TYPE_OGG:
+ dc->error = ogg_decode(cb,af,dc);
+ break;
+#endif
+#ifdef HAVE_FLAC
+ case DECODE_TYPE_FLAC:
+ dc->error = flac_decode(cb,af,dc);
+ break;
+#endif
+#ifdef HAVE_AUDIOFILE
+ case DECODE_TYPE_AUDIOFILE:
+ dc->error = audiofile_decode(cb,af,dc);
+ break;
+#endif
+ default:
+ dc->error = DECODE_ERROR_UNKTYPE;
+ }
+ if(dc->error!=DECODE_ERROR_NOERROR) {
+ dc->start = 0;
+ dc->stop = 0;
+ dc->state = DECODE_STATE_STOP;
+ }
+ }
+ else if(dc->stop) {
+ dc->state = DECODE_STATE_STOP;
+ dc->stop = 0;
+ }
+ else if(dc->seek) dc->start = 1;
+ else usleep(1000);
+ }
+
+ exit(0);
+ /* END OF CHILD */
+ }
+ else if(decode_pid<0) {
+ strcpy(pc->erroredFile,pc->file);
+ pc->error = PLAYER_ERROR_SYSTEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* decode w/ buffering
+ * this will fork another process
+ * child process does decoding
+ * parent process does playing audio
+ */
+void decode() {
+ Buffer * cb;
+ PlayerControl * pc;
+ AudioFormat * af;
+ DecoderControl * dc;
+
+ cb = &(getPlayerData()->buffer);
+
+ cb->begin = 0;
+ cb->end = 0;
+ cb->wrap = 0;
+ pc = &(getPlayerData()->playerControl);
+ dc = &(getPlayerData()->decoderControl);
+ af = &(getPlayerData()->audioFormat);
+ dc->error = 0;
+ dc->start = 1;
+ cb->next = -1;
+
+ if(decode_pid<=0) {
+ if(decoderInit(pc,cb,af,dc)<0) return;
+ }
+
+ {
+ /* PARENT */
+ char silence[CHUNK_SIZE];
+ int pause = 0;
+ int quit = 0;
+ int bbp = buffered_before_play;
+ int doCrossFade = 0;
+ int crossFadeChunks = 0;
+ int fadePosition;
+ int nextChunk = -1;
+ int test;
+
+ memset(silence,0,CHUNK_SIZE);
+
+ if(waitOnDecode(pc,af,dc,cb)<0) return;
+
+ pc->state = PLAYER_STATE_PLAY;
+ pc->play = 0;
+ pc->beginTime = pc->elapsedTime;
+ kill(getppid(),SIGUSR1);
+
+ while(decode_pid>0 && !cb->wrap && cb->end-cb->begin<bbp &&
+ dc->state==DECODE_STATE_DECODE)
+ {
+ processDecodeInput();
+ if(quit) return;
+ usleep(100);
+ }
+
+ while(!quit) {
+ processDecodeInput();
+ if(dc->state==DECODE_STATE_STOP &&
+ pc->queueState==PLAYER_QUEUE_FULL &&
+ pc->queueLockState==PLAYER_QUEUE_UNLOCKED)
+ {
+ cb->next = cb->end;
+ dc->start = 1;
+ pc->queueState = PLAYER_QUEUE_DECODE;
+ kill(getppid(),SIGUSR1);
+ }
+ if(cb->next>=0 && doCrossFade==0 && !dc->start) {
+ nextChunk = -1;
+ if(isCurrentAudioFormat(af)) {
+ doCrossFade = 1;
+ crossFadeChunks =
+ calculateCrossFadeChunks(pc,af);
+ if(!crossFadeChunks ||
+ pc->crossFade>=cb->totalTime)
+ {
+ doCrossFade = -1;
+ }
+ }
+ else doCrossFade = -1;
+ }
+ if(pause) playAudio(silence,CHUNK_SIZE);
+ else if((cb->begin!=cb->end || cb->wrap) &&
+ cb->begin!=cb->next)
+ {
+ if(doCrossFade==1 && cb->next>=0 &&
+ ((cb->next>cb->begin &&
+ (fadePosition=cb->next-cb->begin)
+ <=crossFadeChunks) ||
+ (cb->begin>cb->next &&
+ (fadePosition=cb->next-cb->begin+
+ BUFFERED_CHUNKS)<=crossFadeChunks)))
+ {
+ if(nextChunk==-1) {
+ crossFadeChunks = fadePosition;
+ }
+ nextChunk = cb->begin+crossFadeChunks;
+ test = cb->end;
+ if(cb->wrap) test+=BUFFERED_CHUNKS;
+ if(nextChunk<test) {
+ if(nextChunk>=BUFFERED_CHUNKS)
+ {
+ nextChunk-=
+ BUFFERED_CHUNKS;
+ }
+ pcm_mix(cb->chunks+cb->begin*
+ CHUNK_SIZE,
+ cb->chunks+nextChunk*
+ CHUNK_SIZE,
+ cb->chunkSize[
+ cb->begin],
+ cb->chunkSize[
+ nextChunk],
+ af,
+ ((float)fadePosition)/
+ crossFadeChunks);
+ if(cb->chunkSize[nextChunk]>
+ cb->chunkSize[cb->begin]
+ )
+ {
+ cb->chunkSize[cb->begin]
+ = cb->chunkSize
+ [nextChunk];
+ }
+ }
+ else {
+ if(dc->state==DECODE_STATE_STOP)
+ {
+ doCrossFade = -1;
+ }
+ else {
+ usleep(10);
+ continue;
+ }
+ }
+ }
+ pc->elapsedTime = cb->times[cb->begin];
+ pc->bitRate = cb->bitRate[cb->begin];
+ pcm_volumeChange(cb->chunks+cb->begin*
+ CHUNK_SIZE,
+ cb->chunkSize[cb->begin],
+ af,
+ pc->softwareVolume);
+ playAudio(cb->chunks+cb->begin*CHUNK_SIZE,
+ cb->chunkSize[cb->begin]);
+ cb->begin++;
+ if(cb->begin>=BUFFERED_CHUNKS) {
+ cb->begin = 0;
+ cb->wrap = 0;
+ }
+ }
+ else if(cb->next==cb->begin) {
+ pc->totalPlayTime+= pc->elapsedTime-
+ pc->beginTime;
+ if(doCrossFade==1 && nextChunk>=0) {
+ nextChunk = cb->begin+crossFadeChunks;
+ test = cb->end;
+ if(cb->wrap) test+=BUFFERED_CHUNKS;
+ if(nextChunk<test) {
+ if(nextChunk>=BUFFERED_CHUNKS)
+ {
+ nextChunk-=
+ BUFFERED_CHUNKS;
+ }
+ cb->begin = nextChunk;
+ }
+ }
+ while(pc->queueState==PLAYER_QUEUE_DECODE ||
+ pc->queueLockState==PLAYER_QUEUE_LOCKED)
+ {
+ processDecodeInput();
+ if(quit) {
+ quitDecode(pc,dc);
+ return;
+ }
+ usleep(10);
+ }
+ if(pc->queueState!=PLAYER_QUEUE_PLAY) {
+ quit = 1;
+ break;
+ }
+ else {
+ cb->next = -1;
+ if(waitOnDecode(pc,af,dc,cb)<0) return;
+ nextChunk = -1;
+ doCrossFade = 0;
+ crossFadeChunks = 0;
+ pc->queueState = PLAYER_QUEUE_EMPTY;
+ kill(getppid(),SIGUSR1);
+ }
+ pc->beginTime = cb->times[cb->begin];
+ }
+ else if(decode_pid<=0 ||
+ (dc->state==DECODE_STATE_STOP && !dc->start))
+ {
+ quit = 1;
+ break;
+ }
+ else usleep(10);
+ }
+
+ pc->totalPlayTime+= pc->elapsedTime-pc->beginTime; \
+ quitDecode(pc,dc);
+
+ /* END OF PARENT */
+ }
+
+ return;
+}