Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
visualization.c
Go to the documentation of this file.
00001 /*  Audacious - Cross-platform multimedia player
00002  *  Copyright (C) 2005-2010  Audacious development team
00003  *
00004  *  Based on BMP:
00005  *  Copyright (C) 2003-2004  BMP development team
00006  *
00007  *  Based on XMMS:
00008  *  Copyright (C) 1998-2003  XMMS development team
00009  *
00010  *  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; under version 3 of the License.
00013  *
00014  *  This program is distributed in the hope that it will be useful,
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *  GNU General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU General Public License
00020  *  along with this program.  If not, see <http://www.gnu.org/licenses>.
00021  *
00022  *  The Audacious team does not consider modular code linking to
00023  *  Audacious or using our public API to be a derived work.
00024  */
00025 
00026 #include <glib.h>
00027 #include <gtk/gtk.h>
00028 #include <math.h>
00029 #include <string.h>
00030 
00031 #include <libaudcore/hook.h>
00032 
00033 #include "debug.h"
00034 #include "fft.h"
00035 #include "interface.h"
00036 #include "misc.h"
00037 #include "playback.h"
00038 #include "plugin.h"
00039 #include "plugins.h"
00040 #include "ui_preferences.h"
00041 #include "visualization.h"
00042 
00043 typedef struct {
00044     PluginHandle * plugin;
00045     VisPlugin * header;
00046     GtkWidget * widget;
00047     gboolean started;
00048 } LoadedVis;
00049 
00050 static gint running = FALSE;
00051 static GList * loaded_vis_plugins = NULL;
00052 
00053 void calc_stereo_pcm (VisPCMData dest, const VisPCMData src, gint nch)
00054 {
00055     memcpy(dest[0], src[0], 512 * sizeof(gint16));
00056     if (nch == 1)
00057         memcpy(dest[1], src[0], 512 * sizeof(gint16));
00058     else
00059         memcpy(dest[1], src[1], 512 * sizeof(gint16));
00060 }
00061 
00062 void calc_mono_pcm (VisPCMData dest, const VisPCMData src, gint nch)
00063 {
00064     gint i;
00065     gint16 *d;
00066     const gint16 *sl, *sr;
00067 
00068     if (nch == 1)
00069         memcpy(dest[0], src[0], 512 * sizeof(gint16));
00070     else {
00071         d = dest[0];
00072         sl = src[0];
00073         sr = src[1];
00074         for (i = 0; i < 512; i++) {
00075             *(d++) = (*(sl++) + *(sr++)) >> 1;
00076         }
00077     }
00078 }
00079 
00080 static void calc_freq (gint16 * dest, const gint16 * src)
00081 {
00082     static fft_state *state = NULL;
00083     gfloat tmp_out[257];
00084     gint i;
00085 
00086     if (!state)
00087         state = fft_init();
00088 
00089     fft_perform(src, tmp_out, state);
00090 
00091     for (i = 0; i < 256; i++)
00092         dest[i] = ((gint) sqrt(tmp_out[i + 1])) >> 8;
00093 }
00094 
00095 void calc_mono_freq (VisFreqData dest, const VisPCMData src, gint nch)
00096 {
00097     gint i;
00098     gint16 *d, tmp[512];
00099     const gint16 *sl, *sr;
00100 
00101     if (nch == 1)
00102         calc_freq(dest[0], src[0]);
00103     else {
00104         d = tmp;
00105         sl = src[0];
00106         sr = src[1];
00107         for (i = 0; i < 512; i++) {
00108             *(d++) = (*(sl++) + *(sr++)) >> 1;
00109         }
00110         calc_freq(dest[0], tmp);
00111     }
00112 }
00113 
00114 void calc_stereo_freq (VisFreqData dest, const VisPCMData src, gint nch)
00115 {
00116     calc_freq(dest[0], src[0]);
00117 
00118     if (nch == 2)
00119         calc_freq(dest[1], src[1]);
00120     else
00121         memcpy(dest[1], dest[0], 256 * sizeof(gint16));
00122 }
00123 
00124 static void send_audio (const VisNode * vis_node)
00125 {
00126     gint16 mono_freq[2][256], stereo_freq[2][256];
00127     gboolean mono_freq_calced = FALSE, stereo_freq_calced = FALSE;
00128     gint16 mono_pcm[2][512], stereo_pcm[2][512];
00129     gboolean mono_pcm_calced = FALSE, stereo_pcm_calced = FALSE;
00130 
00131     for (GList * node = loaded_vis_plugins; node != NULL; node = node->next)
00132     {
00133         VisPlugin * vp = ((LoadedVis *) node->data)->header;
00134 
00135         if (vp->num_pcm_chs_wanted > 0 && vp->render_pcm) {
00136             if (vp->num_pcm_chs_wanted == 1) {
00137                 if (!mono_pcm_calced) {
00138                     calc_mono_pcm(mono_pcm, vis_node->data, vis_node->nch);
00139                     mono_pcm_calced = TRUE;
00140                 }
00141                 vp->render_pcm(mono_pcm);
00142             }
00143             else {
00144                 if (!stereo_pcm_calced) {
00145                     calc_stereo_pcm(stereo_pcm, vis_node->data, vis_node->nch);
00146                     stereo_pcm_calced = TRUE;
00147                 }
00148                 vp->render_pcm(stereo_pcm);
00149             }
00150         }
00151         if (vp->num_freq_chs_wanted > 0 && vp->render_freq) {
00152             if (vp->num_freq_chs_wanted == 1) {
00153                 if (!mono_freq_calced) {
00154                     calc_mono_freq(mono_freq, vis_node->data, vis_node->nch);
00155                     mono_freq_calced = TRUE;
00156                 }
00157                 vp->render_freq(mono_freq);
00158             }
00159             else {
00160                 if (!stereo_freq_calced) {
00161                     calc_stereo_freq(stereo_freq, vis_node->data, vis_node->nch);
00162                     stereo_freq_calced = TRUE;
00163                 }
00164                 vp->render_freq(stereo_freq);
00165             }
00166         }
00167     }
00168 }
00169 
00170 static void vis_start (LoadedVis * vis)
00171 {
00172     if (vis->started)
00173         return;
00174     AUDDBG ("Starting %s.\n", plugin_get_name (vis->plugin));
00175     if (vis->header->playback_start != NULL)
00176         vis->header->playback_start ();
00177     vis->started = TRUE;
00178 }
00179 
00180 static void vis_start_all (void)
00181 {
00182     g_list_foreach (loaded_vis_plugins, (GFunc) vis_start, NULL);
00183 }
00184 
00185 static void vis_stop (LoadedVis * vis)
00186 {
00187     if (! vis->started)
00188         return;
00189     AUDDBG ("Stopping %s.\n", plugin_get_name (vis->plugin));
00190     if (vis->header->playback_stop != NULL)
00191         vis->header->playback_stop ();
00192     vis->started = FALSE;
00193 }
00194 
00195 static void vis_stop_all (void)
00196 {
00197     g_list_foreach (loaded_vis_plugins, (GFunc) vis_stop, NULL);
00198 }
00199 
00200 static gint vis_find_cb (LoadedVis * vis, PluginHandle * plugin)
00201 {
00202     return (vis->plugin == plugin) ? 0 : -1;
00203 }
00204 
00205 static void vis_load (PluginHandle * plugin)
00206 {
00207     GList * node = g_list_find_custom (loaded_vis_plugins, plugin,
00208      (GCompareFunc) vis_find_cb);
00209     if (node != NULL)
00210         return;
00211 
00212     AUDDBG ("Loading %s.\n", plugin_get_name (plugin));
00213     VisPlugin * header = plugin_get_header (plugin);
00214     g_return_if_fail (header != NULL);
00215 
00216     LoadedVis * vis = g_slice_new (LoadedVis);
00217     vis->plugin = plugin;
00218     vis->header = header;
00219     vis->widget = NULL;
00220     vis->started = FALSE;
00221 
00222     if (header->get_widget != NULL)
00223         vis->widget = header->get_widget ();
00224 
00225     if (vis->widget != NULL)
00226     {
00227         AUDDBG ("Adding %s to interface.\n", plugin_get_name (plugin));
00228         g_signal_connect (vis->widget, "destroy", (GCallback)
00229          gtk_widget_destroyed, & vis->widget);
00230         interface_add_plugin_widget (plugin, vis->widget);
00231     }
00232 
00233     if (playback_get_playing ())
00234         vis_start (vis);
00235 
00236     if (loaded_vis_plugins == NULL)
00237         vis_runner_add_hook ((VisHookFunc) send_audio, NULL);
00238 
00239     loaded_vis_plugins = g_list_prepend (loaded_vis_plugins, vis);
00240 }
00241 
00242 static void vis_unload (PluginHandle * plugin)
00243 {
00244     GList * node = g_list_find_custom (loaded_vis_plugins, plugin,
00245      (GCompareFunc) vis_find_cb);
00246     if (node == NULL)
00247         return;
00248 
00249     AUDDBG ("Unloading %s.\n", plugin_get_name (plugin));
00250     LoadedVis * vis = node->data;
00251     loaded_vis_plugins = g_list_delete_link (loaded_vis_plugins, node);
00252 
00253     if (loaded_vis_plugins == NULL)
00254         vis_runner_remove_hook ((VisHookFunc) send_audio);
00255 
00256     if (vis->widget != NULL)
00257     {
00258         AUDDBG ("Removing %s from interface.\n", plugin_get_name (plugin));
00259         interface_remove_plugin_widget (plugin, vis->widget);
00260         g_return_if_fail (vis->widget == NULL); /* not destroyed? */
00261     }
00262 
00263     g_slice_free (LoadedVis, vis);
00264 }
00265 
00266 static gboolean vis_init_cb (PluginHandle * plugin)
00267 {
00268     vis_load (plugin);
00269     return TRUE;
00270 }
00271 
00272 void vis_init (void)
00273 {
00274     g_return_if_fail (! running);
00275     running = TRUE;
00276 
00277     plugin_for_enabled (PLUGIN_TYPE_VIS, (PluginForEachFunc) vis_init_cb, NULL);
00278 
00279     hook_associate ("playback begin", (HookFunction) vis_start_all, NULL);
00280     hook_associate ("playback stop", (HookFunction) vis_stop_all, NULL);
00281 }
00282 
00283 static void vis_cleanup_cb (LoadedVis * vis)
00284 {
00285     vis_unload (vis->plugin);
00286 }
00287 
00288 void vis_cleanup (void)
00289 {
00290     g_return_if_fail (running);
00291     running = FALSE;
00292 
00293     hook_dissociate ("playback begin", (HookFunction) vis_start_all);
00294     hook_dissociate ("playback stop", (HookFunction) vis_stop_all);
00295 
00296     g_list_foreach (loaded_vis_plugins, (GFunc) vis_cleanup_cb, NULL);
00297 }
00298 
00299 gboolean vis_plugin_start (PluginHandle * plugin)
00300 {
00301     VisPlugin * vp = plugin_get_header (plugin);
00302     g_return_val_if_fail (vp != NULL, FALSE);
00303 
00304     if (vp->init != NULL && ! vp->init ())
00305         return FALSE;
00306 
00307     if (running)
00308         vis_load (plugin);
00309 
00310     return TRUE;
00311 }
00312 
00313 void vis_plugin_stop (PluginHandle * plugin)
00314 {
00315     VisPlugin * vp = plugin_get_header (plugin);
00316     g_return_if_fail (vp != NULL);
00317 
00318     if (running)
00319         vis_unload (plugin);
00320 
00321     if (vp->settings != NULL)
00322         plugin_preferences_cleanup (vp->settings);
00323     if (vp->cleanup != NULL)
00324         vp->cleanup ();
00325 }
00326 
00327 PluginHandle * vis_plugin_by_widget (/* GtkWidget * */ void * widget)
00328 {
00329     g_return_val_if_fail (widget, NULL);
00330 
00331     for (GList * node = loaded_vis_plugins; node; node = node->next)
00332     {
00333         LoadedVis * vis = node->data;
00334         if (vis->widget == widget)
00335             return vis->plugin;
00336     }
00337     
00338     return NULL;
00339 }