/* initialize piano handle * @param piano handle * @return nothing */ void PianoInit (PianoHandle_t *ph) { memset (ph, 0, sizeof (*ph)); WaitressInit (&ph->waith); strncpy (ph->waith.host, PIANO_RPC_HOST, sizeof (ph->waith.host)-1); strncpy (ph->waith.port, PIANO_RPC_PORT, sizeof (ph->waith.port)-1); /* route-id seems to be random. we're using time anyway... */ snprintf (ph->routeId, sizeof (ph->routeId), "%07liP", time (NULL) % 10000000); }
void MythPianoService::StartPlayback() { BroadcastMessage("Starting playback"); if (m_Playlist == NULL) { BroadcastMessage("Empty playlist"); return; } if (m_Player.mode != audioPlayer::PLAYER_FREED && m_Player.mode != audioPlayer::PLAYER_FINISHED_PLAYBACK) { BroadcastMessage("So sorry, we think we are already playing. Try again (%d).", m_Player.mode); return; } memset (&m_Player, 0, sizeof (m_Player)); WaitressInit (&m_Player.waith); WaitressSetUrl (&m_Player.waith, m_CurrentSong->audioUrl); m_Player.gain = m_CurrentSong->fileGain; m_Player.audioFormat = m_CurrentSong->audioFormat; m_Player.mode = audioPlayer::PLAYER_STARTING; m_Player.writer = &WriteAudioCallback; m_Player.writerCtx = (void*) this; pthread_create (&m_PlayerThread, NULL, BarPlayerThread, &m_Player); BroadcastMessage("New Song"); if (class LCD *lcd = LCD::Get()) { lcd->switchToMusic(m_CurrentSong->artist, m_CurrentSong->album, m_CurrentSong->title); } if (m_Timer) { m_Timer->stop(); m_Timer->disconnect(); delete m_Timer; } m_Timer = new QTimer(this); connect(m_Timer, SIGNAL(timeout()), this, SLOT(heartbeat())); m_Timer->start(1000); }
int MythPianoService::Login() { m_Piano = (PianoHandle_t*) malloc(sizeof(PianoHandle_t)); gnutls_global_init(); PianoInit (m_Piano); WaitressInit (&m_Waith); m_Waith.url.host = strdup (PIANO_RPC_HOST); m_Waith.url.tls = true; m_Waith.tlsFingerprint = tlsFingerprint; memset (&m_Player, 0, sizeof (m_Player)); QString username = gCoreContext->GetSetting("pandora-username"); QString password = gCoreContext->GetSetting("pandora-password"); //wtf really? char* usernameBuff = strndup(username.toUtf8().data(), 1024); char* passwordBuff = strndup(password.toUtf8().data(), 1024); PianoRequestDataLogin_t reqData; reqData.user = usernameBuff; reqData.password = passwordBuff; reqData.step = 0; PianoReturn_t pRet; WaitressReturn_t wRet; BroadcastMessage("Login... "); if (! PianoCall (PIANO_REQUEST_LOGIN, &reqData, &pRet, &wRet)) { return -1; } // wtf free(usernameBuff); free(passwordBuff); if (! PianoCall (PIANO_REQUEST_GET_STATIONS, &reqData, &pRet, &wRet)) { return -1; } m_CurrentStation = m_Piano->stations; return 0; }
/* start new player thread */ static void BarMainStartPlayback (BarApp_t *app, pthread_t *playerThread) { BarUiPrintSong (&app->settings, app->playlist, app->curStation->isQuickMix ? PianoFindStationById (app->ph.stations, app->playlist->stationId) : NULL); if (app->playlist->audioUrl == NULL) { BarUiMsg (&app->settings, MSG_ERR, "Invalid song url.\n"); } else { /* setup player */ memset (&app->player, 0, sizeof (app->player)); WaitressInit (&app->player.waith); WaitressSetUrl (&app->player.waith, app->playlist->audioUrl); /* set up global proxy, player is NULLed on songfinish */ if (app->settings.proxy != NULL) { WaitressSetProxy (&app->player.waith, app->settings.proxy); } app->player.gain = app->playlist->fileGain; if ( !app->mute ) { app->player.scale = BarPlayerCalcScale (app->player.gain + app->settings.volume); } app->player.audioFormat = app->playlist->audioFormat; app->player.settings = &app->settings; pthread_mutex_init (&app->player.pauseMutex, NULL); pthread_cond_init (&app->player.pauseCond, NULL); /* throw event */ BarUiStartEventCmd (&app->settings, "songstart", app->curStation, app->playlist, &app->player, app->ph.stations, PIANO_RET_OK, WAITRESS_RET_OK); BarDownloadStart(app); /* prevent race condition, mode must _not_ be FREED if * thread has been started */ app->player.mode = PLAYER_STARTING; /* start player */ pthread_create (playerThread, NULL, BarPlayerThread, &app->player); } }
/* authenticate user */ static bool BarMainLoginUser (BarApp_t *app) { PianoReturn_t pRet; WaitressReturn_t wRet; PianoRequestDataLogin_t reqData; bool ret; #if 0 WaitressHandle_t waithSync; char *syncTime; unsigned long int syncTimeInt; /* skip sync step by fetching time from somewhere else */ WaitressInit (&waithSync); WaitressSetUrl (&waithSync, "http://ridetheclown.com/s2/synctime.php"); if (app->settings.proxy != NULL && strlen (app->settings.proxy) > 0) { WaitressSetProxy (&waithSync, app->settings.proxy); } wRet = WaitressFetchBuf (&waithSync, &syncTime); WaitressFree (&waithSync); if (wRet != WAITRESS_RET_OK) { BarUiMsg (&app->settings, MSG_ERR, "Unable to sync: %s\n", WaitressErrorToStr (wRet)); return false; } syncTimeInt = strtoul (syncTime, NULL, 0); app->ph.timeOffset = time (NULL) - syncTimeInt; free (syncTime); #endif app->ph.timeOffset = -30239998; /* woo! magic number */ reqData.user = app->settings.username; reqData.password = app->settings.password; reqData.step = 0; BarUiMsg (&app->settings, MSG_INFO, "Login... "); ret = BarUiPianoCall (app, PIANO_REQUEST_LOGIN, &reqData, &pRet, &wRet); BarUiStartEventCmd (&app->settings, "userlogin", NULL, NULL, &app->player, NULL, pRet, wRet); return ret; }
int main (int argc, char **argv) { static BarApp_t app; /* terminal attributes _before_ we started messing around with ~ECHO */ struct termios termOrig; memset (&app, 0, sizeof (app)); /* save terminal attributes, before disabling echoing */ BarTermSave (&termOrig); BarTermSetEcho (0); BarTermSetBuffer (0); /* signals */ signal (SIGPIPE, SIG_IGN); /* init some things */ ao_initialize (); gcry_check_version (NULL); gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); gnutls_global_init (); BarSettingsInit (&app.settings); BarSettingsRead (&app.settings); PianoInit (&app.ph, app.settings.partnerUser, app.settings.partnerPassword, app.settings.device, app.settings.inkey, app.settings.outkey); BarUiMsg (&app.settings, MSG_NONE, "Welcome to " PACKAGE " (" VERSION ")! "); if (app.settings.keys[BAR_KS_HELP] == BAR_KS_DISABLED) { BarUiMsg (&app.settings, MSG_NONE, "\n"); } else { BarUiMsg (&app.settings, MSG_NONE, "Press %c for a list of commands.\n", app.settings.keys[BAR_KS_HELP]); } WaitressInit (&app.waith); app.waith.url.host = app.settings.rpcHost; app.waith.tlsFingerprint = app.settings.tlsFingerprint; /* init fds */ FD_ZERO(&app.input.set); app.input.fds[0] = STDIN_FILENO; FD_SET(app.input.fds[0], &app.input.set); /* open fifo read/write so it won't EOF if nobody writes to it */ assert (sizeof (app.input.fds) / sizeof (*app.input.fds) >= 2); app.input.fds[1] = open (app.settings.fifo, O_RDWR); if (app.input.fds[1] != -1) { struct stat s; /* check for file type, must be fifo */ fstat (app.input.fds[1], &s); if (!S_ISFIFO (s.st_mode)) { BarUiMsg (&app.settings, MSG_ERR, "File at %s is not a fifo\n", app.settings.fifo); close (app.input.fds[1]); app.input.fds[1] = -1; } else { FD_SET(app.input.fds[1], &app.input.set); BarUiMsg (&app.settings, MSG_INFO, "Control fifo at %s opened\n", app.settings.fifo); } } app.input.maxfd = app.input.fds[0] > app.input.fds[1] ? app.input.fds[0] : app.input.fds[1]; ++app.input.maxfd; BarMainLoop (&app); if (app.input.fds[1] != -1) { close (app.input.fds[1]); } PianoDestroy (&app.ph); PianoDestroyPlaylist (app.songHistory); PianoDestroyPlaylist (app.playlist); WaitressFree (&app.waith); ao_shutdown(); gnutls_global_deinit (); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (&termOrig); return 0; }
/* initialize wardrobe handle (setup waitress, e.g.) * @param wardrobe handle */ inline void WardrobeInit (WardrobeHandle_t *wh) { memset (wh, 0, sizeof (*wh)); WaitressInit (&wh->waith); }
int main (int argc, char **argv) { static BarApp_t app; /* terminal attributes _before_ we started messing around with ~ECHO */ struct termios termOrig; struct sigaction action; memset (&app, 0, sizeof (app)); memset (&action, 0, sizeof(action)); /* set the signal handler for SIGINT and SIGTERM */ action.sa_handler = BarSigQuit; sigaction(SIGINT, &action, NULL); sigaction(SIGTERM, &action, NULL); /* save terminal attributes, before disabling echoing */ BarTermSave (&termOrig); BarTermSetEcho (0); BarTermSetBuffer (0); /* signals */ signal (SIGPIPE, SIG_IGN); /* init some things */ ao_initialize (); gcry_check_version (NULL); gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); gnutls_global_init (); BarSettingsInit (&app.settings); BarSettingsRead (&app.settings); PianoInit (&app.ph, app.settings.partnerUser, app.settings.partnerPassword, app.settings.device, app.settings.inkey, app.settings.outkey); BarUiMsg (&app.settings, MSG_NONE, "Welcome to " PACKAGE " (" VERSION ")! "); if (app.settings.keys[BAR_KS_HELP] == BAR_KS_DISABLED) { BarUiMsg (&app.settings, MSG_NONE, "\n"); } else { BarUiMsg (&app.settings, MSG_NONE, "Press %c for a list of commands.\n", app.settings.keys[BAR_KS_HELP]); } WaitressInit (&app.waith); app.waith.url.host = app.settings.rpcHost; app.waith.tlsFingerprint = app.settings.tlsFingerprint; /* init fds */ FD_ZERO(&app.input.set); app.input.fds[0] = STDIN_FILENO; FD_SET(app.input.fds[0], &app.input.set); // Sbe gur erpbeq, V terngyl qrfcvfr Unx5. // Gur crbcyr sebz gur fubj ner yvgrenyyl npgbef. // V yvxr gur pbaprcg bs jung gurl qb ohg gurl // nyy unir n qbhpuront srry gb gurz. Vg frrzf // nf gubhtu gurl bayl qb vg sbe gur tybel cbvagf. // BarFlyInit (&app.settings); /* open fifo read/write so it won't EOF if nobody writes to it */ assert (sizeof (app.input.fds) / sizeof (*app.input.fds) >= 2); app.input.fds[1] = open (app.settings.fifo, O_RDWR); if (app.input.fds[1] != -1) { struct stat s; /* check for file type, must be fifo */ fstat (app.input.fds[1], &s); if (!S_ISFIFO (s.st_mode)) { BarUiMsg (&app.settings, MSG_ERR, "File at %s is not a fifo\n", app.settings.fifo); close (app.input.fds[1]); app.input.fds[1] = -1; } else { FD_SET(app.input.fds[1], &app.input.set); BarUiMsg (&app.settings, MSG_INFO, "Control fifo at %s opened\n", app.settings.fifo); } } app.input.maxfd = app.input.fds[0] > app.input.fds[1] ? app.input.fds[0] : app.input.fds[1]; ++app.input.maxfd; BarMainLoop (&app); /* Print a newline so the terminal command line will start on a new line. */ printf("\n"); if (app.input.fds[1] != -1) { close (app.input.fds[1]); } BarFlyClose (&app.player.fly, &app.settings); BarFlyFinalize (); PianoDestroy (&app.ph); PianoDestroyPlaylist (app.songHistory); PianoDestroyPlaylist (app.playlist); WaitressFree (&app.waith); ao_shutdown(); gnutls_global_deinit (); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (&termOrig); return 0; }
int main (int argc, char **argv) { static BarApp_t app; pthread_t playerThread; /* FIXME: max path length? */ char ctlPath[1024]; FILE *ctlFd = NULL; struct timeval selectTimeout; int maxFd, selectFds[2]; fd_set readSet, readSetCopy; char buf = '\0'; /* terminal attributes _before_ we started messing around with ~ECHO */ struct termios termOrig; memset (&app, 0, sizeof (app)); /* save terminal attributes, before disabling echoing */ BarTermSave (&termOrig); BarTermSetEcho (0); BarTermSetBuffer (0); /* init some things */ ao_initialize (); PianoInit (&app.ph); WaitressInit (&app.waith); strncpy (app.waith.host, PIANO_RPC_HOST, sizeof (app.waith.host)-1); strncpy (app.waith.port, PIANO_RPC_PORT, sizeof (app.waith.port)-1); BarSettingsInit (&app.settings); BarSettingsRead (&app.settings); BarUiMsg (MSG_NONE, "Welcome to " PACKAGE "! Press %c for a list of commands.\n", app.settings.keys[BAR_KS_HELP]); /* init fds */ FD_ZERO(&readSet); selectFds[0] = fileno (stdin); FD_SET(selectFds[0], &readSet); maxFd = selectFds[0] + 1; BarGetXdgConfigDir (PACKAGE "/ctl", ctlPath, sizeof (ctlPath)); /* FIXME: why is r_+_ required? */ ctlFd = fopen (ctlPath, "r+"); if (ctlFd != NULL) { selectFds[1] = fileno (ctlFd); FD_SET(selectFds[1], &readSet); /* assuming ctlFd is always > stdin */ maxFd = selectFds[1] + 1; BarUiMsg (MSG_INFO, "Control fifo at %s opened\n", ctlPath); } if (app.settings.username == NULL) { char nameBuf[100]; BarUiMsg (MSG_QUESTION, "Username: "******"Password: "******"Login... "); if (!BarUiPianoCall (&app.ph, PIANO_REQUEST_LOGIN, &app.waith, &reqData, &pRet, &wRet)) { BarTermRestore (&termOrig); return 0; } } { PianoReturn_t pRet; WaitressReturn_t wRet; BarUiMsg (MSG_INFO, "Get stations... "); if (!BarUiPianoCall (&app.ph, PIANO_REQUEST_GET_STATIONS, &app.waith, NULL, &pRet, &wRet)) { BarTermRestore (&termOrig); return 0; } } /* try to get autostart station */ if (app.settings.autostartStation != NULL) { app.curStation = PianoFindStationById (app.ph.stations, app.settings.autostartStation); if (app.curStation == NULL) { BarUiMsg (MSG_ERR, "Error: Autostart station not found.\n"); } } /* no autostart? ask the user */ if (app.curStation == NULL) { app.curStation = BarUiSelectStation (&app.ph, "Select station: ", app.settings.sortOrder, stdin); } if (app.curStation != NULL) { BarUiPrintStation (app.curStation); } /* little hack, needed to signal: hey! we need a playlist, but don't * free anything (there is nothing to be freed yet) */ memset (&app.player, 0, sizeof (app.player)); while (!app.doQuit) { /* song finished playing, clean up things/scrobble song */ if (app.player.mode == PLAYER_FINISHED_PLAYBACK) { BarUiStartEventCmd (&app.settings, "songfinish", app.curStation, app.playlist, &app.player, PIANO_RET_OK, WAITRESS_RET_OK); /* FIXME: pthread_join blocks everything if network connection * is hung up e.g. */ void *threadRet; pthread_join (playerThread, &threadRet); /* don't continue playback if thread reports error */ if (threadRet != (void *) PLAYER_RET_OK) { app.curStation = NULL; } memset (&app.player, 0, sizeof (app.player)); } /* check whether player finished playing and start playing new * song */ if (app.player.mode >= PLAYER_FINISHED_PLAYBACK || app.player.mode == PLAYER_FREED) { if (app.curStation != NULL) { /* what's next? */ if (app.playlist != NULL) { if (app.settings.history != 0) { /* prepend song to history list */ PianoSong_t *tmpSong = app.songHistory; app.songHistory = app.playlist; /* select next song */ app.playlist = app.playlist->next; app.songHistory->next = tmpSong; /* limit history's length */ /* start with 1, so we're stopping at n-1 and have the * chance to set ->next = NULL */ unsigned int i = 1; tmpSong = app.songHistory; while (i < app.settings.history && tmpSong != NULL) { tmpSong = tmpSong->next; ++i; } /* if too many songs in history... */ if (tmpSong != NULL) { PianoSong_t *delSong = tmpSong->next; tmpSong->next = NULL; if (delSong != NULL) { PianoDestroyPlaylist (delSong); } } } else { /* don't keep history */ app.playlist = app.playlist->next; } } if (app.playlist == NULL) { PianoReturn_t pRet; WaitressReturn_t wRet; PianoRequestDataGetPlaylist_t reqData; reqData.station = app.curStation; reqData.format = app.settings.audioFormat; BarUiMsg (MSG_INFO, "Receiving new playlist... "); if (!BarUiPianoCall (&app.ph, PIANO_REQUEST_GET_PLAYLIST, &app.waith, &reqData, &pRet, &wRet)) { app.curStation = NULL; } else { app.playlist = reqData.retPlaylist; if (app.playlist == NULL) { BarUiMsg (MSG_INFO, "No tracks left.\n"); app.curStation = NULL; } } BarUiStartEventCmd (&app.settings, "stationfetchplaylist", app.curStation, app.playlist, &app.player, pRet, wRet); } /* song ready to play */ if (app.playlist != NULL) { BarUiPrintSong (app.playlist, app.curStation->isQuickMix ? PianoFindStationById (app.ph.stations, app.playlist->stationId) : NULL); if (app.playlist->audioUrl == NULL) { BarUiMsg (MSG_ERR, "Invalid song url.\n"); } else { /* setup player */ memset (&app.player, 0, sizeof (app.player)); WaitressInit (&app.player.waith); WaitressSetUrl (&app.player.waith, app.playlist->audioUrl); /* set up global proxy, player is NULLed on songfinish */ if (app.settings.proxy != NULL) { char tmpPath[2]; WaitressSplitUrl (app.settings.proxy, app.player.waith.proxyHost, sizeof (app.player.waith.proxyHost), app.player.waith.proxyPort, sizeof (app.player.waith.proxyPort), tmpPath, sizeof (tmpPath)); } app.player.gain = app.playlist->fileGain; app.player.audioFormat = app.playlist->audioFormat; /* throw event */ BarUiStartEventCmd (&app.settings, "songstart", app.curStation, app.playlist, &app.player, PIANO_RET_OK, WAITRESS_RET_OK); /* prevent race condition, mode must _not_ be FREED if * thread has been started */ app.player.mode = PLAYER_STARTING; /* start player */ pthread_create (&playerThread, NULL, BarPlayerThread, &app.player); } /* end if audioUrl == NULL */ } /* end if playlist != NULL */ } /* end if curStation != NULL */ } /* select modifies its arguments => copy the set */ memcpy (&readSetCopy, &readSet, sizeof (readSet)); selectTimeout.tv_sec = 1; selectTimeout.tv_usec = 0; /* in the meantime: wait for user actions */ if (select (maxFd, &readSetCopy, NULL, NULL, &selectTimeout) > 0) { FILE *curFd = NULL; if (FD_ISSET(selectFds[0], &readSetCopy)) { curFd = stdin; } else if (FD_ISSET(selectFds[1], &readSetCopy)) { curFd = ctlFd; } buf = fgetc (curFd); size_t i; for (i = 0; i < BAR_KS_COUNT; i++) { if (app.settings.keys[i] == buf) { static const BarKeyShortcutFunc_t idToF[] = {BarUiActHelp, BarUiActLoveSong, BarUiActBanSong, BarUiActAddMusic, BarUiActCreateStation, BarUiActDeleteStation, BarUiActExplain, BarUiActStationFromGenre, BarUiActHistory, BarUiActSongInfo, BarUiActAddSharedStation, BarUiActMoveSong, BarUiActSkipSong, BarUiActPause, BarUiActQuit, BarUiActRenameStation, BarUiActSelectStation, BarUiActTempBanSong, BarUiActPrintUpcoming, BarUiActSelectQuickMix, BarUiActDebug, BarUiActBookmark}; idToF[i] (&app, curFd); break; } } } /* show time */ if (app.player.mode >= PLAYER_SAMPLESIZE_INITIALIZED && app.player.mode < PLAYER_FINISHED_PLAYBACK) { /* Ugly: songDuration is unsigned _long_ int! Lets hope this won't * overflow */ int songRemaining = (signed long int) (app.player.songDuration - app.player.songPlayed) / BAR_PLAYER_MS_TO_S_FACTOR; char pos = 0; if (songRemaining < 0) { /* Use plus sign if song is longer than expected */ pos = 1; songRemaining = -songRemaining; } BarUiMsg (MSG_TIME, "%c%02i:%02i/%02i:%02i\r", (pos ? '+' : '-'), songRemaining / 60, songRemaining % 60, app.player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR / 60, app.player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR % 60); } } /* destroy everything (including the world...) */ if (app.player.mode != PLAYER_FREED) { pthread_join (playerThread, NULL); } if (ctlFd != NULL) { fclose (ctlFd); } PianoDestroy (&app.ph); PianoDestroyPlaylist (app.songHistory); PianoDestroyPlaylist (app.playlist); ao_shutdown(); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (&termOrig); return 0; }
int BarFlyInit(BarSettings_t const* settings) { char const* const PATH_SEPARATORS = "/"; int exit_status = 0; int status; bool statusb; char* component; char* path = NULL; char* proxy = NULL; assert(settings != NULL); assert(settings->audioFileDir != NULL); /* * Initialize the Waitress handle. */ WaitressInit(&fly_waith); if (settings->controlProxy != NULL) { proxy = settings->controlProxy; } else if (settings->proxy != NULL) { proxy = settings->proxy; } if (proxy != NULL) { statusb = WaitressSetProxy(&fly_waith, proxy); if (!statusb) { BarUiMsg(settings, MSG_ERR, "Could not set proxy (proxy = '%s').\n", proxy); } } /* * Create the audio file directory and change into it. */ path = strdup(settings->audioFileDir); if (path == NULL) { BarUiMsg(settings, MSG_ERR, "Out of memory.\n"); goto error; } if (path[0] == '/') { status = chdir("/"); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Could not create the audio file directory (%s).\n", path); goto error; } } component = strtok(path, PATH_SEPARATORS); while (component != NULL) { if ((strcmp(component, ".") == 0) || (strcmp(component, "") == 0)) { /* * This is the current directory. Do Nothing. */ } else if (strcmp(component, "..") == 0) { /* * Just change back up one directory. */ status = chdir(".."); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Could not create the audio file " "directory (%s).\n", path); goto error; } } else { /* * Create the component of the path and change into it. */ status = mkdir(component, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); if ((status != 0) && (errno != EEXIST)) { BarUiMsg(settings, MSG_ERR, "Could not create the audio file " "directory (%s).\n", path); goto error; } status = chdir(component); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Could not create the audio file " "directory (%s).\n", path); goto error; } } component = strtok(NULL, PATH_SEPARATORS); } goto end; error: exit_status = -1; end: if (path != NULL) { free(path); } return exit_status; }
int main (int argc, char **argv) { static BarApp_t app; char ctlPath[PATH_MAX]; /* terminal attributes _before_ we started messing around with ~ECHO */ struct termios termOrig; memset (&app, 0, sizeof (app)); /* save terminal attributes, before disabling echoing */ BarTermSave (&termOrig); BarTermSetEcho (0); BarTermSetBuffer (0); /* init some things */ ao_initialize (); PianoInit (&app.ph); WaitressInit (&app.waith); app.waith.url.host = strdup (PIANO_RPC_HOST); app.waith.url.port = strdup (PIANO_RPC_PORT); BarSettingsInit (&app.settings); BarSettingsRead (&app.settings); BarUiMsg (&app.settings, MSG_NONE, "Welcome to " PACKAGE " (" VERSION ")! "); if (app.settings.keys[BAR_KS_HELP] == BAR_KS_DISABLED) { BarUiMsg (&app.settings, MSG_NONE, "\n"); } else { BarUiMsg (&app.settings, MSG_NONE, "Press %c for a list of commands.\n", app.settings.keys[BAR_KS_HELP]); } /* init fds */ FD_ZERO(&app.input.set); app.input.fds[0] = STDIN_FILENO; FD_SET(app.input.fds[0], &app.input.set); BarGetXdgConfigDir (PACKAGE "/ctl", ctlPath, sizeof (ctlPath)); /* open fifo read/write so it won't EOF if nobody writes to it */ assert (sizeof (app.input.fds) / sizeof (*app.input.fds) >= 2); app.input.fds[1] = open (ctlPath, O_RDWR); if (app.input.fds[1] != -1) { FD_SET(app.input.fds[1], &app.input.set); BarUiMsg (&app.settings, MSG_INFO, "Control fifo at %s opened\n", ctlPath); } app.input.maxfd = app.input.fds[0] > app.input.fds[1] ? app.input.fds[0] : app.input.fds[1]; ++app.input.maxfd; BarMainLoop (&app); if (app.input.fds[1] != -1) { close (app.input.fds[1]); } PianoDestroy (&app.ph); PianoDestroyPlaylist (app.songHistory); PianoDestroyPlaylist (app.playlist); ao_shutdown(); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (&termOrig); return 0; }
/* start new player thread */ static void BarMainStartPlayback (BarApp_t *app, pthread_t *playerThread) { PianoReturn_t pRet; WaitressReturn_t wRet; /* is this an advertising track? */ if (app->playlist->adToken != NULL) { PianoRequestDataGetAdMetadata_t adReqData; adReqData.token = app->playlist->adToken; adReqData.quality = app->settings.audioQuality; BarUiMsg (&app->settings, MSG_INFO, "Fetching ads with token %s... ", adReqData.token); BarUiPianoCall (app, PIANO_REQUEST_GET_AD_METADATA, &adReqData, &pRet, &wRet); /* got token? */ if (adReqData.retTokenCount > 0) { PianoRequestDataRegisterAd_t regReqData; regReqData.token = adReqData.retToken; regReqData.tokenCount = adReqData.retTokenCount; regReqData.station = app->curStation; BarUiMsg (&app->settings, MSG_INFO, "Registering ad... "); BarUiPianoCall (app, PIANO_REQUEST_REGISTER_AD, ®ReqData, &pRet, &wRet); // change the current song to the actual audio ad url app->playlist->audioUrl = adReqData.audioUrl; // you can configure to get silence instead of ads or you can choose // to hear the ads. The default is silence if (app->settings.silenceAds) { app->playlist->fileGain = -999; app->playlist->title = strdup("Audio Ad (Silenced)"); } else { app->playlist->fileGain = adReqData.fileGain; app->playlist->title = strdup("Audio Ad"); } app->playlist->audioFormat = adReqData.audioFormat; app->playlist->artist = strdup("advertiser"); app->playlist->album = strdup("pianobar"); /* delete */ for (size_t i = 0; i < adReqData.retTokenCount; i++) { free (adReqData.retToken[i]); } free (adReqData.retToken); } } BarUiPrintSong (&app->settings, app->playlist, app->curStation->isQuickMix ? PianoFindStationById (app->ph.stations, app->playlist->stationId) : NULL); if (app->playlist->audioUrl == NULL) { BarUiMsg (&app->settings, MSG_ERR, "Invalid song url.\n"); } else { /* setup player */ memset (&app->player, 0, sizeof (app->player)); WaitressInit (&app->player.waith); WaitressSetUrl (&app->player.waith, app->playlist->audioUrl); /* set up global proxy, player is NULLed on songfinish */ if (app->settings.proxy != NULL) { WaitressSetProxy (&app->player.waith, app->settings.proxy); } app->player.gain = app->playlist->fileGain; app->player.scale = BarPlayerCalcScale (app->player.gain + app->settings.volume); app->player.audioFormat = app->playlist->audioFormat; app->player.settings = &app->settings; pthread_mutex_init (&app->player.pauseMutex, NULL); pthread_cond_init (&app->player.pauseCond, NULL); /* throw event */ BarUiStartEventCmd (&app->settings, "songstart", app->curStation, app->playlist, &app->player, app->ph.stations, PIANO_RET_OK, WAITRESS_RET_OK); /* prevent race condition, mode must _not_ be FREED if * thread has been started */ app->player.mode = PLAYER_STARTING; /* start player */ pthread_create (playerThread, NULL, BarPlayerThread, &app->player); } }
int main (int argc, char **argv) { /* handles */ PianoHandle_t ph; static struct audioPlayer player; BarSettings_t settings; pthread_t playerThread; /* playlist; first item is current song */ PianoSong_t *playlist = NULL; PianoSong_t *songHistory = NULL; PianoStation_t *curStation = NULL; char doQuit = 0; /* FIXME: max path length? */ char ctlPath[1024]; FILE *ctlFd = NULL; struct timeval selectTimeout; int maxFd, selectFds[2]; fd_set readSet, readSetCopy; char buf = '\0'; /* terminal attributes _before_ we started messing around with ~ECHO */ struct termios termOrig; BarUiMsg (MSG_NONE, "Welcome to " PACKAGE "!\n"); /* save terminal attributes, before disabling echoing */ BarTermSave (&termOrig); BarTermSetEcho (0); BarTermSetBuffer (0); /* init some things */ ao_initialize (); PianoInit (&ph); BarSettingsInit (&settings); BarSettingsRead (&settings); /* init fds */ FD_ZERO(&readSet); selectFds[0] = fileno (stdin); FD_SET(selectFds[0], &readSet); maxFd = selectFds[0] + 1; BarGetXdgConfigDir (PACKAGE "/ctl", ctlPath, sizeof (ctlPath)); /* FIXME: why is r_+_ required? */ ctlFd = fopen (ctlPath, "r+"); if (ctlFd != NULL) { selectFds[1] = fileno (ctlFd); FD_SET(selectFds[1], &readSet); /* assuming ctlFd is always > stdin */ maxFd = selectFds[1] + 1; BarUiMsg (MSG_INFO, "Control fifo at %s opened\n", ctlPath); } if (settings.username == NULL) { char nameBuf[100]; BarUiMsg (MSG_QUESTION, "Username: "******"Password: "******"Login... "); if (BarUiPrintPianoStatus (PianoConnect (&ph, settings.username, settings.password)) != PIANO_RET_OK) { BarTermRestore (&termOrig); return 0; } BarUiMsg (MSG_INFO, "Get stations... "); if (BarUiPrintPianoStatus (PianoGetStations (&ph)) != PIANO_RET_OK) { BarTermRestore (&termOrig); return 0; } /* try to get autostart station */ if (settings.autostartStation != NULL) { curStation = PianoFindStationById (ph.stations, settings.autostartStation); if (curStation == NULL) { BarUiMsg (MSG_ERR, "Error: Autostart station not found.\n"); } } /* no autostart? ask the user */ if (curStation == NULL) { curStation = BarUiSelectStation (&ph, "Select station: ", stdin); } if (curStation != NULL) { BarUiPrintStation (curStation); } /* little hack, needed to signal: hey! we need a playlist, but don't * free anything (there is nothing to be freed yet) */ memset (&player, 0, sizeof (player)); while (!doQuit) { /* song finished playing, clean up things/scrobble song */ if (player.mode == PLAYER_FINISHED_PLAYBACK) { BarUiStartEventCmd (&settings, "songfinish", curStation, playlist, &player, PIANO_RET_OK); /* FIXME: pthread_join blocks everything if network connection * is hung up e.g. */ void *threadRet; pthread_join (playerThread, &threadRet); /* don't continue playback if thread reports error */ if (threadRet != NULL) { curStation = NULL; } memset (&player, 0, sizeof (player)); } /* check whether player finished playing and start playing new * song */ if (player.mode >= PLAYER_FINISHED_PLAYBACK || player.mode == PLAYER_FREED) { if (curStation != NULL) { /* what's next? */ if (playlist != NULL) { if (settings.history != 0) { /* prepend song to history list */ PianoSong_t *tmpSong = songHistory; songHistory = playlist; /* select next song */ playlist = playlist->next; songHistory->next = tmpSong; /* limit history's length */ /* start with 1, so we're stopping at n-1 and have the * chance to set ->next = NULL */ unsigned int i = 1; tmpSong = songHistory; while (i < settings.history && tmpSong != NULL) { tmpSong = tmpSong->next; ++i; } /* if too many songs in history... */ if (tmpSong != NULL) { PianoSong_t *delSong = tmpSong->next; tmpSong->next = NULL; if (delSong != NULL) { PianoDestroyPlaylist (delSong); } } } else { /* don't keep history */ playlist = playlist->next; } } if (playlist == NULL) { PianoReturn_t pRet = PIANO_RET_ERR; BarUiMsg (MSG_INFO, "Receiving new playlist... "); if ((pRet = BarUiPrintPianoStatus (PianoGetPlaylist (&ph, curStation->id, settings.audioFormat, &playlist))) != PIANO_RET_OK) { curStation = NULL; } else { if (playlist == NULL) { BarUiMsg (MSG_INFO, "No tracks left.\n"); curStation = NULL; } } BarUiStartEventCmd (&settings, "stationfetchplaylist", curStation, playlist, &player, pRet); } /* song ready to play */ if (playlist != NULL) { BarUiPrintSong (playlist, curStation->isQuickMix ? PianoFindStationById (ph.stations, playlist->stationId) : NULL); if (playlist->audioUrl == NULL) { BarUiMsg (MSG_ERR, "Invalid song url.\n"); } else { /* setup player */ memset (&player, 0, sizeof (player)); WaitressInit (&player.waith); WaitressSetUrl (&player.waith, playlist->audioUrl); player.gain = playlist->fileGain; player.audioFormat = playlist->audioFormat; /* Setup dump directories. */ prepare_dump_name(player.dump_filename, playlist); /* Setup dump handle. If we can read the file, * then it already exists so don't re-write. */ if (access(player.dump_filename, R_OK) != 0) { player.dump_handle = fopen(player.dump_filename, "w"); BarUiMsg(MSG_INFO, "Will dump song...\n"); } else { player.dump_handle = NULL; BarUiMsg(MSG_INFO, "Dump file found, will not dump!\n"); } /* throw event */ BarUiStartEventCmd (&settings, "songstart", curStation, playlist, &player, PIANO_RET_OK); /* start player */ pthread_create (&playerThread, NULL, BarPlayerThread, &player); } /* end if audioUrl == NULL */ } /* end if playlist != NULL */ } /* end if curStation != NULL */ } /* select modifies its arguments => copy the set */ memcpy (&readSetCopy, &readSet, sizeof (readSet)); selectTimeout.tv_sec = 1; selectTimeout.tv_usec = 0; /* in the meantime: wait for user actions */ if (select (maxFd, &readSetCopy, NULL, NULL, &selectTimeout) > 0) { FILE *curFd = NULL; if (FD_ISSET(selectFds[0], &readSetCopy)) { curFd = stdin; } else if (FD_ISSET(selectFds[1], &readSetCopy)) { curFd = ctlFd; } buf = fgetc (curFd); size_t i; for (i = 0; i < BAR_KS_COUNT; i++) { if (settings.keys[i] == buf) { BarKeyShortcutFunc_t idToF[] = {BarUiActHelp, BarUiActLoveSong, BarUiActBanSong, BarUiActAddMusic, BarUiActCreateStation, BarUiActDeleteStation, BarUiActExplain, BarUiActStationFromGenre, BarUiActHistory, BarUiActSongInfo, BarUiActAddSharedStation, BarUiActMoveSong, BarUiActSkipSong, BarUiActPause, BarUiActQuit, BarUiActRenameStation, BarUiActSelectStation, BarUiActTempBanSong, BarUiActPrintUpcoming, BarUiActSelectQuickMix, BarUiActDebug, BarUiActBookmark}; idToF[i] (&ph, &player, &settings, &playlist, &curStation, &songHistory, &doQuit, curFd); break; } } } /* show time */ if (player.mode >= PLAYER_SAMPLESIZE_INITIALIZED && player.mode < PLAYER_FINISHED_PLAYBACK) { /* Ugly: songDuration is unsigned _long_ int! Lets hope this won't * overflow */ int songRemaining = (signed long int) (player.songDuration - player.songPlayed) / BAR_PLAYER_MS_TO_S_FACTOR; char pos = 0; if (songRemaining < 0) { /* Use plus sign if song is longer than expected */ pos = 1; songRemaining = -songRemaining; } BarUiMsg (MSG_TIME, "%c%02i:%02i/%02i:%02i\r", (pos ? '+' : '-'), songRemaining / 60, songRemaining % 60, player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR / 60, player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR % 60); } } /* destroy everything (including the world...) */ if (player.mode != PLAYER_FREED) { pthread_join (playerThread, NULL); } if (ctlFd != NULL) { fclose (ctlFd); } PianoDestroy (&ph); PianoDestroyPlaylist (songHistory); PianoDestroyPlaylist (playlist); ao_shutdown(); BarSettingsDestroy (&settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (&termOrig); return 0; }