/* XMMS2 - X Music Multiplexer System * Copyright (C) 2003-2007 XMMS2 Team * * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!! * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. */ /** @file * Extremly simple example of a visualisation for xmms2 * (should probably just be put in /usr/share/doc/xmms2-dev/examples) */ /** @todo these should be pulled in from an include-file */ #define FFT_BITS 10 #define FFT_LEN (1< #include #include #include #include #include "xmmsclient/xmmsclient-glib.h" static GMainLoop *mainloop; static GTimer *timer = NULL; static guint32 basetime = 0; static GTrashStack *free_buffers=NULL; static GList *queue = NULL; static GList *time_queue = NULL; static TTF_Font *font; static SDL_Surface *text = NULL; static gchar mediainfo[64]; static xmmsc_connection_t *connection; /* * */ static void enqueue (guint32 time, float *buf) { queue = g_list_append (queue, buf); time_queue = g_list_append (time_queue, GINT_TO_POINTER (time)); } static float * dequeue (guint32 time) { float *res; int skipped = 0; if (!queue) { return NULL; } while (time > GPOINTER_TO_INT(time_queue->data)) { if (queue->next) { g_trash_stack_push (&free_buffers, queue->data); queue = g_list_delete_link (queue, queue); time_queue = g_list_delete_link (time_queue, time_queue); } else { break; } skipped++; } if (skipped) printf("Skipped %d\n", skipped); res = queue->data; time_queue = g_list_delete_link (time_queue, time_queue); queue = g_list_delete_link (queue, queue); return res; } static void draw_bar (SDL_Surface *bar, int val, int xpos) { int y,x; int ybase; guint32 *lfb; SDL_LockSurface (bar); ybase = (bar->h-256)/2; lfb = bar->pixels; for (y=256-val; y<256; y++) { guint32 col = ((255-y) << 16) | ( (y) << 8 ); for (x=0; x<30; x++) { if (0 < y+ybase && y+ybase < bar->h) lfb[bar->pitch/4*(y+ybase) + xpos + x] = col; } } SDL_UnlockSurface (bar); } /* called periodically from the mainloop */ static gboolean render_vis (gpointer data) { SDL_Surface *surf = (SDL_Surface *)data; SDL_Event event; int i; gulong apa; float *spec; while (SDL_PollEvent (&event)) { switch (event.type) { case SDL_KEYDOWN: switch (event.key.keysym.sym) { case SDLK_F1: SDL_WM_ToggleFullScreen (surf); break; case 'q': case SDLK_ESCAPE: g_main_loop_quit (mainloop); return FALSE; break; default: ; } } } if (!basetime) { /* wait 'til we have timing info */ return TRUE; } spec = dequeue (basetime + (int)(g_timer_elapsed (timer,&apa)*1000.0+0.5)); if (!spec) { return TRUE; } SDL_FillRect (surf, NULL, 0xff000000); if (text) { SDL_BlitSurface(text, NULL, surf, NULL); } for (i=0; i<16; i++) { float sum = 0.0f; int j; for (j=0; jw-16*32)/2); } SDL_UpdateRect (surf, 0, 0, 0, 0); g_trash_stack_push (&free_buffers, spec); return TRUE; } static int res_has_key (xmmsc_result_t *res, const char *key) { char *val; xmmsc_result_get_dict_entry_string (res, key, &val); return !!val; } static void handle_mediainfo (xmmsc_result_t *res, void *userdata) { guint id; xmmsc_connection_t *c = userdata; if (!xmmsc_result_get_uint (res, &id)) { printf ("no id!\n"); return; } res = xmmsc_medialib_get_info (c, id); xmmsc_result_wait (res); if (res_has_key (res, "channel") && res_has_key (res, "title")) { xmmsc_entry_format (mediainfo, sizeof (mediainfo), "[stream] ${title}", res); } else if (res_has_key (res, "channel")) { xmmsc_entry_format (mediainfo, sizeof (mediainfo), "${channel}", res); } else { xmmsc_entry_format (mediainfo, sizeof (mediainfo), "${artist} - ${title}", res); } xmmsc_result_unref (res); } static void handle_playtime (xmmsc_result_t *res, void *userdata) { guint tme; static guint lasttime = -1; SDL_Color white = { 0xFF, 0xFF, 0xFF, 0 }; xmmsc_result_t *newres; if (xmmsc_result_iserror (res)) { return; } if (!xmmsc_result_get_uint (res, &tme)) { return; } g_timer_start (timer); /* restart timer */ basetime = tme; newres = xmmsc_result_restart (res); xmmsc_result_unref (res); xmmsc_result_unref (newres); if (tme/1000 != lasttime) { gchar buf[64]; if (text) { SDL_FreeSurface (text); } snprintf (buf, 63, "%02d:%02d : %s", tme/60000, (tme/1000)%60, mediainfo); lasttime = tme / 1000; text = TTF_RenderUTF8_Blended (font, buf, white); } } static void handle_vis (xmmsc_result_t *res, void *userdata) { guint32 time; int i; float *spec; xmmsc_result_t *newres; if (xmmsc_result_iserror (res)) { return; } if (free_buffers) { spec = g_trash_stack_pop (&free_buffers); } else { spec = g_malloc(FFT_LEN/2*sizeof(float)); } xmmsc_result_get_uint (res, &time); i = 0; xmmsc_result_list_next (res); for (; xmmsc_result_list_valid (res); xmmsc_result_list_next (res)) { unsigned int val; xmmsc_result_get_uint (res, &val); spec[i++] = val / ((gdouble)(INT_MAX)); } /* @todo measure ipc-delay for real! */ enqueue (time-300, spec); newres = xmmsc_result_restart (res); xmmsc_result_unref (res); xmmsc_result_unref (newres); } static void free_queue_entry (gpointer data, gpointer udata) { g_trash_stack_push (&free_buffers, data); } int main(int argc, char **argv) { SDL_Surface *screen; gchar *path; connection = xmmsc_init ("XMMS2-SDL-VIS"); if(!connection){ printf ("bad\n"); return 1; } path = getenv ("XMMS_PATH"); if (!xmmsc_connect (connection, path)){ printf ("couldn't connect to xmms2d: %s\n", xmmsc_get_last_error(connection)); return 1; } mainloop = g_main_loop_new (NULL, FALSE); timer = g_timer_new (); xmmsc_mainloop_gmain_init (connection); if (SDL_Init(SDL_INIT_VIDEO) > 0) { fprintf(stderr, "Unable to init SDL: %s \n", SDL_GetError()); return 1; } if ( TTF_Init() < 0 ) { fprintf(stderr, "Couldn't initialize TTF: %s\n",SDL_GetError()); return 1; } font = TTF_OpenFont("font.ttf", 24); if(!font){ fprintf(stderr, "couldn't open font.ttf\n"); return 1; } screen = SDL_SetVideoMode(640, 480, 32, 0); //set_mediainfo (connection, xmmsc_get_playing_id (connection)); XMMS_CALLBACK_SET (connection, xmmsc_signal_playback_playtime, handle_playtime, NULL); XMMS_CALLBACK_SET (connection, xmmsc_signal_visualisation_data, handle_vis, NULL); XMMS_CALLBACK_SET (connection, xmmsc_broadcast_playback_current_id, handle_mediainfo, connection); XMMS_CALLBACK_SET (connection, xmmsc_broadcast_medialib_entry_changed, handle_mediainfo, connection); XMMS_CALLBACK_SET (connection, xmmsc_playback_current_id, handle_mediainfo, connection); g_timeout_add (20, render_vis, (gpointer)screen); g_main_loop_run (mainloop); /* GO GO GO! */ if (connection) { xmmsc_unref (connection); } g_list_foreach (queue, free_queue_entry, NULL); g_list_free (queue); g_list_free (time_queue); while (free_buffers) { g_free (g_trash_stack_pop (&free_buffers)); } /* TTF_Quit ();*/ SDL_Quit (); return 0; }