2313
|
1 /* Audacious - Cross-platform multimedia player
|
|
2 * Copyright (C) 2005-2007 Audacious development team
|
|
3 *
|
|
4 * Based on BMP:
|
|
5 * Copyright (C) 2003-2004 BMP development team
|
|
6 *
|
|
7 * Based on XMMS:
|
|
8 * Copyright (C) 1998-2003 XMMS development team
|
|
9 *
|
|
10 * This program is free software; you can redistribute it and/or modify
|
|
11 * it under the terms of the GNU General Public License as published by
|
|
12 * the Free Software Foundation; under version 2 of the License.
|
|
13 *
|
|
14 * This program is distributed in the hope that it will be useful,
|
|
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17 * GNU General Public License for more details.
|
|
18 *
|
|
19 * You should have received a copy of the GNU General Public License
|
|
20 * along with this program; if not, write to the Free Software
|
|
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
22 */
|
|
23
|
|
24 #include "visualization.h"
|
|
25
|
|
26 #include <glib.h>
|
|
27 #include <stdlib.h>
|
|
28 #include <math.h>
|
|
29 #include <string.h>
|
|
30
|
|
31 #include "fft.h"
|
|
32 #include "input.h"
|
|
33 #include "main.h"
|
|
34 #include "playback.h"
|
|
35 #include "plugin.h"
|
|
36 #include "ui_preferences.h"
|
|
37 #include "widgets/widgetcore.h"
|
|
38
|
|
39 VisPluginData vp_data = {
|
|
40 NULL,
|
|
41 NULL,
|
|
42 FALSE
|
|
43 };
|
|
44
|
|
45 GList *
|
|
46 get_vis_list(void)
|
|
47 {
|
|
48 return vp_data.vis_list;
|
|
49 }
|
|
50
|
|
51 GList *
|
|
52 get_vis_enabled_list(void)
|
|
53 {
|
|
54 return vp_data.enabled_list;
|
|
55 }
|
|
56
|
|
57 void
|
|
58 vis_disable_plugin(VisPlugin * vp)
|
|
59 {
|
|
60 gint i = g_list_index(vp_data.vis_list, vp);
|
|
61 enable_vis_plugin(i, FALSE);
|
|
62 }
|
|
63
|
|
64 void
|
|
65 vis_about(gint i)
|
|
66 {
|
|
67 GList *node = g_list_nth(vp_data.vis_list, i);
|
|
68
|
|
69 if (node && node->data && VIS_PLUGIN(node->data)->about)
|
|
70 VIS_PLUGIN(node->data)->about();
|
|
71 }
|
|
72
|
|
73 void
|
|
74 vis_configure(gint i)
|
|
75 {
|
|
76 GList *node = g_list_nth(vp_data.vis_list, i);
|
|
77
|
|
78 if (node && node->data && VIS_PLUGIN(node->data)->configure)
|
|
79 VIS_PLUGIN(node->data)->configure();
|
|
80 }
|
|
81
|
|
82 void
|
|
83 vis_playback_start(void)
|
|
84 {
|
|
85 GList *node;
|
|
86 VisPlugin *vp;
|
|
87
|
|
88 if (vp_data.playback_started)
|
|
89 return;
|
|
90
|
|
91 for (node = vp_data.enabled_list; node; node = g_list_next(node)) {
|
|
92 vp = node->data;
|
|
93 if (vp->playback_start)
|
|
94 vp->playback_start();
|
|
95 }
|
|
96 vp_data.playback_started = TRUE;
|
|
97 }
|
|
98
|
|
99 void
|
|
100 vis_playback_stop(void)
|
|
101 {
|
|
102 GList *node = vp_data.enabled_list;
|
|
103 VisPlugin *vp;
|
|
104
|
|
105 if (!vp_data.playback_started)
|
|
106 return;
|
|
107
|
|
108 for (node = vp_data.enabled_list; node; node = g_list_next(node)) {
|
|
109 vp = node->data;
|
|
110 if (vp->playback_stop)
|
|
111 vp->playback_stop();
|
|
112 }
|
|
113 vp_data.playback_started = FALSE;
|
|
114 }
|
|
115
|
|
116 void
|
|
117 enable_vis_plugin(gint i, gboolean enable)
|
|
118 {
|
|
119 GList *node = g_list_nth(vp_data.vis_list, i);
|
|
120 VisPlugin *vp;
|
|
121
|
|
122 if (!node || !(node->data))
|
|
123 return;
|
|
124 vp = node->data;
|
|
125
|
|
126 if (enable && !g_list_find(vp_data.enabled_list, vp)) {
|
|
127 vp_data.enabled_list = g_list_append(vp_data.enabled_list, vp);
|
|
128 if (vp->init)
|
|
129 vp->init();
|
|
130 if (playback_get_playing() && vp->playback_start)
|
|
131 vp->playback_start();
|
|
132 }
|
|
133 else if (!enable && g_list_find(vp_data.enabled_list, vp)) {
|
|
134 vp_data.enabled_list = g_list_remove(vp_data.enabled_list, vp);
|
|
135 if (playback_get_playing() && vp->playback_stop)
|
|
136 vp->playback_stop();
|
|
137 if (vp->cleanup)
|
|
138 vp->cleanup();
|
|
139 }
|
|
140 }
|
|
141
|
|
142 gboolean
|
|
143 vis_enabled(gint i)
|
|
144 {
|
|
145 return (g_list_find
|
|
146 (vp_data.enabled_list,
|
|
147 g_list_nth(vp_data.vis_list, i)->data) != NULL);
|
|
148 }
|
|
149
|
|
150 gchar *
|
|
151 vis_stringify_enabled_list(void)
|
|
152 {
|
|
153 gchar *enalist = NULL, *tmp, *tmp2;
|
|
154 GList *node = vp_data.enabled_list;
|
|
155
|
|
156 if (g_list_length(node)) {
|
|
157 enalist = g_path_get_basename(VIS_PLUGIN(node->data)->filename);
|
|
158 for (node = g_list_next(node); node != NULL; node = g_list_next(node)) {
|
|
159 tmp = enalist;
|
|
160 tmp2 = g_path_get_basename(VIS_PLUGIN(node->data)->filename);
|
|
161 enalist = g_strconcat(tmp, ",", tmp2, NULL);
|
|
162 g_free(tmp);
|
|
163 g_free(tmp2);
|
|
164 }
|
|
165 }
|
|
166 return enalist;
|
|
167 }
|
|
168
|
|
169 void
|
|
170 vis_enable_from_stringified_list(gchar * list)
|
|
171 {
|
|
172 gchar **plugins, *base;
|
|
173 GList *node;
|
|
174 gint i;
|
|
175 VisPlugin *vp;
|
|
176
|
|
177 if (!list || !strcmp(list, ""))
|
|
178 return;
|
|
179 plugins = g_strsplit(list, ",", 0);
|
|
180 for (i = 0; plugins[i]; i++) {
|
|
181 for (node = vp_data.vis_list; node != NULL; node = g_list_next(node)) {
|
|
182 base = g_path_get_basename(VIS_PLUGIN(node->data)->filename);
|
|
183 if (!strcmp(plugins[i], base)) {
|
|
184 vp = node->data;
|
|
185 vp_data.enabled_list =
|
|
186 g_list_append(vp_data.enabled_list, vp);
|
|
187 if (vp->init)
|
|
188 vp->init();
|
|
189 if (playback_get_playing() && vp->playback_start)
|
|
190 vp->playback_start();
|
|
191 }
|
|
192 g_free(base);
|
|
193 }
|
|
194 }
|
|
195 g_strfreev(plugins);
|
|
196 }
|
|
197
|
|
198 static void
|
|
199 calc_stereo_pcm(gint16 dest[2][512], gint16 src[2][512], gint nch)
|
|
200 {
|
|
201 memcpy(dest[0], src[0], 512 * sizeof(gint16));
|
|
202 if (nch == 1)
|
|
203 memcpy(dest[1], src[0], 512 * sizeof(gint16));
|
|
204 else
|
|
205 memcpy(dest[1], src[1], 512 * sizeof(gint16));
|
|
206 }
|
|
207
|
|
208 static void
|
|
209 calc_mono_pcm(gint16 dest[2][512], gint16 src[2][512], gint nch)
|
|
210 {
|
|
211 gint i;
|
|
212 gint16 *d, *sl, *sr;
|
|
213
|
|
214 if (nch == 1)
|
|
215 memcpy(dest[0], src[0], 512 * sizeof(gint16));
|
|
216 else {
|
|
217 d = dest[0];
|
|
218 sl = src[0];
|
|
219 sr = src[1];
|
|
220 for (i = 0; i < 512; i++) {
|
|
221 *(d++) = (*(sl++) + *(sr++)) >> 1;
|
|
222 }
|
|
223 }
|
|
224 }
|
|
225
|
|
226 static void
|
|
227 calc_freq(gint16 * dest, gint16 * src)
|
|
228 {
|
|
229 static fft_state *state = NULL;
|
|
230 gfloat tmp_out[257];
|
|
231 gint i;
|
|
232
|
|
233 if (!state)
|
|
234 state = fft_init();
|
|
235
|
|
236 fft_perform(src, tmp_out, state);
|
|
237
|
|
238 for (i = 0; i < 256; i++)
|
|
239 dest[i] = ((gint) sqrt(tmp_out[i + 1])) >> 8;
|
|
240 }
|
|
241
|
|
242 static void
|
|
243 calc_mono_freq(gint16 dest[2][256], gint16 src[2][512], gint nch)
|
|
244 {
|
|
245 gint i;
|
|
246 gint16 *d, *sl, *sr, tmp[512];
|
|
247
|
|
248 if (nch == 1)
|
|
249 calc_freq(dest[0], src[0]);
|
|
250 else {
|
|
251 d = tmp;
|
|
252 sl = src[0];
|
|
253 sr = src[1];
|
|
254 for (i = 0; i < 512; i++) {
|
|
255 *(d++) = (*(sl++) + *(sr++)) >> 1;
|
|
256 }
|
|
257 calc_freq(dest[0], tmp);
|
|
258 }
|
|
259 }
|
|
260
|
|
261 static void
|
|
262 calc_stereo_freq(gint16 dest[2][256], gint16 src[2][512], gint nch)
|
|
263 {
|
|
264 calc_freq(dest[0], src[0]);
|
|
265
|
|
266 if (nch == 2)
|
|
267 calc_freq(dest[1], src[1]);
|
|
268 else
|
|
269 memcpy(dest[1], dest[0], 256 * sizeof(gint16));
|
|
270 }
|
|
271
|
|
272 void
|
|
273 vis_send_data(gint16 pcm_data[2][512], gint nch, gint length)
|
|
274 {
|
|
275 GList *node = vp_data.enabled_list;
|
|
276 VisPlugin *vp;
|
|
277 gint16 mono_freq[2][256], stereo_freq[2][256];
|
|
278 gboolean mono_freq_calced = FALSE, stereo_freq_calced = FALSE;
|
|
279 gint16 mono_pcm[2][512], stereo_pcm[2][512];
|
|
280 gboolean mono_pcm_calced = FALSE, stereo_pcm_calced = FALSE;
|
|
281 guint8 intern_vis_data[512];
|
|
282 gint i;
|
|
283
|
|
284 if (!pcm_data || nch < 1) {
|
|
285 if (cfg.vis_type != VIS_OFF) {
|
|
286 if (cfg.player_shaded && cfg.player_visible)
|
|
287 svis_timeout_func(mainwin_svis, NULL);
|
|
288 else
|
2947
|
289 vis_timeout_func(mainwin_vis, NULL);
|
2313
|
290 }
|
|
291 return;
|
|
292 }
|
|
293
|
|
294 while (node) {
|
|
295 vp = node->data;
|
|
296 if (vp->num_pcm_chs_wanted > 0 && vp->render_pcm) {
|
|
297 if (vp->num_pcm_chs_wanted == 1) {
|
|
298 if (!mono_pcm_calced) {
|
|
299 calc_mono_pcm(mono_pcm, pcm_data, nch);
|
|
300 mono_pcm_calced = TRUE;
|
|
301 }
|
|
302 vp->render_pcm(mono_pcm);
|
|
303 }
|
|
304 else {
|
|
305 if (!stereo_pcm_calced) {
|
|
306 calc_stereo_pcm(stereo_pcm, pcm_data, nch);
|
|
307 stereo_pcm_calced = TRUE;
|
|
308 }
|
|
309 vp->render_pcm(stereo_pcm);
|
|
310 }
|
|
311 }
|
|
312 if (vp->num_freq_chs_wanted > 0 && vp->render_freq) {
|
|
313 if (vp->num_freq_chs_wanted == 1) {
|
|
314 if (!mono_freq_calced) {
|
|
315 calc_mono_freq(mono_freq, pcm_data, nch);
|
|
316 mono_freq_calced = TRUE;
|
|
317 }
|
|
318 vp->render_freq(mono_freq);
|
|
319 }
|
|
320 else {
|
|
321 if (!stereo_freq_calced) {
|
|
322 calc_stereo_freq(stereo_freq, pcm_data, nch);
|
|
323 stereo_freq_calced = TRUE;
|
|
324 }
|
|
325 vp->render_freq(stereo_freq);
|
|
326 }
|
|
327 }
|
|
328 node = g_list_next(node);
|
|
329 }
|
|
330
|
|
331 if (cfg.vis_type == VIS_OFF)
|
|
332 return;
|
|
333
|
|
334 if (cfg.vis_type == VIS_ANALYZER) {
|
|
335 /* Spectrum analyzer */
|
|
336 /* 76 values */
|
|
337 const gint long_xscale[] =
|
|
338 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
|
339 17, 18,
|
|
340 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
|
|
341 34,
|
|
342 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
|
|
343 50, 51,
|
|
344 52, 53, 54, 55, 56, 57, 58, 61, 66, 71, 76, 81, 87, 93,
|
|
345 100, 107,
|
|
346 114, 122, 131, 140, 150, 161, 172, 184, 255
|
|
347 };
|
|
348 /* 20 values */
|
|
349 const int short_xscale[] =
|
|
350 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 15, 20, 27,
|
|
351 36, 47, 62, 82, 107, 141, 184, 255
|
|
352 };
|
|
353 const double y_scale = 3.60673760222; /* 20.0 / log(256) */
|
|
354 const int *xscale;
|
|
355 gint j, y, max;
|
|
356
|
|
357 if (!mono_freq_calced)
|
|
358 calc_mono_freq(mono_freq, pcm_data, nch);
|
|
359
|
|
360 memset(intern_vis_data, 0, 75);
|
|
361
|
|
362 if (cfg.analyzer_type == ANALYZER_BARS) {
|
|
363 if (cfg.player_shaded) {
|
|
364 max = 13;
|
|
365 }
|
|
366 else {
|
|
367 max = 19;
|
|
368 }
|
|
369 xscale = short_xscale;
|
|
370 }
|
|
371 else {
|
|
372 if (cfg.player_shaded) {
|
|
373 max = 37;
|
|
374 }
|
|
375 else {
|
|
376 max = 75;
|
|
377 }
|
|
378 xscale = long_xscale;
|
|
379 }
|
|
380
|
|
381 for (i = 0; i < max; i++) {
|
|
382 for (j = xscale[i], y = 0; j < xscale[i + 1]; j++) {
|
|
383 if (mono_freq[0][j] > y)
|
|
384 y = mono_freq[0][j];
|
|
385 }
|
|
386 y >>= 7;
|
|
387 if (y != 0) {
|
|
388 intern_vis_data[i] = log(y) * y_scale;
|
|
389 if (intern_vis_data[i] > 15)
|
|
390 intern_vis_data[i] = 15;
|
|
391 }
|
|
392 else
|
|
393 intern_vis_data[i] = 0;
|
|
394 }
|
|
395
|
|
396 }
|
|
397 else if(cfg.vis_type == VIS_VOICEPRINT){
|
|
398 if (cfg.player_shaded && cfg.player_visible) {
|
|
399 /* VU */
|
|
400 gint vu, val;
|
|
401
|
|
402 if (!stereo_pcm_calced)
|
|
403 calc_stereo_pcm(stereo_pcm, pcm_data, nch);
|
|
404 vu = 0;
|
|
405 for (i = 0; i < 512; i++) {
|
|
406 val = abs(stereo_pcm[0][i]);
|
|
407 if (val > vu)
|
|
408 vu = val;
|
|
409 }
|
|
410 intern_vis_data[0] = (vu * 37) >> 15;
|
|
411 if (intern_vis_data[0] > 37)
|
|
412 intern_vis_data[0] = 37;
|
|
413 if (nch == 2) {
|
|
414 vu = 0;
|
|
415 for (i = 0; i < 512; i++) {
|
|
416 val = abs(stereo_pcm[1][i]);
|
|
417 if (val > vu)
|
|
418 vu = val;
|
|
419 }
|
|
420 intern_vis_data[1] = (vu * 37) >> 15;
|
|
421 if (intern_vis_data[1] > 37)
|
|
422 intern_vis_data[1] = 37;
|
|
423 }
|
|
424 else
|
|
425 intern_vis_data[1] = intern_vis_data[0];
|
|
426 }
|
|
427 else{ /*Voiceprint*/
|
|
428 if (!mono_freq_calced)
|
|
429 calc_mono_freq(mono_freq, pcm_data, nch);
|
|
430 memset(intern_vis_data, 0, 256);
|
|
431 /* For the values [0-16] we use the frequency that's 3/2 as much.
|
|
432 If we assume the 512 values calculated by calc_mono_freq to cover 0-22kHz linearly
|
|
433 we get a range of [0-16] * 3/2 * 22000/512 = [0-1,031] Hz.
|
|
434 Most stuff above that is harmonics and we want to utilize the 16 samples we have
|
|
435 to the max[tm]
|
|
436 */
|
|
437 for(i = 0; i < 50 ; i+=3){
|
|
438 intern_vis_data[i/3] += (mono_freq[0][i/2] >> 5);
|
|
439
|
|
440 /*Boost frequencies above 257Hz a little*/
|
|
441 //if(i > 4 * 3)
|
|
442 // intern_vis_data[i/3] += 8;
|
|
443 }
|
|
444 }
|
|
445 }
|
|
446 else { /* (cfg.vis_type == VIS_SCOPE) */
|
|
447
|
|
448 /* Oscilloscope */
|
|
449 gint pos, step;
|
|
450
|
|
451 if (!mono_pcm_calced)
|
|
452 calc_mono_pcm(mono_pcm, pcm_data, nch);
|
|
453
|
|
454 step = (length << 8) / 74;
|
|
455 for (i = 0, pos = 0; i < 75; i++, pos += step) {
|
|
456 intern_vis_data[i] = ((mono_pcm[0][pos >> 8]) >> 12) + 7;
|
|
457 if (intern_vis_data[i] == 255)
|
|
458 intern_vis_data[i] = 0;
|
|
459 else if (intern_vis_data[i] > 12)
|
|
460 intern_vis_data[i] = 12;
|
|
461 /* Do not see the point of that? (comparison always false) -larne.
|
|
462 if (intern_vis_data[i] < 0)
|
|
463 intern_vis_data[i] = 0; */
|
|
464 }
|
|
465 }
|
|
466 if (cfg.player_shaded && cfg.player_visible)
|
|
467 svis_timeout_func(mainwin_svis, intern_vis_data);
|
|
468 else
|
2947
|
469 vis_timeout_func(mainwin_vis, intern_vis_data);
|
2313
|
470 }
|