diff --git a/Makefile b/Makefile index badfaf1..2b75550 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # CFLAGS := -I$(MARVELL_ROOTFS)/usr/include/SDL2 -LDFLAGS := -lSDL2 +LDFLAGS := -lSDL2 -lSDL2_ttf testspriteminimal: testspriteminimal.o $(CC) -o $@ $< $(LDFLAGS) diff --git a/README.md b/README.md index daada8c..3961e04 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,10 @@ As such, note that: ## Functionalities This code allows to build an app that you can install and run on your _steamlink_. Once it is installed, you can launch it directly from the _steamlink_ home menu. This app starts _librespot_ as a background program, and pipes the audio to the first audio device it finds. -You juste have to use the _spotify connect_ (from your phone or a computer, using the official Spotify application) to start a playlist. +You juste have to use _spotify connect_ (from your phone or a computer, using the official Spotify application) to start a song. Once you're done, you can exit the app by pressing any key on your keyboard or any buttons on your gamepad (should be compatible whith the Steam gamepad). -**Be aware that this is all the app does for now! There is no current track or status information displayed, it's basically just a headless player, and the only thing displayed on the TV is a static image.** +**Be aware that this is all the app does for now! Since the new version, the librespot logs are displayed, allowing you to know which track is being played. But still, there is no complex GUI with album cover or so, it's basically just a headless player with a very simple interface.** # Instructions ## Building @@ -88,7 +88,7 @@ By default the app is configured to redirect _librespot_ logs into this file: If you have trouble using _spotify4steamlink_, you should start by having a look at this file ;). As I said earlier, you might also encounter troubles when exiting the app. For now, the only workaround is to unplug the _steamlink_, and then plug it back again. -Still, during my tests, I noticed the problem don't seem to happen when you exit _while playing a song_. +Still, during my tests, I noticed the problem seems **not to happen** if you exit **while playing a song**. Hopefully having this knowledge will prevent you troubles :). # Acknowledgements diff --git a/build_steamlink.sh b/build_steamlink.sh index db722bd..72fe262 100644 --- a/build_steamlink.sh +++ b/build_steamlink.sh @@ -17,7 +17,8 @@ export DESTDIR="${PWD}/steamlink/apps/spotify" # Copy the files to the app directory mkdir -p "${DESTDIR}" cp -v testspriteminimal "${DESTDIR}" -cp -v spotify2.bmp "${DESTDIR}" +cp -v spotify3.bmp "${DESTDIR}" +cp -v consolas.ttf "${DESTDIR}" cp -v start_librespot.sh "${DESTDIR}" cp -v icon2.png "${DESTDIR}/icon.png" diff --git a/consolas.ttf b/consolas.ttf new file mode 100644 index 0000000..e881ca4 Binary files /dev/null and b/consolas.ttf differ diff --git a/spotify2.bmp b/spotify2.bmp deleted file mode 100644 index 9b464ed..0000000 Binary files a/spotify2.bmp and /dev/null differ diff --git a/spotify3.bmp b/spotify3.bmp new file mode 100644 index 0000000..bfb985e Binary files /dev/null and b/spotify3.bmp differ diff --git a/testspriteminimal.c b/testspriteminimal.c index 3081c2f..5e3bcc0 100644 --- a/testspriteminimal.c +++ b/testspriteminimal.c @@ -17,30 +17,59 @@ #include #endif -#include "SDL.h" - -#define WINDOW_WIDTH 640 -#define WINDOW_HEIGHT 480 - -#define AUDIO_SAMPLES 4096 +#include "SDL.h" +#include "SDL_ttf.h" + +#define INITIAL_WINDOW_WIDTH 640 +#define INITIAL_WINDOW_HEIGHT 480 + +#define AUDIO_SAMPLES 4096 + +#define LIBRESPOT_START_CMD "/home/steam/librespot-org-build/arm-unknown-linux-gnueabihf/release/librespot --cache /var/cache --disable-audio-cache --name steamlink --disable-discovery --bitrate 320 --initial-volume 85 --backend pipe 2>/tmp/spotify.log" +#define LIBRESPOT_KILL_CMD "pidof /home/steam/librespot-org-build/arm-unknown-linux-gnueabihf/release/librespot | xargs kill" +#define LIBRESPOT_LOG_FILE "/tmp/spotify.log" + +#define BACKGROUND_BMP_FILE "spotify3.bmp" + +#define FONT_FILE "consolas.ttf" +#define FONT_SIZE 18 // Disable this flag when publishing to steamlink //#define TEST_MODE + + +/* -- Global variables -- */ static SDL_Texture *sprite; static int sprite_w, sprite_h; - SDL_Renderer *renderer; int done; - -// variable declarations -static FILE *audio_buf; // global pointer to the audio buffer to be played +static FILE *audio_buf; // global pointer to the audio buffer to be played +TTF_Font *font = NULL; +static FILE *spotify_log_file; +SDL_Color text_color = {255, 255, 255}; // white +SDL_Surface *text_surf = NULL; +static int text_square_pos_x, text_square_pos_y, text_square_pos_w, text_square_pos_h; + + +/* -- Functions -- */ /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ static void quit(int rc) { exit(rc); +} + +void compute_text_square_dimensions(SDL_Window* window) +{ + int window_w, window_h; + SDL_GetWindowSize(window, &window_w, &window_h); + // Converting dimensions from a 1920*1080 screen if necessary + text_square_pos_x = 680 * window_w / 1920; + text_square_pos_y = 126 * window_h / 1080; + text_square_pos_w = 1000 * window_w / 1920; + text_square_pos_h = 906 * window_h / 1080; } int @@ -68,9 +97,70 @@ LoadSprite(char *file, SDL_Renderer *renderer) /* We're ready to roll. :) */ return (0); +} + +// Read the tail of the log file +char* tailLogFile() +{ + long lSize; + char * buffer; + size_t result; + + // obtain file size: + fseek (spotify_log_file , 0 , SEEK_END); + lSize = ftell (spotify_log_file); + rewind (spotify_log_file); + + // allocate memory to contain the whole file: + buffer = (char*) malloc (sizeof(char)*lSize); + if (buffer == NULL) {quit(2);} + + // copy the file into the buffer: + result = fread (buffer,1,lSize,spotify_log_file); + if (result != lSize) {quit(3);} + + return buffer; +} + +// Render the text surface +void +renderText(SDL_Renderer * renderer) +{ + // Write our text into the surface + char* buffer = tailLogFile(); + text_surf = TTF_RenderUTF8_Blended_Wrapped(font, buffer, text_color, text_square_pos_w); + free(buffer); + // Create texture from the surface + SDL_Texture *sprite = SDL_CreateTextureFromSurface(renderer, text_surf); + if (!sprite) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture from text surface: %s\n", SDL_GetError()); + SDL_FreeSurface(text_surf); + quit (-1); + } + // Set the position and size of source and destination rectangles + int actual_w = text_surf->w; + int actual_h = (text_surf->h < text_square_pos_h) ? text_surf->h : text_square_pos_h; + int actual_y = (text_surf->h < text_square_pos_h) ? 0 : (text_surf->h - text_square_pos_h); + SDL_Rect *srcrect = (SDL_Rect *)malloc(sizeof(SDL_Rect)); + srcrect->x = 0; + srcrect->y = actual_y; + srcrect->w = actual_w; + srcrect->h = actual_h; + SDL_Rect *dstrect = (SDL_Rect *)malloc(sizeof(SDL_Rect)); + dstrect->x = text_square_pos_x; + dstrect->y = text_square_pos_y; + dstrect->w = actual_w; + dstrect->h = actual_h; + // Blit the text onto the screen + SDL_RenderCopy(renderer, sprite, srcrect, dstrect); + // Free memory + SDL_FreeSurface(text_surf); + SDL_DestroyTexture(sprite); + free(dstrect); + free(srcrect); } -// Render a sprite in position (0, 0) on a black screen +// Render a sprite fullscreen void renderBackground(SDL_Renderer * renderer, SDL_Texture * sprite) { @@ -78,17 +168,8 @@ renderBackground(SDL_Renderer * renderer, SDL_Texture * sprite) SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF); SDL_RenderClear(renderer); - SDL_Rect *position = malloc(sizeof(SDL_Rect)); - position->x = 0; - position->y = 0; - position->w = sprite_w; - position->h = sprite_h; - /* Blit the sprite onto the screen */ - SDL_RenderCopy(renderer, sprite, NULL, position); - - /* Update the screen! */ - SDL_RenderPresent(renderer); + SDL_RenderCopy(renderer, sprite, NULL, NULL); } // audio callback function @@ -122,7 +203,10 @@ void loop() } } - renderBackground(renderer, sprite); + renderBackground(renderer, sprite); + renderText(renderer); + /* Update the screen! */ + SDL_RenderPresent(renderer); #ifdef __EMSCRIPTEN__ if (done) { @@ -135,7 +219,7 @@ void loop() void initAudio() { // Initialize SDL. if (SDL_Init(SDL_INIT_AUDIO) < 0) - return 1; + quit(1); static SDL_AudioSpec want; // the specs of our piece of music SDL_memset(&want, 0, sizeof(want)); @@ -150,7 +234,7 @@ void initAudio() { /* Open the audio device */ if ( SDL_OpenAudio(&want, NULL) < 0 ){ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", SDL_GetError()); - exit(-1); + quit(-1); } /* Start playing */ @@ -166,12 +250,12 @@ void openAudioBuffer() { #else // Make sure the env variable "http_proxy" is not set unsetenv("http_proxy"); - audio_buf = popen("/home/steam/librespot-org-build/arm-unknown-linux-gnueabihf/release/librespot -v --cache /var/cache --disable-audio-cache --name steamlink --disable-discovery --bitrate 320 --initial-volume 100 --backend pipe 2>/tmp/spotify.log", "r"); + audio_buf = popen(LIBRESPOT_START_CMD, "r"); #endif // TEST_MODE if (audio_buf == NULL){ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error starting librespot: %s\n", SDL_GetError()); - exit(-1); + quit(-1); } } @@ -181,7 +265,7 @@ void closeAudioBuffer() { fclose(audio_buf); #else // Send the SIGTERM signal to librespot - system("pidof /home/steam/librespot-org-build/arm-unknown-linux-gnueabihf/release/librespot | xargs kill"); + system(LIBRESPOT_KILL_CMD); // Close the pipe to librespot pclose(audio_buf); #endif // TEST_MODE @@ -192,44 +276,88 @@ void stopAudio() { // shut everything down SDL_Delay(100); SDL_CloseAudio(); +} + +void openSpotifyLogFile() { + +#ifdef TEST_MODE + spotify_log_file = fopen("spotify.log", "r"); +#else + spotify_log_file = fopen(LIBRESPOT_LOG_FILE, "r"); +#endif // TEST_MODE + + if (spotify_log_file == NULL){ + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error opening librespot log file: %s\n", SDL_GetError()); + quit(-1); + } } +void closeSpotifyLogFile() { + fclose(spotify_log_file); +} + + +/* -- MAIN function -- */ + int main(int argc, char *argv[]) -{ +{ + /* SDL Init - Graphics */ SDL_Window *window; - - /* Enable standard application logging */ + // Enable standard application logging SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); - - if (SDL_CreateWindowAndRenderer(WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer) < 0) { + // Create window and renderer + if (SDL_CreateWindowAndRenderer(INITIAL_WINDOW_WIDTH, INITIAL_WINDOW_HEIGHT, SDL_WINDOW_FULLSCREEN_DESKTOP, &window, &renderer) < 0) { quit(2); - } - + } + // Compute text square dimensions + compute_text_square_dimensions(window); // Load the background image - if (LoadSprite("spotify2.bmp", renderer) < 0) { + if (LoadSprite(BACKGROUND_BMP_FILE, renderer) < 0) { quit(2); } - + // Init subsystem with game controller support SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER ); + + /* SDL Init - Audio */ + // Start librespot and open audio buffer + openAudioBuffer(); + // Open audio device and start playback + initAudio(); + + /* SDL Init - TTF */ + if(TTF_Init() == -1) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error in TTF_Init: %s\n", TTF_GetError()); + quit(EXIT_FAILURE); + } + // Load Font file + font = TTF_OpenFont(FONT_FILE, FONT_SIZE); + // Load Spotify log file + openSpotifyLogFile(); - openAudioBuffer(); - initAudio(); /* Main render loop */ done = 0; - #ifdef __EMSCRIPTEN__ emscripten_set_main_loop(loop, 0, 1); #else while (!done) { loop(); } -#endif - +#endif + + + /*SDL Close - TTF */ + closeSpotifyLogFile(); + TTF_CloseFont(font); + TTF_Quit(); + + /* SDL Close - Audio */ stopAudio(); closeAudioBuffer(); - + + /* SDL Close - Graphics */ SDL_QuitSubSystem( SDL_INIT_GAMECONTROLLER ); quit(0);