I'm writing a GTK+ audio application, and I would like to draw an oscilloscope that updates in real-time to visualize the sound wave. I've been trying a drawing area and pix map approach where I plot points every frame, but it isn't working. If I clear the entire oscilloscope every frame, the image tears. Not only that, the slow speed makes the audio very choppy. Right now my code can only smear the wave form on top of what's been already drawn. I've also tried clearing the pixels I drew from the last frame -- but the update rectangle makes things a mess -- it didn't work.
What is the best way to draw a constantly updating set of monochrome pixels onto the screen with GTK? Speed is top priority. Should I try rendering an OpenGL visual onto a drawing area widget? Is the pixbuf a better choice than a pixmap? I wouldn't know because I'm unfamiliar with the API and the documentation does not have any practical advice for this type of situation (heck, the drawing area widget isn't even documented). Here is the code for my current method -- it's pretty straight-forward:
Code:
static GdkPixmap *pixmap = NULL;
GtkWidget *drawing_area = NULL;
static gboolean configure_event(GtkWidget *widget, GdkEventConfigure *event)
{
if (pixmap)
g_object_unref(pixmap);
pixmap = gdk_pixmap_new(widget->window, widget->allocation.width, widget->allocation.height, -1);
gdk_draw_rectangle(pixmap, widget->style->black_gc, TRUE, 0, 0, widget->allocation.width, widget->allocation.height);
return TRUE;
}
static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event)
{
gdk_draw_drawable(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE (widget)], pixmap,
event->area.x, event->area.y,
event->area.x, event->area.y,
event->area.width, event->area.height);
return FALSE;
}
/////////////////////////////// WIDGET INITIALIZATION CODE ////////////////////////////////////
drawing_area = gtk_drawing_area_new();
gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 640, 480);
gtk_table_attach_defaults(GTK_TABLE(table), drawing_area, 0, 2, 0, 1);
g_signal_connect(G_OBJECT (drawing_area), "expose_event", G_CALLBACK (expose_event), NULL);
g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK (configure_event), NULL);
gtk_widget_set_events(drawing_area, GDK_EXPOSURE_MASK);
gtk_widget_show(drawing_area);
///////////////////////////////////////////////////////////////////////////////////////////////
int x_pos = 0; // global because the low-latency audio doesn't all come in at once
void UpdateOscilloscope(short *audio, int len)
{
GdkRectangle update_rect;
int lower_x = -1, lower_y = -1, upper_x = -1, upper_y = -1;
for (register int i=0; i < len / 2; i++)
{
// determine our point
int pt_x = x_pos;
int pt_y = 240 - audio[i] / 137;
// adjust our boundary if necessary (should be minimum area needed to draw the oscilloscope wave)
if (lower_x == -1 || pt_x <= lower_x) lower_x = pt_x;
if (lower_y == -1 || pt_y <= lower_y) lower_y = pt_y;
if (upper_x == -1 || pt_x >= upper_x) upper_x = pt_x;
if (upper_y == -1 || pt_y >= upper_y) upper_y = pt_y;
// plot the point
gdk_draw_point(pixmap, drawing_area->style->white_gc, pt_x, pt_y);
// increment our x position
x_pos++;
if (x_pos >= 640) { x_pos = 0; break; }
}
// adjust our update rectangle based on the oscilloscope wave boundary
update_rect.x = lower_x;
update_rect.y = lower_y;
update_rect.width = upper_x - lower_x;
update_rect.height = upper_y - lower_y;
// queue a redraw
gtk_widget_queue_draw_area(drawing_area, update_rect.x, update_rect.y, update_rect.width, update_rect.height);
}