2013-10-13 20:05:38 +00:00
|
|
|
/*
|
|
|
|
* gl.c
|
|
|
|
*
|
|
|
|
* OpenGL part of fosphor
|
|
|
|
*
|
2014-07-22 19:45:31 +00:00
|
|
|
* Copyright (C) 2013-2014 Sylvain Munaut
|
2013-10-13 20:05:38 +00:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program 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 General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \addtogroup gl
|
|
|
|
* @{
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \file gl.c
|
|
|
|
* \brief OpenGL part of fosphor
|
|
|
|
*/
|
|
|
|
|
2013-11-07 22:46:29 +00:00
|
|
|
#include <errno.h>
|
2013-11-09 20:49:35 +00:00
|
|
|
#include <math.h>
|
2013-10-13 20:05:38 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "gl_platform.h"
|
|
|
|
|
2013-11-09 20:49:35 +00:00
|
|
|
#include "axis.h"
|
2014-01-21 09:07:05 +00:00
|
|
|
#include "fosphor.h"
|
2013-10-13 20:05:38 +00:00
|
|
|
#include "gl.h"
|
|
|
|
#include "gl_cmap.h"
|
|
|
|
#include "gl_cmap_gen.h"
|
|
|
|
#include "gl_font.h"
|
2013-11-07 22:46:29 +00:00
|
|
|
#include "private.h"
|
2013-10-13 20:05:38 +00:00
|
|
|
#include "resource.h"
|
|
|
|
|
|
|
|
|
|
|
|
struct fosphor_gl_state
|
|
|
|
{
|
|
|
|
int init_complete;
|
|
|
|
|
|
|
|
struct gl_font *font;
|
|
|
|
|
2013-10-26 15:52:43 +00:00
|
|
|
struct fosphor_gl_cmap_ctx *cmap_ctx;
|
2013-10-13 20:05:38 +00:00
|
|
|
GLuint cmap_waterfall;
|
|
|
|
GLuint cmap_histogram;
|
|
|
|
|
|
|
|
GLuint tex_waterfall;
|
|
|
|
GLuint tex_histogram;
|
|
|
|
|
|
|
|
GLuint vbo_spectrum;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* Helpers / Internal API */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static void
|
|
|
|
gl_tex2d_float_clear(GLuint tex_id, int width, int height)
|
|
|
|
{
|
|
|
|
float buf[16*16];
|
|
|
|
int x, y, cw, ch;
|
|
|
|
|
|
|
|
memset(buf, 0x00, sizeof(buf));
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, tex_id);
|
|
|
|
|
|
|
|
for (y=0; y<height; y+=16) {
|
|
|
|
for (x=0; x<width; x+=16) {
|
|
|
|
cw = ((x+16) > width ) ? (width - x) : 16;
|
|
|
|
ch = ((y+16) > height) ? (height - y) : 16;
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, cw, ch, GL_RED, GL_FLOAT, buf);
|
|
|
|
}
|
|
|
|
}
|
2014-07-24 18:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gl_tex2d_write(GLuint tex_id, float *src, int width, int height)
|
|
|
|
{
|
|
|
|
glBindTexture(GL_TEXTURE_2D, tex_id);
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-07-24 18:48:13 +00:00
|
|
|
glTexImage2D(
|
|
|
|
GL_TEXTURE_2D,
|
|
|
|
0, GL_R32F,
|
|
|
|
width, height, 0,
|
|
|
|
GL_RED, GL_FLOAT,
|
|
|
|
src
|
|
|
|
);
|
2013-10-13 20:05:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gl_vbo_clear(GLuint vbo_id, int size)
|
|
|
|
{
|
|
|
|
void *ptr;
|
|
|
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
|
|
|
|
|
|
|
|
ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
|
|
|
|
if (!ptr)
|
|
|
|
abort();
|
|
|
|
|
|
|
|
memset(ptr, 0x00, size);
|
|
|
|
|
|
|
|
glUnmapBuffer(GL_ARRAY_BUFFER);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
static void
|
2014-07-24 18:48:13 +00:00
|
|
|
gl_vbo_read(GLuint vbo_id, void *dst, int size)
|
2013-10-13 20:05:38 +00:00
|
|
|
{
|
|
|
|
void *ptr;
|
|
|
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
|
|
|
|
|
|
|
|
ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
|
|
|
|
if (!ptr)
|
|
|
|
abort();
|
|
|
|
|
|
|
|
memcpy(dst, ptr, size);
|
|
|
|
|
|
|
|
glUnmapBuffer(GL_ARRAY_BUFFER);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-07-24 18:48:13 +00:00
|
|
|
static void
|
|
|
|
gl_vbo_write(GLuint vbo_id, void *src, int size)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
void *ptr;
|
|
|
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
|
|
|
|
|
|
|
|
ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
|
|
|
|
if (!ptr)
|
|
|
|
abort();
|
|
|
|
|
|
|
|
memcpy(ptr, src, size);
|
|
|
|
|
|
|
|
glUnmapBuffer(GL_ARRAY_BUFFER);
|
|
|
|
#else
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, size, src, GL_DYNAMIC_DRAW);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-10-13 20:05:38 +00:00
|
|
|
static void
|
2013-11-07 22:46:29 +00:00
|
|
|
gl_deferred_init(struct fosphor *self)
|
2013-10-13 20:05:38 +00:00
|
|
|
{
|
2013-11-07 22:46:29 +00:00
|
|
|
struct fosphor_gl_state *gl = self->gl;
|
2013-10-13 20:05:38 +00:00
|
|
|
int len;
|
|
|
|
|
|
|
|
/* Prevent double init */
|
|
|
|
if (gl->init_complete)
|
|
|
|
return;
|
|
|
|
|
|
|
|
gl->init_complete = 1;
|
|
|
|
|
|
|
|
/* Waterfall texture (FFT_LEN * 1024) */
|
|
|
|
glGenTextures(1, &gl->tex_waterfall);
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, gl->tex_waterfall);
|
|
|
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
|
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, FOSPHOR_FFT_LEN, 1024, 0, GL_RED, GL_FLOAT, NULL);
|
|
|
|
|
|
|
|
gl_tex2d_float_clear(gl->tex_waterfall, FOSPHOR_FFT_LEN, 1024);
|
|
|
|
|
|
|
|
/* Histogram texture (FFT_LEN * 128) */
|
|
|
|
glGenTextures(1, &gl->tex_histogram);
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, gl->tex_histogram);
|
|
|
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, FOSPHOR_FFT_LEN, 128, 0, GL_RED, GL_FLOAT, NULL);
|
|
|
|
|
|
|
|
gl_tex2d_float_clear(gl->tex_histogram, FOSPHOR_FFT_LEN, 128);
|
|
|
|
|
|
|
|
/* Spectrum VBO (2 * FFT_LEN, half for live, half for 'hold') */
|
|
|
|
glGenBuffers(1, &gl->vbo_spectrum);
|
|
|
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, gl->vbo_spectrum);
|
|
|
|
|
|
|
|
len = 2 * sizeof(float) * 2 * FOSPHOR_FFT_LEN;
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, len, NULL, GL_DYNAMIC_DRAW);
|
|
|
|
|
|
|
|
gl_vbo_clear(gl->vbo_spectrum, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* Exposed API */
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
2013-11-07 22:46:29 +00:00
|
|
|
int
|
|
|
|
fosphor_gl_init(struct fosphor *self)
|
2013-10-13 20:05:38 +00:00
|
|
|
{
|
|
|
|
struct fosphor_gl_state *gl;
|
|
|
|
const void *font_data;
|
|
|
|
int len, rv;
|
|
|
|
|
|
|
|
/* Allocate structure */
|
|
|
|
gl = malloc(sizeof(struct fosphor_gl_state));
|
|
|
|
if (!gl)
|
2013-11-07 22:46:29 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
self->gl = gl;
|
2013-10-13 20:05:38 +00:00
|
|
|
|
|
|
|
memset(gl, 0, sizeof(struct fosphor_gl_state));
|
|
|
|
|
|
|
|
/* Font */
|
|
|
|
gl->font = glf_alloc(8, GLF_FLG_LCD);
|
2013-11-07 22:46:29 +00:00
|
|
|
if (!gl->font) {
|
|
|
|
rv = -ENOMEM;
|
2013-10-13 20:05:38 +00:00
|
|
|
goto error;
|
2013-11-07 22:46:29 +00:00
|
|
|
}
|
2013-10-13 20:05:38 +00:00
|
|
|
|
|
|
|
font_data = resource_get("DroidSansMonoDotted.ttf", &len);
|
2013-11-07 22:46:29 +00:00
|
|
|
if (!font_data) {
|
|
|
|
rv = -ENOENT;
|
2013-10-13 20:05:38 +00:00
|
|
|
goto error;
|
2013-11-07 22:46:29 +00:00
|
|
|
}
|
2013-10-13 20:05:38 +00:00
|
|
|
|
|
|
|
rv = glf_load_face_mem(gl->font, font_data, len);
|
|
|
|
if (rv)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* Color mapping */
|
2013-10-26 15:52:43 +00:00
|
|
|
gl->cmap_ctx = fosphor_gl_cmap_init();
|
|
|
|
|
|
|
|
rv = (gl->cmap_ctx == NULL);
|
2013-10-13 20:05:38 +00:00
|
|
|
|
|
|
|
rv |= fosphor_gl_cmap_generate(&gl->cmap_waterfall,
|
|
|
|
fosphor_gl_cmap_waterfall, 256);
|
|
|
|
rv |= fosphor_gl_cmap_generate(&gl->cmap_histogram,
|
|
|
|
fosphor_gl_cmap_histogram, 256);
|
|
|
|
|
|
|
|
if (rv)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* Done */
|
2013-11-07 22:46:29 +00:00
|
|
|
return 0;
|
2013-10-13 20:05:38 +00:00
|
|
|
|
|
|
|
error:
|
2013-11-07 22:46:29 +00:00
|
|
|
fosphor_gl_release(self);
|
|
|
|
|
|
|
|
return rv;
|
2013-10-13 20:05:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-11-07 22:46:29 +00:00
|
|
|
fosphor_gl_release(struct fosphor *self)
|
2013-10-13 20:05:38 +00:00
|
|
|
{
|
2013-11-07 22:46:29 +00:00
|
|
|
struct fosphor_gl_state *gl = self->gl;
|
|
|
|
|
2013-10-13 20:05:38 +00:00
|
|
|
/* Safety */
|
|
|
|
if (!gl)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Release all */
|
|
|
|
glDeleteBuffers(1, &gl->vbo_spectrum);
|
|
|
|
|
|
|
|
glDeleteTextures(1, &gl->tex_histogram);
|
|
|
|
glDeleteTextures(1, &gl->tex_waterfall);
|
|
|
|
|
|
|
|
glDeleteTextures(1, &gl->cmap_histogram);
|
|
|
|
glDeleteTextures(1, &gl->cmap_waterfall);
|
2013-10-26 15:52:43 +00:00
|
|
|
fosphor_gl_cmap_release(gl->cmap_ctx);
|
2013-10-13 20:05:38 +00:00
|
|
|
|
|
|
|
glf_free(gl->font);
|
|
|
|
|
|
|
|
/* Release structure */
|
|
|
|
free(gl);
|
2013-11-13 22:56:23 +00:00
|
|
|
|
|
|
|
/* Nothing left */
|
|
|
|
self->gl = NULL;
|
2013-10-13 20:05:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GLuint
|
2013-11-07 22:46:29 +00:00
|
|
|
fosphor_gl_get_shared_id(struct fosphor *self,
|
2013-10-13 20:05:38 +00:00
|
|
|
enum fosphor_gl_id id)
|
|
|
|
{
|
2013-11-07 22:46:29 +00:00
|
|
|
struct fosphor_gl_state *gl = self->gl;
|
|
|
|
|
2014-07-24 17:17:42 +00:00
|
|
|
/* CL is now sufficiently booted to complete the GL init
|
2013-10-13 20:05:38 +00:00
|
|
|
* in a CL context */
|
2013-11-07 22:46:29 +00:00
|
|
|
gl_deferred_init(self);
|
2013-10-13 20:05:38 +00:00
|
|
|
|
|
|
|
/* Select ID to return */
|
|
|
|
switch (id) {
|
|
|
|
case GL_ID_TEX_WATERFALL:
|
|
|
|
return gl->tex_waterfall;
|
|
|
|
|
|
|
|
case GL_ID_TEX_HISTOGRAM:
|
|
|
|
return gl->tex_histogram;
|
|
|
|
|
|
|
|
case GL_ID_VBO_SPECTRUM:
|
|
|
|
return gl->vbo_spectrum;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-24 18:48:13 +00:00
|
|
|
void
|
|
|
|
fosphor_gl_refresh(struct fosphor *self)
|
|
|
|
{
|
|
|
|
struct fosphor_gl_state *gl = self->gl;
|
|
|
|
|
|
|
|
if (self->flags & FLG_FOSPHOR_USE_CLGL_SHARING)
|
|
|
|
return;
|
|
|
|
|
|
|
|
gl_deferred_init(self);
|
|
|
|
|
|
|
|
gl_tex2d_write(gl->tex_waterfall, self->img_waterfall, FOSPHOR_FFT_LEN, 1024);
|
|
|
|
gl_tex2d_write(gl->tex_histogram, self->img_histogram, FOSPHOR_FFT_LEN, 128);
|
|
|
|
gl_vbo_write(gl->vbo_spectrum, self->buf_spectrum, 2 * 2 * sizeof(float) * FOSPHOR_FFT_LEN);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-10-13 20:05:38 +00:00
|
|
|
void
|
2014-01-21 09:07:05 +00:00
|
|
|
fosphor_gl_draw(struct fosphor *self, struct fosphor_render *render)
|
2013-10-13 20:05:38 +00:00
|
|
|
{
|
2013-11-07 22:46:29 +00:00
|
|
|
struct fosphor_gl_state *gl = self->gl;
|
2013-11-09 20:49:35 +00:00
|
|
|
struct freq_axis freq_axis;
|
2014-01-21 09:07:05 +00:00
|
|
|
float x[2], y[2], u[2], v[2];
|
|
|
|
float tw, bw;
|
2013-10-13 20:05:38 +00:00
|
|
|
int i;
|
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
/* Utils */
|
|
|
|
tw = 1.0f / (float)FOSPHOR_FFT_LEN; /* Texel width */
|
|
|
|
bw = 1.0f / (float)(FOSPHOR_FFT_LEN-1); /* Bin width (displayed) */
|
|
|
|
|
|
|
|
/* Texture mapping notes:
|
|
|
|
*
|
|
|
|
* - The texture have the "DC" bin at texel 0, however we want it to
|
|
|
|
* be displayed centered and to do so, texture coordinates are used.
|
|
|
|
* - One of the bin is not displayed (the one at u=0.5f) because
|
|
|
|
* it is neither positive freq, nor negative ones, but both. To
|
|
|
|
* compensate for this, the (1.0f - tw) factor is used.
|
|
|
|
*
|
|
|
|
* Vertex mapping notes:
|
|
|
|
*
|
|
|
|
* - We want the vertex to appear at the center of the displayed bins
|
|
|
|
* - The vertex 'X' coordinates are filled in by the display kernel as
|
|
|
|
* ((bin #) ^ (N >> 1)) / (N >> 1) - 1
|
|
|
|
* - So the DC bin is 0.0f and the undisplayed bin is -1. The others
|
|
|
|
* are spread between [ -1+2*tw to 1-2*tw ]
|
|
|
|
* - For display, that range is first remapped to [0 to 1], then to
|
|
|
|
* [ bw/2 to 1-bw/2 ] (where bw is normalized displayed bin width)
|
|
|
|
* so that each point maps to the center of the bin on the textures.
|
|
|
|
* - Finally the zoom is applied and then the transform to map on the
|
|
|
|
* requested screen area
|
|
|
|
*/
|
2013-10-13 20:05:38 +00:00
|
|
|
|
|
|
|
/* Draw waterfall */
|
2014-01-21 09:07:05 +00:00
|
|
|
if (render->options & FRO_WATERFALL)
|
|
|
|
{
|
|
|
|
x[0] = render->_x[0];
|
|
|
|
x[1] = render->_x[1];
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
y[0] = render->_y_wf[0];
|
|
|
|
y[1] = render->_y_wf[1];
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
u[0] = 0.5f + tw + ((1.0f - tw) * render->freq_start);
|
|
|
|
u[1] = 0.5f + tw + ((1.0f - tw) * render->freq_stop);
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
v[1] = (float)render->_wf_pos / 1024.0f;
|
|
|
|
v[0] = v[1] - render->wf_span;
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
fosphor_gl_cmap_enable(gl->cmap_ctx,
|
|
|
|
gl->tex_waterfall, gl->cmap_waterfall,
|
|
|
|
self->power.scale, self->power.offset,
|
|
|
|
GL_CMAP_MODE_BILINEAR);
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
glBegin( GL_QUADS );
|
|
|
|
glTexCoord2f(u[0], v[0]); glVertex2f(x[0], y[0]);
|
|
|
|
glTexCoord2f(u[1], v[0]); glVertex2f(x[1], y[0]);
|
|
|
|
glTexCoord2f(u[1], v[1]); glVertex2f(x[1], y[1]);
|
|
|
|
glTexCoord2f(u[0], v[1]); glVertex2f(x[0], y[1]);
|
|
|
|
glEnd();
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
fosphor_gl_cmap_disable();
|
|
|
|
}
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
/* Draw histogram */
|
|
|
|
if (render->options & FRO_HISTO)
|
|
|
|
{
|
|
|
|
x[0] = render->_x[0];
|
|
|
|
x[1] = render->_x[1];
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
y[0] = render->_y_histo[0];
|
|
|
|
y[1] = render->_y_histo[1];
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
u[0] = 0.5f + tw + ((1.0f - tw) * render->freq_start);
|
|
|
|
u[1] = 0.5f + tw + ((1.0f - tw) * render->freq_stop);
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
v[0] = 0.0f;
|
|
|
|
v[1] = 1.0f;
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
fosphor_gl_cmap_enable(gl->cmap_ctx,
|
|
|
|
gl->tex_histogram, gl->cmap_histogram,
|
|
|
|
1.1f, 0.0f, GL_CMAP_MODE_BILINEAR);
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
glBegin( GL_QUADS );
|
|
|
|
glTexCoord2f(u[0], v[0]); glVertex2f(x[0], y[0]);
|
|
|
|
glTexCoord2f(u[1], v[0]); glVertex2f(x[1], y[0]);
|
|
|
|
glTexCoord2f(u[1], v[1]); glVertex2f(x[1], y[1]);
|
|
|
|
glTexCoord2f(u[0], v[1]); glVertex2f(x[0], y[1]);
|
|
|
|
glEnd();
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
fosphor_gl_cmap_disable();
|
|
|
|
}
|
|
|
|
else if (render->options & (FRO_LIVE | FRO_MAX_HOLD))
|
|
|
|
{
|
|
|
|
x[0] = render->_x[0];
|
|
|
|
x[1] = render->_x[1];
|
|
|
|
|
|
|
|
y[0] = render->_y_histo[0];
|
|
|
|
y[1] = render->_y_histo[1];
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
glColor3f(0.0f, 0.0f, 0.1f);
|
|
|
|
|
|
|
|
glBegin( GL_QUADS );
|
|
|
|
glVertex2f(x[0], y[0]);
|
|
|
|
glVertex2f(x[1], y[0]);
|
|
|
|
glVertex2f(x[1], y[1]);
|
|
|
|
glVertex2f(x[0], y[1]);
|
|
|
|
glEnd();
|
|
|
|
}
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
/* Draw spectrum */
|
|
|
|
if (render->options & (FRO_LIVE | FRO_MAX_HOLD))
|
|
|
|
{
|
|
|
|
int idx[2], len;
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
/* Select end-points */
|
|
|
|
idx[0] = 1 + (int)ceilf (render->freq_start * (float)(FOSPHOR_FFT_LEN - 1) - 0.5f);
|
|
|
|
idx[1] = 1 + (int)floorf(render->freq_stop * (float)(FOSPHOR_FFT_LEN - 1) - 0.5f);
|
|
|
|
len = idx[1] - idx[0] + 1;
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
/* Setup */
|
|
|
|
glPushMatrix();
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
/* Screen position scaling */
|
|
|
|
glTranslatef(
|
|
|
|
render->_x[0],
|
|
|
|
render->_y_histo[0],
|
|
|
|
0.0f
|
|
|
|
);
|
2013-11-09 20:49:35 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
glScalef(
|
|
|
|
render->_x[1] - render->_x[0],
|
|
|
|
render->_y_histo[1] - render->_y_histo[0],
|
|
|
|
1.0f
|
|
|
|
);
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
/* Power offset / scaling */
|
|
|
|
glScalef(1.0f, self->power.scale, 1.0f);
|
|
|
|
glTranslatef(0.0f, self->power.offset, 0.0f);
|
|
|
|
|
|
|
|
/* Spectrum range selection */
|
|
|
|
glScalef(1.0f / (render->freq_stop - render->freq_start), 1.0f, 1.0f);
|
|
|
|
glTranslatef(-render->freq_start, 0.0f, 0.0f);
|
|
|
|
|
|
|
|
/* Map the center of each N-1 bins */
|
|
|
|
glTranslatef(0.5f * bw, 0.0f, 0.0f);
|
|
|
|
glScalef(1.0f - bw, 1.0f, 1.0f);
|
|
|
|
|
|
|
|
/* Spectrum x scaling to [0.0 -> 1.0] range */
|
|
|
|
glTranslatef(0.5f, 0.0f, 0.0f);
|
|
|
|
glScalef(0.5f / (1.0f - 2.0f * tw), 1.0f, 1.0f);
|
|
|
|
|
|
|
|
/* GL state setup */
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, gl->vbo_spectrum);
|
|
|
|
glVertexPointer(2, GL_FLOAT, 0, 0);
|
2013-10-13 20:05:38 +00:00
|
|
|
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
2014-01-21 09:07:05 +00:00
|
|
|
glEnable(GL_LINE_SMOOTH);
|
|
|
|
glLineWidth(1.0f);
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
/* Live */
|
|
|
|
if (render->options & FRO_LIVE)
|
|
|
|
{
|
|
|
|
glColor4f(1.0f, 1.0f, 1.0f, 0.75f);
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glDrawArrays(GL_LINE_STRIP, idx[0], len);
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
}
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
/* Max hold */
|
|
|
|
if (render->options & FRO_MAX_HOLD)
|
|
|
|
{
|
|
|
|
glColor4f(1.0f, 0.0f, 0.0f, 0.75f);
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glDrawArrays(GL_LINE_STRIP, idx[0] + FOSPHOR_FFT_LEN, len);
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
}
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
/* Cleanup */
|
|
|
|
glDisable(GL_BLEND);
|
2013-11-09 20:49:35 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
glPopMatrix();
|
|
|
|
}
|
2013-11-09 20:49:35 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
/* Setup frequency axis */
|
2014-05-07 14:48:28 +00:00
|
|
|
if (render->freq_start != 0.0f || render->freq_stop != 1.0f)
|
|
|
|
{
|
|
|
|
/* The freq_{start,stop} have some imprecisions due to the floating
|
|
|
|
* point nature. To avoid this crapping the display of the axis, we
|
|
|
|
* try to 'round' them */
|
|
|
|
|
|
|
|
double freq_start = round(1e7 * render->freq_start) / 1e7;
|
|
|
|
double freq_stop = round(1e7 * render->freq_stop) / 1e7;
|
|
|
|
|
|
|
|
double rel_center = ((freq_stop + freq_start) / 2.0) - 0.5;
|
|
|
|
double rel_span = (freq_stop - freq_start);
|
|
|
|
|
|
|
|
freq_axis_build(&freq_axis,
|
|
|
|
self->frequency.center + rel_center * self->frequency.span,
|
|
|
|
self->frequency.span * rel_span,
|
|
|
|
render->freq_n_div
|
|
|
|
);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Use the straight number we were provider without math to
|
|
|
|
* avoid any imprecisions */
|
|
|
|
freq_axis_build(&freq_axis,
|
|
|
|
self->frequency.center,
|
|
|
|
self->frequency.span,
|
|
|
|
render->freq_n_div
|
|
|
|
);
|
|
|
|
}
|
2013-10-13 20:05:38 +00:00
|
|
|
|
2014-01-21 09:07:05 +00:00
|
|
|
/* Draw grid */
|
|
|
|
if (render->options & (FRO_LIVE | FRO_MAX_HOLD | FRO_HISTO))
|
|
|
|
{
|
|
|
|
for (i=0; i<11; i++)
|
|
|
|
{
|
|
|
|
float fg_color[3] = { 1.00f, 1.00f, 0.33f };
|
2014-05-07 14:32:53 +00:00
|
|
|
float yv;
|
2014-01-21 09:07:05 +00:00
|
|
|
|
|
|
|
yv = render->_y_histo[0] + i * render->_y_histo_div;
|
|
|
|
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
|
|
|
|
|
|
|
|
glBegin(GL_LINES);
|
|
|
|
glVertex2f(render->_x[0] + 0.5f, yv + 0.5f);
|
|
|
|
glVertex2f(render->_x[1] - 0.5f, yv + 0.5f);
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
|
2014-05-07 14:32:53 +00:00
|
|
|
if (render->options & FRO_LABEL_PWR)
|
|
|
|
{
|
|
|
|
glf_begin(gl->font, fg_color);
|
2014-01-21 09:07:05 +00:00
|
|
|
|
|
|
|
glf_printf(gl->font,
|
|
|
|
render->_x_label, GLF_RIGHT,
|
|
|
|
yv, GLF_CENTER,
|
|
|
|
"%d", self->power.db_ref - (10-i) * self->power.db_per_div
|
|
|
|
);
|
2014-05-07 14:32:53 +00:00
|
|
|
|
|
|
|
glf_end();
|
2014-01-21 09:07:05 +00:00
|
|
|
}
|
2014-05-07 14:32:53 +00:00
|
|
|
}
|
2014-01-21 09:07:05 +00:00
|
|
|
|
2014-05-07 14:32:53 +00:00
|
|
|
for (i=0; i<=render->freq_n_div; i++)
|
|
|
|
{
|
|
|
|
float fg_color[3] = { 1.00f, 1.00f, 0.33f };
|
|
|
|
float xv, xv_ofs, xv_ofs_total;
|
|
|
|
char buf[32];
|
|
|
|
|
|
|
|
xv = render->_x[0] + i * render->_x_div;
|
2014-01-21 09:07:05 +00:00
|
|
|
|
2014-05-07 14:32:53 +00:00
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
|
|
|
|
|
|
|
|
glBegin(GL_LINES);
|
|
|
|
glVertex2f(xv + 0.5f, render->_y_histo[0]+0.5f);
|
|
|
|
glVertex2f(xv + 0.5f, render->_y_histo[1]-0.5f);
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
|
|
|
|
freq_axis_render(&freq_axis, buf, (render->freq_n_div / 2));
|
|
|
|
xv_ofs_total = glf_width_str(gl->font, buf);
|
|
|
|
freq_axis_render(&freq_axis, buf, -(render->freq_n_div / 2));
|
|
|
|
xv_ofs_total += glf_width_str(gl->font, buf);
|
|
|
|
xv_ofs_total /= 2.0f;
|
|
|
|
|
|
|
|
if (render->options & FRO_LABEL_FREQ)
|
|
|
|
{
|
|
|
|
int ib = i - (render->freq_n_div / 2);
|
|
|
|
|
|
|
|
glf_begin(gl->font, fg_color);
|
|
|
|
|
|
|
|
freq_axis_render(&freq_axis, buf, ib);
|
|
|
|
|
|
|
|
xv_ofs = floor((- xv_ofs_total * ib) / render->freq_n_div);
|
2014-01-21 09:07:05 +00:00
|
|
|
|
|
|
|
glf_printf(gl->font,
|
|
|
|
xv + xv_ofs, GLF_CENTER,
|
|
|
|
render->_y_label, GLF_CENTER,
|
|
|
|
"%s", buf
|
|
|
|
);
|
|
|
|
|
2014-05-07 14:32:53 +00:00
|
|
|
glf_end();
|
|
|
|
}
|
2014-01-21 09:07:05 +00:00
|
|
|
}
|
2013-10-13 20:05:38 +00:00
|
|
|
}
|
|
|
|
|
2014-05-01 19:15:26 +00:00
|
|
|
/* Draw channels */
|
|
|
|
if (render->options & FRO_CHANNELS)
|
|
|
|
{
|
|
|
|
struct {
|
|
|
|
int dir;
|
|
|
|
float pos;
|
|
|
|
} pt[2*FOSPHOR_MAX_CHANNELS+2], tpt;
|
|
|
|
|
|
|
|
int i, j, n;
|
|
|
|
|
|
|
|
/* Generate the points from the channels */
|
|
|
|
n = 2;
|
|
|
|
|
|
|
|
pt[0].dir = -1; pt[0].pos = 0.0f;
|
|
|
|
pt[1].dir = 1; pt[1].pos = 1.0f;
|
|
|
|
|
|
|
|
for (i=0; i<FOSPHOR_MAX_CHANNELS; i++)
|
|
|
|
{
|
|
|
|
float f;
|
|
|
|
|
|
|
|
if (!render->channels[i].enabled)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
f = render->channels[i].center
|
|
|
|
- render->channels[i].width / 2.0f;
|
|
|
|
pt[n].dir = 1;
|
|
|
|
pt[n].pos = (f > 0.0f) ? (f < 1.0f ? f : 1.0f) : 0.0f;
|
|
|
|
n++;
|
|
|
|
|
|
|
|
f = render->channels[i].center
|
|
|
|
+ render->channels[i].width / 2.0f;
|
|
|
|
|
|
|
|
pt[n].dir = -1;
|
|
|
|
pt[n].pos = (f > 0.0f) ? (f < 1.0f ? f : 1.0f) : 0.0f;
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Only if there is something to do ... */
|
|
|
|
if (n > 2)
|
|
|
|
{
|
|
|
|
int l = pt[0].dir;
|
|
|
|
|
|
|
|
/* GL setup */
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
|
|
glPushMatrix();
|
|
|
|
|
|
|
|
glTranslatef(render->_x[0], 0.0f, 0.0f);
|
|
|
|
glScalef(render->_x[1] - render->_x[0], 1.0f, 1.0f);
|
|
|
|
|
|
|
|
/* Sort and draw at the same time */
|
|
|
|
for (i=1; i<n; i++)
|
|
|
|
{
|
|
|
|
int mi = i;
|
|
|
|
|
|
|
|
/* Find min index */
|
|
|
|
for (j=i+1; j<n; j++) {
|
|
|
|
if (pt[j].pos < pt[mi].pos)
|
|
|
|
mi = j;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Swap */
|
|
|
|
tpt = pt[i];
|
|
|
|
pt[i] = pt[mi];
|
|
|
|
pt[mi] = tpt;
|
|
|
|
|
|
|
|
/* Draw */
|
|
|
|
if ((pt[i-1].pos != pt[i].pos) && (l != 0))
|
|
|
|
{
|
|
|
|
if (l < 0)
|
|
|
|
glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
|
|
|
|
else
|
|
|
|
glColor4f(1.0f, 1.0f, 1.0f, 0.2f - 0.2f / (1 + l));
|
|
|
|
|
|
|
|
if (render->options & FRO_WATERFALL) {
|
|
|
|
glBegin( GL_QUADS );
|
|
|
|
glVertex2f(pt[i ].pos, render->_y_histo[0]);
|
|
|
|
glVertex2f(pt[i-1].pos, render->_y_histo[0]);
|
|
|
|
glVertex2f(pt[i-1].pos, render->_y_histo[1]);
|
|
|
|
glVertex2f(pt[i ].pos, render->_y_histo[1]);
|
|
|
|
glEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (render->options & (FRO_LIVE | FRO_MAX_HOLD | FRO_HISTO)) {
|
|
|
|
glBegin( GL_QUADS );
|
|
|
|
glVertex2f(pt[i ].pos, render->_y_wf[0]);
|
|
|
|
glVertex2f(pt[i-1].pos, render->_y_wf[0]);
|
|
|
|
glVertex2f(pt[i-1].pos, render->_y_wf[1]);
|
|
|
|
glVertex2f(pt[i ].pos, render->_y_wf[1]);
|
|
|
|
glEnd();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
l += pt[i].dir;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* GL cleanup */
|
|
|
|
glPopMatrix();
|
|
|
|
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-13 20:05:38 +00:00
|
|
|
/* Ensure GL is done */
|
|
|
|
glFinish();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! @} */
|