From 112528c8abaf712e2db0f8aef6b7d64b52281127 Mon Sep 17 00:00:00 2001
From: Lars-Dominik Braun <PromyLOPh@gmail.com>
Date: Tue, 10 Jun 2008 09:26:12 +0200
Subject: Added missing files to repo

---
 src/Makefile.am |   6 +
 src/main.c      | 339 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 345 insertions(+)
 create mode 100644 src/Makefile.am
 create mode 100644 src/main.c

(limited to 'src')

diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..8f88440
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,6 @@
+bin_PROGRAMS = pianobar
+
+pianobar_SOURCES = main.c
+pianobar_CPPFLAGS = ${LIBCURL_CFLAGS} ${LIBAO_CFLAGS} -I../libpiano
+pianobar_LDADD = ${LIBCURL_LIBS} ${LIBAO_LIBS} ${LIBFAAD_LIBS} -lpthread ../libpiano/libpiano.la
+
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..d125122
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,339 @@
+/*
+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 <piano.h>
+#include <curl/curl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ao/ao.h>
+#include <neaacdec.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <termios.h>
+#include <poll.h>
+
+struct aacPlayer {
+	/* buffer */
+	char *buffer;
+	size_t bufferFilled;
+	char lastBytes[4];
+	/* 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;
+};
+
+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 */
+int playCurlCb (void *ptr, size_t size, size_t nmemb, void *stream) {
+	char *data = ptr;
+	struct aacPlayer *player = stream;
+
+	if (player->doQuit) {
+		return -1;
+	}
+
+	if (player->dataMode == 1) {
+		size_t bufferOffset = 0, aacBufferN;
+		char *aacDecoded, *aacBuffer;
+		NeAACDecFrameInfo frameInfo;
+
+		aacBufferN = player->bufferFilled + nmemb;
+		aacBuffer = calloc (aacBufferN, sizeof (char));
+		memcpy (aacBuffer, player->buffer, player->bufferFilled);
+		memcpy (aacBuffer + player->bufferFilled, data, nmemb * size);
+
+		free (player->buffer);
+
+		/* keep some bytes in buffer */
+		while (bufferOffset < aacBufferN-500) {
+			//printf ("play buffer at %i/%i: ", bufferOffset, aacBufferN);
+			//dumpBuffer (aacBuffer+bufferOffset, 50);
+			/* FIXME: well, i think we need this block length table */
+			aacDecoded = NeAACDecDecode(player->aacHandle, &frameInfo,
+					(unsigned char *) aacBuffer+bufferOffset,
+					aacBufferN-bufferOffset);
+			//printf ("bytesconsumed: %li\nerror: %i\nsamples: %lu\nsamplerate: %lu\n\n", frameInfo.bytesconsumed, frameInfo.error, frameInfo.samples, frameInfo.samplerate);
+			if (frameInfo.error != 0) {
+				printf ("error: %s\n\n", NeAACDecGetErrorMessage (frameInfo.error));
+				break;
+			}
+			ao_play (player->audioOutDevice, aacDecoded,
+					frameInfo.samples*frameInfo.channels);
+			bufferOffset += frameInfo.bytesconsumed;
+		}
+		/* copy remaining bytes */
+		player->buffer = calloc (aacBufferN - bufferOffset, sizeof (char));
+		memcpy (player->buffer, aacBuffer+bufferOffset,
+				aacBufferN - bufferOffset);
+		player->bufferFilled = aacBufferN - bufferOffset;
+		//printf ("player->buffer (%i) = ", aacBufferN - bufferOffset);
+		//dumpBuffer (player->buffer, 50);
+		free (aacBuffer);
+	} else {
+		player->buffer = calloc (nmemb + sizeof (player->lastBytes), size);
+		/* we are going to find a 4-byte string, but curl sends fragments and
+		 * if the identifier is cut into two pieces, we will get into big
+		 * trouble... */
+		memcpy (player->buffer, player->lastBytes, sizeof (player->lastBytes));
+		memcpy (player->buffer+sizeof (player->lastBytes), data, size*nmemb);
+
+		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;
+
+					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;
+					}
+					//printf ("samplerate: %li\nchannels: %i\n\n",
+					//		player->samplerate, player->channels);
+					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+4, nmemb - (searchBegin-player->buffer));
+					player->bufferFilled = nmemb - (searchBegin-player->buffer);
+					//printf ("copied %i bytes: ", nmemb - (searchBegin-player->buffer));
+					//dumpBuffer (player->buffer, 50);
+					break;
+				}
+				searchBegin++;
+			}
+		}
+		if (!player->dataMode) {
+			memcpy (player->lastBytes, data + (size * nmemb - sizeof (player->lastBytes)),
+					sizeof (player->lastBytes));
+			//printf ("last bytes: ");
+			//dumpBuffer (player->lastBytes, 4);
+			free (player->buffer);
+		}
+	}
+
+	return size*nmemb;
+}
+
+void *threadPlayUrl (void *data) {
+	struct aacPlayer *player = data;
+	NeAACDecConfigurationPtr conf;
+	CURL *audioFd;
+
+	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 (audioFd, CURLOPT_URL, player->url);
+	curl_easy_setopt (audioFd, CURLOPT_WRITEFUNCTION, playCurlCb);
+	curl_easy_setopt (audioFd, CURLOPT_WRITEDATA, player);
+	/* at the moment we don't need publicity */
+	curl_easy_setopt (audioFd, CURLOPT_USERAGENT, "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9pre) Gecko/2008051115 Firefox/3.0pre");
+	curl_easy_perform (audioFd);
+
+	free (player->buffer);
+	NeAACDecClose(player->aacHandle);
+	ao_close(player->audioOutDevice);
+	curl_easy_cleanup (audioFd);
+
+	player->finishedPlayback = 1;
+
+	return NULL;
+}
+
+PianoStation_t *selectStation (PianoHandle_t *ph) {
+	PianoStation_t *curStation;
+	size_t i;
+
+	printf ("which station do you want to listen to?\n");
+	i = 0;
+	curStation = ph->stations;
+	while (curStation != NULL) {
+		printf ("%2i) %s\n", i, curStation->name);
+		curStation = curStation->next;
+		i++;
+	}
+	scanf ("%i", &i);
+	curStation = ph->stations;
+	while (curStation != NULL && i > 0) {
+		curStation = curStation->next;
+		i--;
+	}
+	return curStation;
+}
+
+int main (int argc, char **argv) {
+	PianoHandle_t ph;
+	struct aacPlayer player;
+	char doQuit = 0;
+	PianoSong_t *curSong = NULL;
+	PianoStation_t *curStation;
+	struct termios termopts;
+
+	/* init some things */
+	curl_global_init (CURL_GLOBAL_SSL);
+	ao_initialize();
+	PianoInit (&ph);
+
+	/* no buffering for stdin */
+	tcgetattr (fileno (stdin), &termopts);
+	termopts.c_lflag &= ~ICANON;
+	tcsetattr(fileno (stdin), TCSANOW, &termopts);
+	setvbuf (stdin, NULL, _IONBF, 1);
+
+	PianoConnect (&ph, argv[1], argv[2]);
+	PianoGetStations (&ph);
+	printf ("webAuthToken: %s\nauthToken: %s\nlistenerId: %s\n", ph.user.webAuthToken, ph.user.authToken, ph.user.listenerId);
+
+	/* select station */
+	curStation = selectStation (&ph);
+	printf ("playing station %s\n", curStation->name);
+	PianoGetPlaylist (&ph, curStation->id);
+
+	curSong = ph.playlist;
+	/* play first track */
+	while (!doQuit) {
+		PianoSong_t *lastSong = NULL;
+		pthread_t playerThread;
+		printf ("%s by %s\n", curSong->title, curSong->artist);
+		memset (&player, 0, sizeof (player));
+		player.url = strdup (curSong->audioUrl);
+
+		/* start player */
+		pthread_create (&playerThread, NULL, threadPlayUrl, &player);
+
+		/* in the meantime: wait for user actions */
+		while (!player.finishedPlayback) {
+			struct pollfd polls = {fileno (stdin), POLLIN, POLLIN};
+			char buf;
+
+			if (poll (&polls, 1, 1000) > 0) {
+				read (fileno (stdin), &buf, sizeof (buf));
+				switch (buf) {
+					case '?':
+						printf ("n\tnext track\nq\tquit\ns\tchange station\n");
+						break;
+
+					case 'b':
+						player.doQuit = 1;
+						PianoRateTrack (&ph, curStation, curSong,
+								PIANO_RATE_BAN);
+						PianoDestroyPlaylist (&ph);
+						break;
+
+					case 'l':
+						PianoRateTrack (&ph, curStation, curSong,
+								PIANO_RATE_LOVE);
+						break;
+
+					case 'n':
+						player.doQuit = 1;
+						break;
+
+					case 'q':
+						doQuit = 1;
+						player.doQuit = 1;
+						break;
+
+					case 's':
+						/* FIXME: does not work, segfault... */
+						player.doQuit = 1;
+						PianoDestroyPlaylist (&ph);
+						curStation = selectStation (&ph);
+						printf ("changed station to %s\n", curStation->name);
+						break;
+
+				}
+			}
+		}
+		pthread_join (playerThread, NULL);
+
+		free (player.url);
+		/* what's next? */
+		lastSong = curSong;
+		curSong = lastSong->next;
+		if (curSong == NULL && !doQuit) {
+			printf ("receiving new playlist\n");
+			PianoGetPlaylist (&ph, curStation->id);
+			curSong = lastSong->next;
+			if (curSong == NULL) {
+				/* no tracks left */
+				doQuit = 1;
+			}
+		}
+	}
+
+	/* destroy everything (including the world...) */
+	PianoDestroy (&ph);
+	curl_global_cleanup ();
+	ao_shutdown();
+
+	return 0;
+}
-- 
cgit v1.2.3