Index: gsr-window.c
===================================================================
--- gsr-window.c	(revision 3897)
+++ gsr-window.c	(working copy)
@@ -53,6 +53,7 @@
 #include "egg-recent-util.h"
 
 #include "gsr-window.h"
+#include "vumeter.h"
 
 GST_DEBUG_CATEGORY_STATIC (gsr_debug);
 #define GST_CAT_DEFAULT gsr_debug
@@ -1312,7 +1313,9 @@ stop_cb (GtkAction *action,
 		set_pipeline_state_to_null (priv->record->pipeline);
 		gtk_widget_set_sensitive (window->priv->level, FALSE);
 		gtk_widget_set_sensitive (window->priv->volume_label, FALSE);
-		gtk_progress_set_percentage (GTK_PROGRESS (window->priv->level), 0);
+		egg_vumeter_set_channels (EGG_VUMETER (window->priv->level), 1);
+		egg_vumeter_set_db (EGG_VUMETER (window->priv->level), -60, 1);
+		egg_vumeter_set_peak_db (EGG_VUMETER (window->priv->level), -60, 1);
 	}
 }
 
@@ -1940,26 +1943,28 @@ level_message_handler_cb (GstBus * bus, 
 
     if (g_str_equal (name, "level")) {
       gint channels;
-      gdouble peak_dB;
-      gdouble myind;
-      const GValue *list;
+      gdouble peak_dB, dB;
+      const GValue *rms_list;
+      const GValue *peak_list;
       const GValue *value;
 
       gint i;
       /* we can get the number of channels as the length of any of the value
        * lists */
 
-      list = gst_structure_get_value (s, "rms");
-      channels = gst_value_list_get_size (list);
+      rms_list = gst_structure_get_value (s, "rms");
+      peak_list = gst_structure_get_value (s, "peak");
+      channels = gst_value_list_get_size (rms_list);
+      egg_vumeter_set_channels (EGG_VUMETER (window->priv->level), channels);
 
       for (i = 0; i < channels; ++i) {
-	list = gst_structure_get_value (s, "peak");
-        value = gst_value_list_get_value (list, i);
-        peak_dB = g_value_get_double (value);
-	myind = exp (peak_dB / 20);
-	if (myind > 1.0)
-		myind = 1.0;
-	gtk_progress_set_percentage (GTK_PROGRESS (window->priv->level), myind);
+	value = gst_value_list_get_value (rms_list, i);
+	dB = g_value_get_double (value);
+	egg_vumeter_set_db (EGG_VUMETER (window->priv->level), dB, i);
+
+	value = gst_value_list_get_value (peak_list, i);
+	peak_dB = g_value_get_double (value);
+	egg_vumeter_set_peak_db (EGG_VUMETER (window->priv->level), peak_dB, i);
       }
     }
   }
@@ -2233,7 +2238,6 @@ gsr_window_init (GSRWindow *window)
 	GtkWidget *hbox;
 	GtkWidget *label;
 	GtkWidget *table;
-	GtkWidget *align;
 	GtkWidget *frame;
 	gchar *id;
 	gchar *path;
@@ -2459,7 +2463,6 @@ gsr_window_init (GSRWindow *window)
 
 	/* hack to get the same shadow as the status bar.. */
 	gtk_widget_style_get (GTK_WIDGET (priv->statusbar), "shadow-type", &shadow_type, NULL);
-
 	frame = gtk_frame_new (NULL);
 	gtk_frame_set_shadow_type (GTK_FRAME (frame), shadow_type);
 	gtk_widget_show (frame);
@@ -2474,13 +2477,8 @@ gsr_window_init (GSRWindow *window)
 	gtk_box_pack_start (GTK_BOX (hbox), priv->volume_label, FALSE, TRUE, 0);
 
 	/* initialize priv->level */
-	align = gtk_aspect_frame_new ("", 0.0, 0.0, 20, FALSE);
-	gtk_frame_set_shadow_type (GTK_FRAME (align), GTK_SHADOW_NONE);
-	gtk_widget_show (align);
-	gtk_box_pack_start (GTK_BOX (hbox), align, FALSE, FALSE, 0);
-
-	priv->level = gtk_progress_bar_new ();
-	gtk_container_add (GTK_CONTAINER (align), priv->level);
+	priv->level = egg_vumeter_new ();
+	gtk_box_pack_start (GTK_BOX (hbox), priv->level, FALSE, FALSE, 0);
 
 	gtk_widget_set_sensitive (window->priv->volume_label, FALSE);
 	gtk_widget_set_sensitive (window->priv->level, FALSE);
Index: vumeter.c
===================================================================
--- vumeter.c	(revision 0)
+++ vumeter.c	(revision 0)
@@ -0,0 +1,198 @@
+/**
+ * vumeter.c
+ *
+ * A GTK+ widget that implements a vumeter
+ *
+ * (c) 2008, Frederic Peters
+ *
+ * Authors:
+ *   Frederic Peters  <fpeters@0d.be>
+ */
+
+#include <gtk/gtk.h>
+#include <math.h>
+
+#include "vumeter.h"
+
+G_DEFINE_TYPE (EggVumeter, egg_vumeter, GTK_TYPE_DRAWING_AREA);
+
+static gboolean egg_vumeter_expose (GtkWidget *vumeter, GdkEventExpose *event);
+static void egg_vumeter_size_request (GtkWidget *vumeter, GtkRequisition *requisition);
+static void redraw (GtkWidget *vumeter);
+static void draw (GtkWidget *vumeter, cairo_t *cr);
+
+static void
+egg_vumeter_class_init (EggVumeterClass *class)
+{
+	GtkWidgetClass *widget_class;
+
+	widget_class = GTK_WIDGET_CLASS (class);
+
+	widget_class->expose_event = egg_vumeter_expose;
+	widget_class->size_request = egg_vumeter_size_request;
+}
+
+static void
+egg_vumeter_init (EggVumeter *vumeter)
+{
+	vumeter->dB = g_array_sized_new (FALSE, FALSE, sizeof(double), 2);
+	vumeter->peak_dB = g_array_sized_new (FALSE, FALSE, sizeof(double), 2);
+	vumeter->channels = 0;
+	egg_vumeter_set_channels (vumeter, 1);
+}
+
+void
+egg_vumeter_set_db (EggVumeter *vumeter, gdouble dB, int channel)
+{
+	if (channel >= vumeter->channels)
+		return;
+	g_array_insert_val(vumeter->dB, channel, dB);
+	redraw (GTK_WIDGET(vumeter));
+}
+
+void
+egg_vumeter_set_peak_db (EggVumeter *vumeter, gdouble peak_dB, int channel)
+{
+	if (channel >= vumeter->channels)
+		return;
+	g_array_insert_val(vumeter->peak_dB, channel, peak_dB);
+	redraw (GTK_WIDGET(vumeter));
+}
+
+void
+egg_vumeter_set_channels (EggVumeter *vumeter, int channels)
+{
+	int i;
+	double zero = -60.0;
+
+	if (channels == vumeter->channels)
+		return;
+	if (channels > vumeter->channels) {
+		g_array_set_size(vumeter->dB, channels);
+		g_array_set_size(vumeter->peak_dB, channels);
+		for (i=vumeter->channels; i<channels; i++) {
+			g_array_insert_val(vumeter->dB, i, zero);
+			g_array_insert_val(vumeter->peak_dB, i, zero);
+		}
+	}
+	vumeter->channels = channels;
+	redraw (GTK_WIDGET(vumeter));
+}
+
+
+static void
+redraw (GtkWidget *vumeter)
+{
+	GtkWidget *widget;
+	GdkRegion *region;
+	
+	if (!vumeter->window) return;
+
+	region = gdk_drawable_get_clip_region (vumeter->window);
+
+	/* redraw the cairo canvas completely by exposing it */
+	gdk_window_invalidate_region (vumeter->window, region, TRUE);
+	gdk_window_process_updates (vumeter->window, TRUE);
+
+	gdk_region_destroy (region);
+}
+
+static void
+draw (GtkWidget *vumeter, cairo_t *cr)
+{
+	int width, height;
+	int led_width, led_height;
+	int i, j;
+	gdouble peak_t;
+	gdouble rms_t;
+	int rms_ti, peak_ti;
+	int nb_leds = 20;
+
+	width = vumeter->allocation.width;
+	height = vumeter->allocation.height;
+	led_width = width / nb_leds;
+	led_height = height / EGG_VUMETER(vumeter)->channels;
+	
+	for (j=0; j<EGG_VUMETER(vumeter)->channels; j++) {
+
+		if (GTK_WIDGET_SENSITIVE(vumeter)) {
+			peak_t = exp (g_array_index(EGG_VUMETER(vumeter)->peak_dB, double, j) / 20);
+			if (peak_t > 1.0)
+				peak_t = 1.0;
+			peak_ti = peak_t*nb_leds;
+			rms_t = exp (g_array_index(EGG_VUMETER(vumeter)->dB, double, j) / 20);
+			if (rms_t > 1.0)
+				rms_t = 1.0;
+			rms_ti = rms_t*nb_leds;
+		} else {
+			rms_ti = 0;
+			peak_ti = 0;
+		}
+
+		for (i=0; i<nb_leds; i++) {
+			double tint = 0.3;
+			cairo_rectangle (cr, led_width*i, led_height*j,
+					led_width-2, led_height);
+			if (rms_ti > i) {
+				tint = 1.0;
+			}
+			if (i > (nb_leds*0.85)) {
+				cairo_set_source_rgb (cr, tint, 0, 0); /* red */
+			} else if (i > (nb_leds*0.7)) {
+				cairo_set_source_rgb (cr, tint, tint, 0); /* yellow */
+			} else {
+				cairo_set_source_rgb (cr, 0, tint, 0); /* green */
+			}
+			cairo_fill (cr);
+			if (rms_ti < i && peak_ti == i) {
+				/* peak hold bar */
+				if (i > (nb_leds*0.85)) {
+					cairo_set_source_rgb (cr, 1, 0, 0); /* red */
+				} else if (i > (nb_leds*0.7)) {
+					cairo_set_source_rgb (cr, 1, 1, 0); /* yellow */
+				} else {
+					cairo_set_source_rgb (cr, 0, 1, 0); /* green */
+				}
+				cairo_rectangle (cr,
+						led_width*(i+1)-2-(led_width/2)-1,
+						led_height*j,
+						2, led_height);
+				cairo_fill (cr);
+			}
+		}
+	}
+}
+
+static gboolean
+egg_vumeter_expose (GtkWidget *vumeter, GdkEventExpose *event)
+{
+	cairo_t *cr;
+
+	/* get a cairo_t */
+	cr = gdk_cairo_create (vumeter->window);
+
+	cairo_rectangle (cr,
+			event->area.x, event->area.y,
+			event->area.width, event->area.height);
+	cairo_clip (cr);
+	
+	draw (vumeter, cr);
+
+	cairo_destroy (cr);
+
+	return FALSE;
+}
+
+
+static void
+egg_vumeter_size_request (GtkWidget *vumeter, GtkRequisition *requisition)
+{
+	requisition->height = 20;
+	requisition->width = 240;
+}
+
+GtkWidget *
+egg_vumeter_new (void)
+{
+	return g_object_new (EGG_TYPE_VUMETER, NULL);
+}
Index: Makefile.am
===================================================================
--- Makefile.am	(revision 3897)
+++ Makefile.am	(working copy)
@@ -16,7 +16,9 @@ bin_PROGRAMS = gnome-sound-recorder
 gnome_sound_recorder_SOURCES =	\
 	gnome-recorder.c	\
 	gsr-window.c		\
-	gsr-window.h
+	gsr-window.h		\
+	vumeter.c		\
+	vumeter.h
 
 uidir = $(datadir)/gnome-sound-recorder/ui
 ui_DATA = ui.xml
Index: vumeter.h
===================================================================
--- vumeter.h	(revision 0)
+++ vumeter.h	(revision 0)
@@ -0,0 +1,51 @@
+/**
+ * vumeter.h
+ *
+ * A GTK+ widget that implements a vumeter
+ *
+ * (c) 2008, Frederic Peters
+ *
+ * Authors:
+ *   Frederic Peters  <fpeters@0d.be>
+ */
+
+#ifndef __EGG_VUMETER_H__
+#define __EGG_VUMETER_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_VUMETER		(egg_vumeter_get_type ())
+#define EGG_VUMETER(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_VUMETER, EggVumeter))
+#define EGG_VUMETER_CLASS(obj)	(G_TYPE_CHECK_CLASS_CAST ((obj), EGG_VUMETER, EggVumeterClass))
+#define EGG_IS_VUMETER(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_VUMETER))
+#define EGG_IS_VUMETER_CLASS(obj)	(G_TYPE_CHECK_CLASS_TYPE ((obj), EFF_TYPE_VUMETER))
+#define EGG_VUMETER_GET_CLASS	(G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_VUMETER, EggVumeterClass))
+
+typedef struct _EggVumeter		EggVumeter;
+typedef struct _EggVumeterClass	EggVumeterClass;
+
+struct _EggVumeter
+{
+	GtkDrawingArea parent;
+
+	/* < private > */
+	GArray *dB;
+	GArray *peak_dB;
+	int channels;
+};
+
+struct _EggVumeterClass
+{
+	GtkDrawingAreaClass parent_class;
+};
+
+GtkWidget *egg_vumeter_new (void);
+void egg_vumeter_set_channels (EggVumeter *vumeter, int channels);
+void egg_vumeter_set_db (EggVumeter *vumeter, gdouble dB, int channel);
+void egg_vumeter_set_peak_db (EggVumeter *vumeter, gdouble peak_dB, int channel);
+
+G_END_DECLS
+
+#endif
