summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am10
-rw-r--r--src/main.c189
-rw-r--r--src/player.c239
-rw-r--r--src/player.h51
4 files changed, 315 insertions, 174 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 6bffa2d..606430b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,10 @@
bin_PROGRAMS = pianobar
-pianobar_SOURCES = main.c terminal.c terminal.h settings.c settings.h
-pianobar_CPPFLAGS = ${LIBCURL_CFLAGS} ${LIBAO_CFLAGS} ${LIBXML_CFLAGS} -I../libpiano/src
-pianobar_LDADD = ${LIBCURL_LIBS} ${LIBAO_LIBS} ${LIBFAAD_LIBS} ${LIBREADLINE_LIBS} ${LIBXML_LIBS} -lpthread ../libpiano/src/libpiano.la
+pianobar_SOURCES = main.c terminal.c terminal.h settings.c settings.h \
+ player.c player.h
+pianobar_CPPFLAGS = ${LIBCURL_CFLAGS} ${LIBAO_CFLAGS} ${LIBXML_CFLAGS} \
+ -I../libpiano/src
+pianobar_LDADD = ${LIBCURL_LIBS} ${LIBAO_LIBS} ${LIBFAAD_LIBS} \
+ ${LIBREADLINE_LIBS} ${LIBXML_LIBS} -lpthread \
+ ../libpiano/src/libpiano.la
dist_man1_MANS = pianobar.1
diff --git a/src/main.c b/src/main.c
index 26ff6e8..81181aa 100644
--- a/src/main.c
+++ b/src/main.c
@@ -27,7 +27,6 @@ THE SOFTWARE.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
-#include <ao/ao.h>
#include <neaacdec.h>
#include <pthread.h>
#include <unistd.h>
@@ -37,175 +36,7 @@ THE SOFTWARE.
#include "terminal.h"
#include "settings.h"
#include "config.h"
-
-struct aacPlayer {
- /* buffer; should be large enough */
- char buffer[CURL_MAX_WRITE_SIZE*2];
- size_t bufferFilled;
- /* got mdat atom */
- char dataMode;
- char foundEsdsAtom;
- char audioInitialized;
- /* aac */
- NeAACDecHandle aacHandle;
- unsigned long samplerate;
- unsigned char channels;
- /* audio out */
- ao_device *audioOutDevice;
- char *url;
- char finishedPlayback;
- char doQuit;
- char doPause;
- CURL *audioFd;
-};
-
-void dumpBuffer (char *buf, size_t len) {
- int i;
- for (i = 0; i < len; i++) {
- printf ("%02x ", buf[i] & 0xff);
- }
- printf ("\n");
-}
-
-/* FIXME: this is a huge block of _bad_ and buggy code */
-size_t playCurlCb (void *ptr, size_t size, size_t nmemb, void *stream) {
- char *data = ptr;
- struct aacPlayer *player = stream;
-
- if (player->doQuit) {
- return 0;
- }
-
- /* FIXME: not the best solution to poll every second, but the easiest one
- * I know... (pthread's conditions could be another solution) */
- if (player->doPause == 1) {
- curl_easy_pause (player->audioFd, CURLPAUSE_ALL);
- while (player->doPause == 1) {
- sleep (1);
- }
- curl_easy_pause (player->audioFd, CURLPAUSE_CONT);
- }
-
- /* fill buffer */
- if (player->bufferFilled + size*nmemb > sizeof (player->buffer)) {
- printf ("Buffer overflow!\n");
- return 0;
- }
- memcpy (player->buffer+player->bufferFilled, data, size*nmemb);
- player->bufferFilled += size*nmemb;
-
- if (player->dataMode == 1) {
- char *aacDecoded;
- NeAACDecFrameInfo frameInfo;
-
- /* 500 is just a guessed size; prevent buffer underruns and error
- * messages by faad2 */
- while (player->bufferFilled > 500) {
- /* FIXME: well, i think we need this block length table */
- aacDecoded = NeAACDecDecode(player->aacHandle, &frameInfo,
- (unsigned char *) player->buffer, player->bufferFilled);
- if (frameInfo.error != 0) {
- printf ("error: %s\n\n",
- NeAACDecGetErrorMessage (frameInfo.error));
- break;
- }
- ao_play (player->audioOutDevice, aacDecoded,
- frameInfo.samples*frameInfo.channels);
- /* move remaining bytes to buffer beginning */
- memmove (player->buffer, player->buffer + frameInfo.bytesconsumed,
- player->bufferFilled - frameInfo.bytesconsumed);
- player->bufferFilled -= frameInfo.bytesconsumed;
- }
- } else {
- char *searchBegin = player->buffer;
-
- if (!player->foundEsdsAtom) {
- while (searchBegin < player->buffer + nmemb) {
- if (memcmp (searchBegin, "esds", 4) == 0) {
- player->foundEsdsAtom = 1;
- break;
- }
- searchBegin++;
- }
- }
- if (player->foundEsdsAtom && !player->audioInitialized) {
- /* FIXME: is this the correct way? */
- while (searchBegin < player->buffer + nmemb) {
- if (memcmp (searchBegin, "\x05\x80\x80\x80", 4) == 0) {
- ao_sample_format format;
- int audioOutDriver;
-
- /* +1+4 needs to be replaced by <something>! */
- char err = NeAACDecInit2 (player->aacHandle,
- (unsigned char *) searchBegin+1+4, 5,
- &player->samplerate, &player->channels);
- if (err != 0) {
- printf ("whoops... %i\n", err);
- return 1;
- }
- audioOutDriver = ao_default_driver_id();
- format.bits = 16;
- format.channels = player->channels;
- format.rate = player->samplerate;
- format.byte_format = AO_FMT_LITTLE;
- player->audioOutDevice = ao_open_live (audioOutDriver,
- &format, NULL);
- player->audioInitialized = 1;
- break;
- }
- searchBegin++;
- }
- }
- if (player->audioInitialized) {
- while (searchBegin < player->buffer + nmemb) {
- if (memcmp (searchBegin, "mdat", 4) == 0) {
- player->dataMode = 1;
- memmove (player->buffer, searchBegin + strlen ("mdat"),
- nmemb - (searchBegin - player->buffer));
- player->bufferFilled = nmemb - (searchBegin-player->buffer);
- break;
- }
- searchBegin++;
- }
- }
- if (!player->dataMode) {
- /* copy last four bytes to buffer beginning and set filled
- * size to four */
- memcpy (player->buffer, player->buffer+player->bufferFilled - 4, 4);
- player->bufferFilled = 4;
- }
- }
-
- return size*nmemb;
-}
-
-/* player thread; for every song a new thread is started */
-void *threadPlayUrl (void *data) {
- struct aacPlayer *player = data;
- NeAACDecConfigurationPtr conf;
-
- player->audioFd = curl_easy_init ();
- player->aacHandle = NeAACDecOpen();
-
- conf = NeAACDecGetCurrentConfiguration(player->aacHandle);
- conf->outputFormat = FAAD_FMT_16BIT;
- conf->downMatrix = 1;
- NeAACDecSetConfiguration(player->aacHandle, conf);
-
- curl_easy_setopt (player->audioFd, CURLOPT_URL, player->url);
- curl_easy_setopt (player->audioFd, CURLOPT_WRITEFUNCTION, playCurlCb);
- curl_easy_setopt (player->audioFd, CURLOPT_WRITEDATA, player);
- curl_easy_setopt (player->audioFd, CURLOPT_USERAGENT, PACKAGE_STRING);
- curl_easy_perform (player->audioFd);
-
- NeAACDecClose(player->aacHandle);
- ao_close(player->audioOutDevice);
- curl_easy_cleanup (player->audioFd);
-
- player->finishedPlayback = 1;
-
- return NULL;
-}
+#include "player.h"
PianoStation_t *selectStation (PianoHandle_t *ph) {
PianoStation_t *curStation;
@@ -534,7 +365,23 @@ int main (int argc, char **argv) {
curStation->name);
}
break;
- }
+ } /* end case */
+ } /* end poll */
+
+ /* show time */
+ if (player.finishedPlayback == 0) {
+ /* FIXME: is this calculation correct? */
+ /* one frame length: T = 1/f */
+ float songLength = 1.0 / (float) player.samplerate *
+ (float) player.channels * 1000.0 *
+ (float) player.sampleSizeN;
+ float songRemaining = songLength - 1.0 / (float) player.samplerate *
+ (float) player.channels * 1000.0 *
+ (float) player.sampleSizeCurr;
+ printf ("-%02i:%02i/%02i:%02i\r", (int) songRemaining/60,
+ (int) songRemaining%60, (int) songLength/60,
+ (int) songLength%60);
+ fflush (stdout);
}
}
diff --git a/src/player.c b/src/player.c
new file mode 100644
index 0000000..3a7aa25
--- /dev/null
+++ b/src/player.c
@@ -0,0 +1,239 @@
+/*
+Copyright (c) 2008 Lars-Dominik Braun
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include <unistd.h>
+#include <string.h>
+
+#include "player.h"
+#include "config.h"
+
+/* LE to BE and reverse
+ * @param unsigned int
+ * @return byteswapped unsigned int
+ */
+unsigned int changeByteorderUI32 (char buf[4]) {
+ unsigned int ret = 0;
+
+ ret = buf[0] << 24 & 0xffffffff;
+ ret |= buf[1] << 16 & 0xffffff;
+ ret |= buf[2] << 8 & 0xffff;
+ ret |= buf[3] << 0 & 0xff;
+ return ret;
+}
+
+/* our heart: plays streamed music
+ * @param streamed data
+ * @param block size
+ * @param received blocks
+ * @param extra data (player data)
+ * @return received bytes or less on error
+ */
+size_t playCurlCb (void *ptr, size_t size, size_t nmemb, void *stream) {
+ char *data = ptr;
+ struct aacPlayer *player = stream;
+
+ if (player->doQuit) {
+ return 0;
+ }
+
+ /* FIXME: not the best solution to poll every second, but the easiest
+ * one I know... (pthread's conditions could be another solution) */
+ if (player->doPause == 1) {
+ curl_easy_pause (player->audioFd, CURLPAUSE_ALL);
+ while (player->doPause == 1) {
+ sleep (1);
+ }
+ curl_easy_pause (player->audioFd, CURLPAUSE_CONT);
+ }
+
+ /* fill buffer */
+ if (player->bufferFilled + size*nmemb > sizeof (player->buffer)) {
+ printf ("Buffer overflow!\n");
+ return 0;
+ }
+ memcpy (player->buffer+player->bufferFilled, data, size*nmemb);
+ player->bufferFilled += size*nmemb;
+ player->bufferRead = 0;
+
+ if (player->mode == RECV_DATA) {
+ char *aacDecoded;
+ NeAACDecFrameInfo frameInfo;
+
+ while ((player->bufferFilled - player->bufferRead) >
+ player->sampleSize[player->sampleSizeCurr]) {
+ /* decode frame */
+ aacDecoded = NeAACDecDecode(player->aacHandle, &frameInfo,
+ (unsigned char *) player->buffer + player->bufferRead,
+ player->sampleSize[player->sampleSizeCurr]);
+ if (frameInfo.error != 0) {
+ printf ("error: %s\n\n",
+ NeAACDecGetErrorMessage (frameInfo.error));
+ break;
+ }
+ ao_play (player->audioOutDevice, aacDecoded,
+ frameInfo.samples*frameInfo.channels);
+ player->bufferRead += frameInfo.bytesconsumed;
+ player->sampleSizeCurr++;
+ }
+ } else {
+ if (player->mode == FIND_ESDS) {
+ while (player->bufferRead+4 < player->bufferFilled) {
+ if (memcmp (player->buffer + player->bufferRead, "esds",
+ 4) == 0) {
+ player->mode = FOUND_ESDS;
+ player->bufferRead += 4;
+ break;
+ }
+ player->bufferRead++;
+ }
+ }
+ if (player->mode == FOUND_ESDS) {
+ /* FIXME: is this the correct way? */
+ /* we're gonna read 10 bytes */
+ while (player->bufferRead+1+4+5 < player->bufferFilled) {
+ if (memcmp (player->buffer + player->bufferRead,
+ "\x05\x80\x80\x80", 4) == 0) {
+ ao_sample_format format;
+ int audioOutDriver;
+
+ /* +1+4 needs to be replaced by <something>! */
+ player->bufferRead += 1+4;
+ char err = NeAACDecInit2 (player->aacHandle,
+ (unsigned char *) player->buffer +
+ player->bufferRead, 5, &player->samplerate,
+ &player->channels);
+ player->bufferRead += 5;
+ if (err != 0) {
+ printf ("Error while initializing audio decoder (%i)\n",
+ err);
+ return 1;
+ }
+ audioOutDriver = ao_default_driver_id();
+ format.bits = 16;
+ format.channels = player->channels;
+ format.rate = player->samplerate;
+ format.byte_format = AO_FMT_LITTLE;
+ player->audioOutDevice = ao_open_live (audioOutDriver,
+ &format, NULL);
+ player->mode = AUDIO_INITIALIZED;
+ break;
+ }
+ player->bufferRead++;
+ }
+ }
+ if (player->mode == AUDIO_INITIALIZED) {
+ while (player->bufferRead+4+8 < player->bufferFilled) {
+ if (memcmp (player->buffer + player->bufferRead, "stsz",
+ 4) == 0) {
+ player->mode = FOUND_STSZ;
+ player->bufferRead += 4;
+ /* skip version and unknown */
+ player->bufferRead += 8;
+ break;
+ }
+ player->bufferRead++;
+ }
+ }
+ /* get frame sizes */
+ if (player->mode == FOUND_STSZ) {
+ while (player->bufferRead+4 < player->bufferFilled) {
+ /* how many frames do we have? */
+ if (player->sampleSizeN == 0) {
+ /* mp4 uses big endian, convert */
+ player->sampleSizeN = changeByteorderUI32 (player->buffer +
+ player->bufferRead);
+ player->sampleSize = calloc (player->sampleSizeN,
+ sizeof (player->sampleSizeN));
+ player->bufferRead += 4;
+ player->sampleSizeCurr = 0;
+ break;
+ } else {
+ player->sampleSize[player->sampleSizeCurr] =
+ changeByteorderUI32 (player->buffer +
+ player->bufferRead);
+ player->sampleSizeCurr++;
+ player->bufferRead += 4;
+ }
+ /* all sizes read, nearly ready for data mode */
+ if (player->sampleSizeCurr >= player->sampleSizeN) {
+ player->mode = SAMPLESIZE_INITIALIZED;
+ break;
+ }
+ }
+ }
+ /* search for data atom and let the show begin... */
+ if (player->mode == SAMPLESIZE_INITIALIZED) {
+ while (player->bufferRead+4 < player->bufferFilled) {
+ if (memcmp (player->buffer + player->bufferRead, "mdat",
+ 4) == 0) {
+ player->mode = RECV_DATA;
+ player->sampleSizeCurr = 0;
+ player->bufferRead += 4;
+ break;
+ }
+ player->bufferRead++;
+ }
+ }
+ }
+
+ /* move remaining bytes to buffer beginning */
+ memmove (player->buffer, player->buffer + player->bufferRead,
+ (player->bufferFilled - player->bufferRead));
+ player->bufferFilled = (player->bufferFilled - player->bufferRead);
+
+ return size*nmemb;
+}
+
+
+/* player thread; for every song a new thread is started
+ * @param aacPlayer structure
+ * @return NULL NULL NULL ...
+ */
+void *threadPlayUrl (void *data) {
+ struct aacPlayer *player = data;
+ NeAACDecConfigurationPtr conf;
+
+ player->audioFd = curl_easy_init ();
+ player->aacHandle = NeAACDecOpen();
+
+ conf = NeAACDecGetCurrentConfiguration(player->aacHandle);
+ conf->outputFormat = FAAD_FMT_16BIT;
+ conf->downMatrix = 1;
+ NeAACDecSetConfiguration(player->aacHandle, conf);
+
+ curl_easy_setopt (player->audioFd, CURLOPT_URL, player->url);
+ curl_easy_setopt (player->audioFd, CURLOPT_WRITEFUNCTION, playCurlCb);
+ curl_easy_setopt (player->audioFd, CURLOPT_WRITEDATA, player);
+ curl_easy_setopt (player->audioFd, CURLOPT_USERAGENT, PACKAGE_STRING);
+ curl_easy_perform (player->audioFd);
+
+ NeAACDecClose(player->aacHandle);
+ ao_close(player->audioOutDevice);
+ curl_easy_cleanup (player->audioFd);
+ if (player->sampleSize != NULL) {
+ free (player->sampleSize);
+ }
+
+ player->finishedPlayback = 1;
+
+ return NULL;
+}
diff --git a/src/player.h b/src/player.h
new file mode 100644
index 0000000..2e36c11
--- /dev/null
+++ b/src/player.h
@@ -0,0 +1,51 @@
+/*
+Copyright (c) 2008 Lars-Dominik Braun
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include <curl/curl.h>
+#include <neaacdec.h>
+#include <ao/ao.h>
+
+struct aacPlayer {
+ /* buffer; should be large enough */
+ char buffer[CURL_MAX_WRITE_SIZE*2];
+ size_t bufferFilled;
+ size_t bufferRead;
+ enum {FIND_ESDS, FOUND_ESDS, AUDIO_INITIALIZED, FOUND_STSZ,
+ SAMPLESIZE_INITIALIZED, RECV_DATA} mode;
+ /* stsz atom: sample sizes */
+ unsigned int *sampleSize;
+ size_t sampleSizeN;
+ size_t sampleSizeCurr;
+ /* aac */
+ NeAACDecHandle aacHandle;
+ unsigned long samplerate;
+ unsigned char channels;
+ /* audio out */
+ ao_device *audioOutDevice;
+ char *url;
+ char finishedPlayback;
+ char doQuit;
+ char doPause;
+ CURL *audioFd;
+};
+
+void *threadPlayUrl (void *data);