diff options
| author | Takamichi Horikawa <takamichiho@gmail.com> | 2017-04-04 23:57:39 +0900 | 
|---|---|---|
| committer | Takamichi Horikawa <takamichiho@gmail.com> | 2017-04-04 23:57:39 +0900 | 
| commit | c6c96944ae1bb1d7363349ec21be9dca76ee9ec4 (patch) | |
| tree | 287881e3661707b1a5185ef8dd1e24c866db0636 | |
| parent | 4c68a0d48bf2fa49c7a20e728ebf8c0dde073b5c (diff) | |
gtk: add Oscilloscope view OpenGL renderer
| -rw-r--r-- | gtk/Makefile.am | 7 | ||||
| -rw-r--r-- | gtk/configure.ac | 13 | ||||
| -rw-r--r-- | gtk/oscilloview-gl.c | 170 | ||||
| -rw-r--r-- | gtk/oscilloview.c | 11 | 
4 files changed, 193 insertions, 8 deletions
| diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 0247644..3524f15 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -42,7 +42,6 @@ endif  fmplayer_SOURCES=main.c \                   toneview.c \ -                 oscilloview.c \                   ../tonedata/tonedata.c \                   ../common/fmplayer_file.c \                   ../common/fmplayer_file_gio.c \ @@ -50,3 +49,9 @@ fmplayer_SOURCES=main.c \                   $(FMDRIVER_SRC) \                   $(FMDSP_SRC) +if ENABLE_OPENGL +fmplayer_SOURCES+=oscilloview-gl.c +fmplayer_LDADD+=-lGL +else +fmplayer_SOURCES+=oscilloview.c +endif diff --git a/gtk/configure.ac b/gtk/configure.ac index f49bd74..79d02c1 100644 --- a/gtk/configure.ac +++ b/gtk/configure.ac @@ -22,5 +22,18 @@ AS_IF([test "x$emmintrin_found" = "xyes"], [    AC_DEFINE([ENABLE_SSE])  ]) +AC_ARG_ENABLE([opengl], AS_HELP_STRING([--enable-opengl], [Enable OpenGL rendering for Oscilloscope view (default: enable if found)])) +AS_IF([test "x$enable_opengl" != "xno"], [ +  AC_CHECK_LIB([GL], [glGetString], [opengl_found=yes]) +  AC_CHECK_HEADER([GL/glcorearb.h], , [opengl_found=]) +  AS_IF([test "x$enable_opengl" = "x" -a "x$opengl_found" = "xyes"], [ +    enable_opengl=yes +  ]) +  AS_IF([test "x$enable_opengl" = "xyes" -a "x$opengl_found" != "xyes"], [ +    AC_MSG_ERROR([OpenGL header/library not found (-lGL, GL/glcorearb.h)], [1]) +  ]) +]) +AM_CONDITIONAL([ENABLE_OPENGL], [test "x$enable_opengl" = "xyes"]) +  AC_CONFIG_FILES([Makefile])  AC_OUTPUT diff --git a/gtk/oscilloview-gl.c b/gtk/oscilloview-gl.c new file mode 100644 index 0000000..b67bdce --- /dev/null +++ b/gtk/oscilloview-gl.c @@ -0,0 +1,170 @@ +#include <gtk/gtk.h> +#define GL_GLEXT_PROTOTYPES +#include <GL/glcorearb.h> +#include <stdlib.h> +#include <string.h> +#include "oscilloview.h" + +struct oscilloview oscilloview_g = { +  .flag = ATOMIC_FLAG_INIT +}; + +enum { +  VIEW_SAMPLES = 1024, +}; + +static struct { +  GtkWidget *win; +  struct oscillodata oscillodata[LIBOPNA_OSCILLO_TRACK_COUNT]; +  GLuint program; +  GLuint vs, fs; +  GLuint vao; +  GLuint vbo_linear; +  GLuint vbo_data; +  GLint uni_xpos; +  GLint uni_ypos; +} g; + +static void on_glarea_unrealize(GtkWidget *w, gpointer ptr) { +  (void)w; +  GtkGLArea *area = GTK_GL_AREA(w); +  gtk_gl_area_make_current(area); +  if (gtk_gl_area_get_error(area)) return; +  glDeleteProgram(g.program); +  glDeleteShader(g.vs); +  glDeleteShader(g.fs); +  glDeleteBuffers(1, &g.vbo_linear); +  glDeleteBuffers(1, &g.vbo_data); +  glDeleteVertexArrays(1, &g.vao); +} + +static void on_destroy(GtkWidget *w, gpointer ptr) { +  (void)w; +  (void)ptr; +  g.win = 0; +} + +static const char v_sh[] = +"#version 110\n" +"attribute float coordx, coordy;\n" +"uniform float xpos, ypos;\n" +"void main(void) {\n" +"  gl_Position = vec4(vec2(coordx/3.0 + xpos, coordy*2.0/3.0 + ypos), 0.0, 1.0);\n" +"}\n"; + +static const char f_sh[] = +"#version 110\n" +"void main(void) {\n" +"  gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n" +"}\n"; + +static GLuint create_shader(const char *shader, GLenum type) { +  GLuint s = glCreateShader(type); +  glShaderSource(s, 1, &shader, 0); +  glCompileShader(s); +  GLint ok; +  glGetShaderiv(s, GL_COMPILE_STATUS, &ok); +  if (!ok) { +    GLint len; +    glGetShaderiv(s, GL_INFO_LOG_LENGTH, &len); +    if (len > 0) { +      char *log = malloc(len); +      if (log) { +        glGetShaderInfoLog(s, len, 0, log); +        printf("%s shader error: \n%s\n", +               (type == GL_VERTEX_SHADER) ? "vertex" : "fragment", +               log); +        free(log); +      } +    } +    glDeleteShader(s); +    return 0; +  } +  return s; +} + +static void on_realize(GtkWidget *w, gpointer ptr) { +  (void)ptr; +  GtkGLArea *area = GTK_GL_AREA(w); +  gtk_gl_area_make_current(area); +  if (gtk_gl_area_get_error(area)) return; +  g.program = glCreateProgram(); +  g.vs = create_shader(v_sh, GL_VERTEX_SHADER); +  g.fs = create_shader(f_sh, GL_FRAGMENT_SHADER); +  glAttachShader(g.program, g.vs); +  glAttachShader(g.program, g.fs); +  glBindAttribLocation(g.program, 0, "coordx"); +  glBindAttribLocation(g.program, 1, "coordy"); +  glLinkProgram(g.program); +  glUseProgram(g.program); +  g.uni_xpos = glGetUniformLocation(g.program, "xpos"); +  g.uni_ypos = glGetUniformLocation(g.program, "ypos"); +  glGenVertexArrays(1, &g.vao); +  glBindVertexArray(g.vao); +  glGenBuffers(1, &g.vbo_linear); +  glBindBuffer(GL_ARRAY_BUFFER, g.vbo_linear); +  static GLfloat linear_pos[VIEW_SAMPLES]; +  for (int i = 0; i < VIEW_SAMPLES; i++) { +    linear_pos[i] = 2.0f*((i-512) / (float)VIEW_SAMPLES); +  } +  glBufferData(GL_ARRAY_BUFFER, sizeof(linear_pos), linear_pos, GL_STATIC_DRAW); +  glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, 0); +  glGenBuffers(1, &g.vbo_data); +  glBindBuffer(GL_ARRAY_BUFFER, g.vbo_data); +  glBufferData(GL_ARRAY_BUFFER, 2*VIEW_SAMPLES, 0, GL_STREAM_DRAW); +  glVertexAttribPointer(1, 1, GL_SHORT, GL_TRUE, 0, 0); +} + +static gboolean on_render(GtkGLArea *area, +                          GdkGLContext *ctx, +                          gpointer ptr) { +  (void)ptr; +  if (!atomic_flag_test_and_set_explicit( +    &oscilloview_g.flag, memory_order_acquire)) { +    memcpy(g.oscillodata, +           oscilloview_g.oscillodata, +           sizeof(oscilloview_g.oscillodata)); +    atomic_flag_clear_explicit(&oscilloview_g.flag, memory_order_release); +  } +  glEnableVertexAttribArray(0); +  glEnableVertexAttribArray(1); +  glClear(GL_COLOR_BUFFER_BIT); + +  for (int x = 0; x < 3; x++) { +    for (int y = 0; y < 3; y++) { +      int start = OSCILLO_SAMPLE_COUNT - VIEW_SAMPLES; +      start -= (g.oscillodata[x*3+y].offset >> OSCILLO_OFFSET_SHIFT); +      glBindBuffer(GL_ARRAY_BUFFER, g.vbo_data); +      glBufferData(GL_ARRAY_BUFFER, 2*VIEW_SAMPLES, &g.oscillodata[x*3+y].buf[start], GL_STREAM_DRAW); +      glUniform1f(g.uni_xpos, (x - 1) * (2.0f/3.0f)); +      glUniform1f(g.uni_ypos, (y - 1) * (-2.0f/3.0f)); +      glDrawArrays(GL_LINE_STRIP, 0, 1024); +    } +  } +  glDisableVertexAttribArray(0); +  glDisableVertexAttribArray(1); +  return TRUE; +} + +static gboolean tick_cb(GtkWidget *w, GdkFrameClock *clock, gpointer ptr) { +  (void)clock; +  (void)ptr; +  gtk_widget_queue_draw(w); +  return G_SOURCE_CONTINUE; +} + +void show_oscilloview(void) { +  if (!g.win) { +    g.win = gtk_window_new(GTK_WINDOW_TOPLEVEL); +    gtk_window_set_title(GTK_WINDOW(g.win), "Oscilloscope view"); +    g_signal_connect(g.win, "destroy", G_CALLBACK(on_destroy), 0); +    GtkWidget *glarea = gtk_gl_area_new(); +    g_signal_connect(G_OBJECT(glarea), "unrealize", G_CALLBACK(on_glarea_unrealize), 0); +    gtk_gl_area_set_required_version(GTK_GL_AREA(glarea), 3, 2); +    g_signal_connect(glarea, "render", G_CALLBACK(on_render), 0); +    g_signal_connect(glarea, "realize", G_CALLBACK(on_realize), 0); +    gtk_container_add(GTK_CONTAINER(g.win), glarea); +    gtk_widget_add_tick_callback(glarea, tick_cb, 0, 0); +  } +  gtk_widget_show_all(g.win); +} diff --git a/gtk/oscilloview.c b/gtk/oscilloview.c index bf56140..e2893c6 100644 --- a/gtk/oscilloview.c +++ b/gtk/oscilloview.c @@ -10,8 +10,6 @@ struct oscilloview oscilloview_g = {  enum {    VIEW_SAMPLES = 1024,    VIEW_SKIP = 2, -  WIDTH = 600, -  HEIGHT = 300,  };  static struct { @@ -75,11 +73,10 @@ void show_oscilloview(void) {      g.win = gtk_window_new(GTK_WINDOW_TOPLEVEL);      gtk_window_set_title(GTK_WINDOW(g.win), "Oscilloscope view");      g_signal_connect(g.win, "destroy", G_CALLBACK(on_destroy), 0); +    GtkWidget *drawarea = gtk_drawing_area_new(); +    gtk_container_add(GTK_CONTAINER(g.win), drawarea); +    g_signal_connect(drawarea, "draw", G_CALLBACK(draw_cb), 0); +    gtk_widget_add_tick_callback(drawarea, tick_cb, 0, 0);    } -  GtkWidget *drawarea = gtk_drawing_area_new(); -  gtk_container_add(GTK_CONTAINER(g.win), drawarea); -  //gtk_widget_set_size_request(drawarea, WIDTH*3, HEIGHT*3); -  g_signal_connect(drawarea, "draw", G_CALLBACK(draw_cb), 0); -  gtk_widget_add_tick_callback(drawarea, tick_cb, 0, 0);    gtk_widget_show_all(g.win);  } | 
