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 "visualization.h"
00041 
00042 typedef struct {
00043     PluginHandle * plugin;
00044     VisPlugin * header;
00045     GtkWidget * widget;
00046     gboolean started;
00047 } LoadedVis;
00048 
00049 static GList * loaded_vis_plugins = NULL;
00050 
00051 void calc_stereo_pcm (VisPCMData dest, const VisPCMData src, gint nch)
00052 {
00053     memcpy(dest[0], src[0], 512 * sizeof(gint16));
00054     if (nch == 1)
00055         memcpy(dest[1], src[0], 512 * sizeof(gint16));
00056     else
00057         memcpy(dest[1], src[1], 512 * sizeof(gint16));
00058 }
00059 
00060 void calc_mono_pcm (VisPCMData dest, const VisPCMData src, gint nch)
00061 {
00062     gint i;
00063     gint16 *d;
00064     const gint16 *sl, *sr;
00065 
00066     if (nch == 1)
00067         memcpy(dest[0], src[0], 512 * sizeof(gint16));
00068     else {
00069         d = dest[0];
00070         sl = src[0];
00071         sr = src[1];
00072         for (i = 0; i < 512; i++) {
00073             *(d++) = (*(sl++) + *(sr++)) >> 1;
00074         }
00075     }
00076 }
00077 
00078 static void calc_freq (gint16 * dest, const gint16 * src)
00079 {
00080     static fft_state *state = NULL;
00081     gfloat tmp_out[257];
00082     gint i;
00083 
00084     if (!state)
00085         state = fft_init();
00086 
00087     fft_perform(src, tmp_out, state);
00088 
00089     for (i = 0; i < 256; i++)
00090         dest[i] = ((gint) sqrt(tmp_out[i + 1])) >> 8;
00091 }
00092 
00093 void calc_mono_freq (VisFreqData dest, const VisPCMData src, gint nch)
00094 {
00095     gint i;
00096     gint16 *d, tmp[512];
00097     const gint16 *sl, *sr;
00098 
00099     if (nch == 1)
00100         calc_freq(dest[0], src[0]);
00101     else {
00102         d = tmp;
00103         sl = src[0];
00104         sr = src[1];
00105         for (i = 0; i < 512; i++) {
00106             *(d++) = (*(sl++) + *(sr++)) >> 1;
00107         }
00108         calc_freq(dest[0], tmp);
00109     }
00110 }
00111 
00112 void calc_stereo_freq (VisFreqData dest, const VisPCMData src, gint nch)
00113 {
00114     calc_freq(dest[0], src[0]);
00115 
00116     if (nch == 2)
00117         calc_freq(dest[1], src[1]);
00118     else
00119         memcpy(dest[1], dest[0], 256 * sizeof(gint16));
00120 }
00121 
00122 static void send_audio (const VisNode * vis_node)
00123 {
00124     gint16 mono_freq[2][256], stereo_freq[2][256];
00125     gboolean mono_freq_calced = FALSE, stereo_freq_calced = FALSE;
00126     gint16 mono_pcm[2][512], stereo_pcm[2][512];
00127     gboolean mono_pcm_calced = FALSE, stereo_pcm_calced = FALSE;
00128 
00129     for (GList * node = loaded_vis_plugins; node != NULL; node = node->next)
00130     {
00131         VisPlugin * vp = ((LoadedVis *) node->data)->header;
00132 
00133         if (vp->num_pcm_chs_wanted > 0 && vp->render_pcm) {
00134             if (vp->num_pcm_chs_wanted == 1) {
00135                 if (!mono_pcm_calced) {
00136                     calc_mono_pcm(mono_pcm, vis_node->data, vis_node->nch);
00137                     mono_pcm_calced = TRUE;
00138                 }
00139                 vp->render_pcm(mono_pcm);
00140             }
00141             else {
00142                 if (!stereo_pcm_calced) {
00143                     calc_stereo_pcm(stereo_pcm, vis_node->data, vis_node->nch);
00144                     stereo_pcm_calced = TRUE;
00145                 }
00146                 vp->render_pcm(stereo_pcm);
00147             }
00148         }
00149         if (vp->num_freq_chs_wanted > 0 && vp->render_freq) {
00150             if (vp->num_freq_chs_wanted == 1) {
00151                 if (!mono_freq_calced) {
00152                     calc_mono_freq(mono_freq, vis_node->data, vis_node->nch);
00153                     mono_freq_calced = TRUE;
00154                 }
00155                 vp->render_freq(mono_freq);
00156             }
00157             else {
00158                 if (!stereo_freq_calced) {
00159                     calc_stereo_freq(stereo_freq, vis_node->data, vis_node->nch);
00160                     stereo_freq_calced = TRUE;
00161                 }
00162                 vp->render_freq(stereo_freq);
00163             }
00164         }
00165     }
00166 }
00167 
00168 static void vis_start (LoadedVis * vis)
00169 {
00170     if (vis->started)
00171         return;
00172     AUDDBG ("Starting %s.\n", plugin_get_name (vis->plugin));
00173     if (vis->header->playback_start != NULL)
00174         vis->header->playback_start ();
00175     vis->started = TRUE;
00176 }
00177 
00178 static void vis_start_all (void)
00179 {
00180     g_list_foreach (loaded_vis_plugins, (GFunc) vis_start, NULL);
00181 }
00182 
00183 static void vis_stop (LoadedVis * vis)
00184 {
00185     if (! vis->started)
00186         return;
00187     AUDDBG ("Stopping %s.\n", plugin_get_name (vis->plugin));
00188     if (vis->header->playback_stop != NULL)
00189         vis->header->playback_stop ();
00190     vis->started = FALSE;
00191 }
00192 
00193 static void vis_stop_all (void)
00194 {
00195     g_list_foreach (loaded_vis_plugins, (GFunc) vis_stop, NULL);
00196 }
00197 
00198 static gint vis_find_cb (LoadedVis * vis, PluginHandle * plugin)
00199 {
00200     return (vis->plugin == plugin) ? 0 : -1;
00201 }
00202 
00203 static void vis_load (PluginHandle * plugin)
00204 {
00205     GList * node = g_list_find_custom (loaded_vis_plugins, plugin,
00206      (GCompareFunc) vis_find_cb);
00207     if (node != NULL)
00208         return;
00209 
00210     AUDDBG ("Loading %s.\n", plugin_get_name (plugin));
00211     VisPlugin * header = plugin_get_header (plugin);
00212     g_return_if_fail (header != NULL);
00213 
00214     if (header->init != NULL)
00215         header->init ();
00216 
00217     LoadedVis * vis = g_slice_new (LoadedVis);
00218     vis->plugin = plugin;
00219     vis->header = header;
00220     vis->widget = NULL;
00221     vis->started = FALSE;
00222 
00223     if (header->get_widget != NULL)
00224         vis->widget = header->get_widget ();
00225 
00226     if (vis->widget != NULL)
00227     {
00228         AUDDBG ("Adding %s to interface.\n", plugin_get_name (plugin));
00229         g_signal_connect (vis->widget, "destroy", (GCallback)
00230          gtk_widget_destroyed, & vis->widget);
00231         interface_add_plugin_widget (plugin, vis->widget);
00232     }
00233 
00234     if (playback_get_playing ())
00235         vis_start (vis);
00236 
00237     if (loaded_vis_plugins == NULL)
00238         vis_runner_add_hook ((VisHookFunc) send_audio, NULL);
00239 
00240     loaded_vis_plugins = g_list_prepend (loaded_vis_plugins, vis);
00241 }
00242 
00243 static void vis_unload (PluginHandle * plugin)
00244 {
00245     GList * node = g_list_find_custom (loaded_vis_plugins, plugin,
00246      (GCompareFunc) vis_find_cb);
00247     if (node == NULL)
00248         return;
00249 
00250     AUDDBG ("Unloading %s.\n", plugin_get_name (plugin));
00251     LoadedVis * vis = node->data;
00252     loaded_vis_plugins = g_list_delete_link (loaded_vis_plugins, node);
00253 
00254     if (loaded_vis_plugins == NULL)
00255         vis_runner_remove_hook ((VisHookFunc) send_audio);
00256 
00257     if (vis->widget != NULL)
00258     {
00259         AUDDBG ("Removing %s from interface.\n", plugin_get_name (plugin));
00260         interface_remove_plugin_widget (plugin, vis->widget);
00261         g_return_if_fail (vis->widget == NULL); /* not destroyed? */
00262     }
00263 
00264     if (vis->header->cleanup != NULL)
00265         vis->header->cleanup ();
00266 
00267     g_slice_free (LoadedVis, vis);
00268 }
00269 
00270 static gboolean vis_init_cb (PluginHandle * plugin)
00271 {
00272     vis_load (plugin);
00273     return TRUE;
00274 }
00275 
00276 void vis_init (void)
00277 {
00278     plugin_for_enabled (PLUGIN_TYPE_VIS, (PluginForEachFunc) vis_init_cb, NULL);
00279 
00280     hook_associate ("playback begin", (HookFunction) vis_start_all, NULL);
00281     hook_associate ("playback stop", (HookFunction) vis_stop_all, NULL);
00282 }
00283 
00284 static void vis_cleanup_cb (LoadedVis * vis)
00285 {
00286     vis_unload (vis->plugin);
00287 }
00288 
00289 void vis_cleanup (void)
00290 {
00291     hook_dissociate ("playback begin", (HookFunction) vis_start_all);
00292     hook_dissociate ("playback stop", (HookFunction) vis_stop_all);
00293 
00294     g_list_foreach (loaded_vis_plugins, (GFunc) vis_cleanup_cb, NULL);
00295 }
00296 
00297 void vis_plugin_enable (PluginHandle * plugin, gboolean enable)
00298 {
00299     plugin_set_enabled (plugin, enable);
00300 
00301     if (enable)
00302         vis_load (plugin);
00303     else
00304         vis_unload (plugin);
00305 }