1
/* opengl_spectrum.c (C) 2002 by Andy Lo A Foe <andy@alsaplayer.org>
3
* Based on code found in xmms:
4
* Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
6
* This file is part of AlsaPlayer.
8
* AlsaPlayer is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 3 of the License, or
11
* (at your option) any later version.
13
* AlsaPlayer is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with this program; if not, see <http://www.gnu.org/licenses/>.
24
#include <X11/keysym.h>
33
#include "scope_plugin.h"
34
#include "alsaplayer_error.h"
35
#include "utilities.h"
50
static Display *dpy = NULL;
51
static Colormap colormap = 0;
52
static GLXContext glxcontext = NULL;
53
static Window window = 0;
54
static GLfloat y_angle = 45.0, y_speed = 0.5;
55
static GLfloat x_angle = 20.0, x_speed = 0.0;
56
static GLfloat z_angle = 0.0, z_speed = 0.0;
57
static GLfloat heights[16][16], scale;
58
static int going = FALSE, grabbed_pointer = FALSE;
59
static Atom wm_delete_window_atom;
60
static pthread_t draw_thread;
61
static pthread_mutex_t scope_mutex;
68
#include <sys/types.h>
73
static void stop_display(int);
74
static void oglspectrum_start(void);
76
static void wait_for_vsync()
81
static struct pollfd pollfds;
83
fd = open("/dev/nvidia0", O_RDONLY);
85
alsaplayer_error("Error opening NVIDIA device /dev/nvidia0");
88
pollfds.events = 0xffff;
89
pollfds.revents = 0xffff;
90
alsaplayer_error("Using NVIDIA poll method for vsync");
94
poll (&pollfds, 1, -1);
101
static Window create_window(int width, int height)
110
XSetWindowAttributes attr;
113
XVisualInfo *visinfo;
114
Atom wm_protocols[1];
116
if ((dpy = XOpenDisplay(NULL)) == NULL)
119
scrnum = DefaultScreen(dpy);
120
root = RootWindow(dpy, scrnum);
122
if ((visinfo = glXChooseVisual(dpy, scrnum, attr_list)) == NULL)
125
attr.background_pixel = 0;
126
attr.border_pixel = 0;
127
attr.colormap = colormap = XCreateColormap(dpy, root,
128
visinfo->visual, AllocNone);
129
attr.event_mask = StructureNotifyMask | KeyPressMask;
130
mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
132
win = XCreateWindow(dpy, root, 0, 0, width, height,
133
0, visinfo->depth, InputOutput,
134
visinfo->visual, mask, &attr);
135
XmbSetWMProperties(dpy, win, "OpenGL Spectrum analyzer",
136
"OpenGL Spectrum analyzer", NULL, 0, NULL, NULL,
138
wm_delete_window_atom = wm_protocols[0] =
139
XInternAtom(dpy, "WM_DELETE_WINDOW", False);
140
XSetWMProtocols(dpy, win, wm_protocols, 1);
142
glxcontext = glXCreateContext(dpy, visinfo, NULL, True);
146
glXMakeCurrent(dpy, win, glxcontext);
152
static void draw_rectangle(GLfloat x1, GLfloat y1, GLfloat z1, GLfloat x2, GLfloat y2, GLfloat z2)
157
glVertex3f(x1, y1, z1);
158
glVertex3f(x2, y1, z1);
159
glVertex3f(x2, y2, z2);
161
glVertex3f(x2, y2, z2);
162
glVertex3f(x1, y2, z2);
163
glVertex3f(x1, y1, z1);
167
glVertex3f(x1, y1, z1);
168
glVertex3f(x2, y1, z2);
169
glVertex3f(x2, y2, z2);
171
glVertex3f(x2, y2, z2);
172
glVertex3f(x1, y2, z1);
173
glVertex3f(x1, y1, z1);
177
static void draw_bar(GLfloat x_offset, GLfloat z_offset, GLfloat height, GLfloat red, GLfloat green, GLfloat blue )
181
glColor3f(red,green,blue);
182
draw_rectangle(x_offset, height, z_offset, x_offset + width, height, z_offset + 0.1);
183
draw_rectangle(x_offset, 0, z_offset, x_offset + width, 0, z_offset + 0.1);
185
glColor3f(0.5 * red, 0.5 * green, 0.5 * blue);
186
draw_rectangle(x_offset, 0.0, z_offset + 0.1, x_offset + width, height, z_offset + 0.1);
187
draw_rectangle(x_offset, 0.0, z_offset, x_offset + width, height, z_offset );
189
glColor3f(0.25 * red, 0.25 * green, 0.25 * blue);
190
draw_rectangle(x_offset, 0.0, z_offset , x_offset, height, z_offset + 0.1);
191
draw_rectangle(x_offset + width, 0.0, z_offset , x_offset + width, height, z_offset + 0.1);
196
static void draw_bars(void)
199
GLfloat x_offset, z_offset, r_base, b_base;
203
glClearColor(0,0,0,0);
204
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
207
glTranslatef(0.0,-0.5,-5.0);
208
glRotatef(x_angle,1.0,0.0,0.0);
209
glRotatef(y_angle,0.0,1.0,0.0);
210
glRotatef(z_angle,0.0,0.0,1.0);
212
glBegin(GL_TRIANGLES);
213
for(y = 0; y < 16; y++)
215
z_offset = -1.6 + ((15 - y) * 0.2);
217
b_base = y * (1.0 / 15);
218
r_base = 1.0 - b_base;
220
for(x = 0; x < 16; x++)
222
x_offset = -1.6 + (x * 0.2);
224
draw_bar(x_offset, z_offset, heights[y][x], r_base - (x * (r_base / 15.0)), x * (1.0 / 15), b_base);
231
glXSwapBuffers(dpy,window);
234
#define DEFAULT_W 640
235
#define DEFAULT_H 480
237
void *draw_thread_func(void *arg)
239
Bool configured = FALSE;
241
window_w = prefs_get_int(ap_prefs, "opengl_spectrum", "width", DEFAULT_W);
242
window_h = prefs_get_int(ap_prefs, "opengl_spectrum", "height", DEFAULT_H);
244
if ((window = create_window(window_w, window_h)) == 0)
246
alsaplayer_error("unable to create window");
250
XMapWindow(dpy, window);
252
glMatrixMode(GL_PROJECTION);
254
glFrustum(-1, 1, -1, 1, 1.5, 10);
255
glMatrixMode(GL_MODELVIEW);
257
glEnable(GL_DEPTH_TEST);
258
glDepthFunc(GL_LESS);
268
XNextEvent(dpy, &event);
271
case ConfigureNotify:
272
glViewport(0,0,event.xconfigure.width, event.xconfigure.height);
273
window_w = event.xconfigure.width;
274
window_h = event.xconfigure.height;
275
prefs_set_int(ap_prefs, "opengl_spectrum", "width", window_w);
276
prefs_set_int(ap_prefs, "opengl_spectrum", "height", window_h);
282
XLookupString (&event.xkey, buf, 16, &keysym, NULL);
290
/*xmms_remote_playlist_prev(oglspectrum_vp.xmms_session); */
293
/*xmms_remote_play(oglspectrum_vp.xmms_session); */
296
/*xmms_remote_pause(oglspectrum_vp.xmms_session); */
299
/*xmms_remote_stop(oglspectrum_vp.xmms_session); */
302
/* xmms_remote_playlist_next(oglspectrum_vp.xmms_session); */
347
if ((Atom)event.xclient.data.l[0] == wm_delete_window_atom)
374
glXMakeCurrent(dpy, 0, NULL);
375
glXDestroyContext(dpy, glxcontext);
382
XUngrabPointer(dpy, CurrentTime);
383
grabbed_pointer = FALSE;
386
XDestroyWindow(dpy, window);
389
pthread_mutex_unlock(&scope_mutex);
390
stop_display(0); /* Close down display */
394
static void start_display(void)
398
for(x = 0; x < 16; x++)
400
for(y = 0; y < 16; y++)
405
scale = 1.0 / log(256.0);
415
pthread_create(&draw_thread, NULL, draw_thread_func, NULL);
418
static void stop_display(int join_thread)
420
if (going && join_thread)
423
pthread_join(draw_thread, NULL);
428
XFreeColormap(dpy, colormap);
438
static int oglspectrum_init(void *arg)
440
pthread_mutex_init(&scope_mutex, NULL);
442
if (prefs_get_bool(ap_prefs, "opengl_spectrum", "active", 0)) {
448
static void oglspectrum_cleanup(void)
453
static void oglspectrum_start(void)
455
if (pthread_mutex_trylock(&scope_mutex) != 0) {
456
alsaplayer_error("spectrum already running");
462
static void oglspectrum_stop(void)
467
static void oglspectrum_set_fft(void *fft_buffer, int samples, int channels)
472
int *buf = (int *)fft_buffer;
475
int xscale[] = {0, 1, 2, 3, 5, 7, 10, 14, 20, 28, 40, 54, 74, 101, 137, 187, 255};
477
for(y = 15; y > 0; y--) {
478
for(i = 0; i < 16; i++) {
479
heights[y][i] = heights[y - 1][i];
483
for(i = 0; i < NUM_BANDS; i++) {
484
for(c = xscale[i], y = 0; c < xscale[i + 1]; c++) {
485
if((buf[c]+buf[samples+c]) > y)
486
y = buf[c]+buf[samples+c];
490
val = (log(y) * scale);
497
static int oglspectrum_running(void)
502
static void oglspectrum_shutdown(void)
504
prefs_set_bool(ap_prefs, "opengl_spectrum", "active", oglspectrum_running());
505
if (oglspectrum_running()) {
511
scope_plugin oglspectrum_plugin = {
512
SCOPE_PLUGIN_VERSION,
520
oglspectrum_shutdown,
526
scope_plugin *scope_plugin_info(void)
528
return &oglspectrum_plugin;