add VU and IEC meter DSP (from jmeters)
[ardour.git] / gtk2_ardour / canvas-waveview.c
index b78bf63ea712aa37c18954e44c7adaf7c4288836..2080b208d8a2eb6f839cc0657bf3c72e58d6a98c 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
-     Copyright (C) 2000-2002 Paul Davis 
+     Copyright (C) 2000-2002 Paul Davis
 
      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
 
      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
 #include <stdio.h>
 #include <math.h>
 #include <libgnomecanvas/libgnomecanvas.h>
 #include <stdio.h>
 #include <math.h>
 #include <libgnomecanvas/libgnomecanvas.h>
+#include <cairo.h>
 #include <string.h>
 #include <limits.h>
 #include <string.h>
 #include <limits.h>
+#include <unistd.h>
 
 
-#include <ardour/dB.h>
+#include "ardour/dB.h"
 
 #include "logmeter.h"
 #include "canvas-waveview.h"
 #include "rgb_macros.h"
 
 
 #include "logmeter.h"
 #include "canvas-waveview.h"
 #include "rgb_macros.h"
 
+/* POSIX guarantees casting between void* and function pointers, ISO C doesn't
+ * We can work around warnings by going one step deeper in our casts
+ */
+#ifdef _POSIX_VERSION
+#define POSIX_FUNC_PTR_CAST(type, object) *((type*) &(object))
+#endif // _POSIX_VERSION
 
 
-extern void c_stacktrace();
+extern void c_stacktrace(void);
 
 enum {
         PROP_0,
 
 enum {
         PROP_0,
@@ -52,7 +60,10 @@ enum {
         PROP_WAVE_COLOR,
         PROP_CLIP_COLOR,
         PROP_ZERO_COLOR,
         PROP_WAVE_COLOR,
         PROP_CLIP_COLOR,
         PROP_ZERO_COLOR,
+        PROP_FILL_COLOR,
+        PROP_FILLED,
         PROP_RECTIFIED,
         PROP_RECTIFIED,
+        PROP_ZERO_LINE,
         PROP_REGION_START,
         PROP_LOGSCALED,
 };
         PROP_REGION_START,
         PROP_LOGSCALED,
 };
@@ -106,10 +117,13 @@ static void gnome_canvas_waveview_set_data_src   (GnomeCanvasWaveView *,
 static void gnome_canvas_waveview_set_channel    (GnomeCanvasWaveView *,
                                                   guint32);
 
 static void gnome_canvas_waveview_set_channel    (GnomeCanvasWaveView *,
                                                   guint32);
 
-static gint32 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview,
+static guint32 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview,
                                                   gulong               start_sample,
                                                   gulong               end_sample);
 
                                                   gulong               start_sample,
                                                   gulong               end_sample);
 
+
+static int _gradient_rendering = 0;
+
 static GnomeCanvasItemClass *parent_class;
 
 GType
 static GnomeCanvasItemClass *parent_class;
 
 GType
@@ -159,113 +173,127 @@ gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class)
                  PROP_DATA_SRC,
                  g_param_spec_pointer ("data_src", NULL, NULL,
                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
                  PROP_DATA_SRC,
                  g_param_spec_pointer ("data_src", NULL, NULL,
                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_CHANNEL,
                  g_param_spec_uint ("channel", NULL, NULL,
                                     0, G_MAXUINT, 0,
                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_CHANNEL,
                  g_param_spec_uint ("channel", NULL, NULL,
                                     0, G_MAXUINT, 0,
                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_LENGTH_FUNCTION,
                  g_param_spec_pointer ("length_function", NULL, NULL,
                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_LENGTH_FUNCTION,
                  g_param_spec_pointer ("length_function", NULL, NULL,
                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_SOURCEFILE_LENGTH_FUNCTION,
                  g_param_spec_pointer ("sourcefile_length_function", NULL, NULL,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_SOURCEFILE_LENGTH_FUNCTION,
                  g_param_spec_pointer ("sourcefile_length_function", NULL, NULL,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_PEAK_FUNCTION,
                  g_param_spec_pointer ("peak_function", NULL, NULL,
                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_PEAK_FUNCTION,
                  g_param_spec_pointer ("peak_function", NULL, NULL,
                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_GAIN_FUNCTION,
                  g_param_spec_pointer ("gain_function", NULL, NULL,
                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_GAIN_FUNCTION,
                  g_param_spec_pointer ("gain_function", NULL, NULL,
                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_GAIN_SRC,
                  g_param_spec_pointer ("gain_src", NULL, NULL,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_GAIN_SRC,
                  g_param_spec_pointer ("gain_src", NULL, NULL,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-       
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_CACHE,
                  g_param_spec_pointer ("cache", NULL, NULL,
                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_CACHE,
                  g_param_spec_pointer ("cache", NULL, NULL,
                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_CACHE_UPDATER,
                  g_param_spec_boolean ("cache_updater", NULL, NULL,
                                       FALSE,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_CACHE_UPDATER,
                  g_param_spec_boolean ("cache_updater", NULL, NULL,
                                       FALSE,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_SAMPLES_PER_UNIT,
                  g_param_spec_double ("samples_per_unit", NULL, NULL,
                                       0.0, G_MAXDOUBLE, 0.0,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_SAMPLES_PER_UNIT,
                  g_param_spec_double ("samples_per_unit", NULL, NULL,
                                       0.0, G_MAXDOUBLE, 0.0,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_AMPLITUDE_ABOVE_AXIS,
                  g_param_spec_double ("amplitude_above_axis", NULL, NULL,
                                       0.0, G_MAXDOUBLE, 0.0,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_AMPLITUDE_ABOVE_AXIS,
                  g_param_spec_double ("amplitude_above_axis", NULL, NULL,
                                       0.0, G_MAXDOUBLE, 0.0,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_X,
                  g_param_spec_double ("x", NULL, NULL,
                                       0.0, G_MAXDOUBLE, 0.0,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_X,
                  g_param_spec_double ("x", NULL, NULL,
                                       0.0, G_MAXDOUBLE, 0.0,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_Y,
                  g_param_spec_double ("y", NULL, NULL,
                                       0.0, G_MAXDOUBLE, 0.0,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_Y,
                  g_param_spec_double ("y", NULL, NULL,
                                       0.0, G_MAXDOUBLE, 0.0,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_HEIGHT,
                  g_param_spec_double ("height", NULL, NULL,
                                       0.0, G_MAXDOUBLE, 0.0,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_HEIGHT,
                  g_param_spec_double ("height", NULL, NULL,
                                       0.0, G_MAXDOUBLE, 0.0,
                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_WAVE_COLOR,
                  g_param_spec_uint ("wave_color", NULL, NULL,
                                     0, G_MAXUINT, 0,
                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_WAVE_COLOR,
                  g_param_spec_uint ("wave_color", NULL, NULL,
                                     0, G_MAXUINT, 0,
                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_CLIP_COLOR,
                  g_param_spec_uint ("clip_color", NULL, NULL,
                                     0, G_MAXUINT, 0,
                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_CLIP_COLOR,
                  g_param_spec_uint ("clip_color", NULL, NULL,
                                     0, G_MAXUINT, 0,
                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_ZERO_COLOR,
                  g_param_spec_uint ("zero_color", NULL, NULL,
                                     0, G_MAXUINT, 0,
                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_ZERO_COLOR,
                  g_param_spec_uint ("zero_color", NULL, NULL,
                                     0, G_MAXUINT, 0,
                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR,
+                 g_param_spec_uint ("fill_color", NULL, NULL,
+                                    0, G_MAXUINT, 0,
+                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILLED,
+                 g_param_spec_boolean ("filled", NULL, NULL,
+                                       FALSE,
+                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_RECTIFIED,
         g_object_class_install_property
                 (gobject_class,
                  PROP_RECTIFIED,
@@ -273,22 +301,29 @@ gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class)
                                        FALSE,
                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
 
                                        FALSE,
                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
 
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_ZERO_LINE,
+                 g_param_spec_boolean ("zero_line", NULL, NULL,
+                                       FALSE,
+                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_LOGSCALED,
                  g_param_spec_boolean ("logscaled", NULL, NULL,
                                        FALSE,
                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_LOGSCALED,
                  g_param_spec_boolean ("logscaled", NULL, NULL,
                                        FALSE,
                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         g_object_class_install_property
                 (gobject_class,
                  PROP_REGION_START,
                  g_param_spec_uint ("region_start", NULL, NULL,
                                     0, G_MAXUINT, 0,
                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
         g_object_class_install_property
                 (gobject_class,
                  PROP_REGION_START,
                  g_param_spec_uint ("region_start", NULL, NULL,
                                     0, G_MAXUINT, 0,
                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
-        
+
         object_class->destroy = gnome_canvas_waveview_destroy;
         object_class->destroy = gnome_canvas_waveview_destroy;
-        
+
         item_class->update = gnome_canvas_waveview_update;
         item_class->bounds = gnome_canvas_waveview_bounds;
         item_class->point = gnome_canvas_waveview_point;
         item_class->update = gnome_canvas_waveview_update;
         item_class->bounds = gnome_canvas_waveview_bounds;
         item_class->point = gnome_canvas_waveview_point;
@@ -296,8 +331,14 @@ gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class)
         item_class->draw = gnome_canvas_waveview_draw;
 }
 
         item_class->draw = gnome_canvas_waveview_draw;
 }
 
+void 
+gnome_canvas_waveview_set_gradient_waveforms (int yn)
+{
+       _gradient_rendering = yn;
+}
+
 GnomeCanvasWaveViewCache*
 GnomeCanvasWaveViewCache*
-gnome_canvas_waveview_cache_new ()
+gnome_canvas_waveview_cache_new (void)
 {
        GnomeCanvasWaveViewCache *c;
 
 {
        GnomeCanvasWaveViewCache *c;
 
@@ -335,6 +376,8 @@ gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview)
        waveview->gain_src = NULL;
        waveview->rectified = FALSE;
        waveview->logscaled = FALSE;
        waveview->gain_src = NULL;
        waveview->rectified = FALSE;
        waveview->logscaled = FALSE;
+       waveview->filled = TRUE;
+       waveview->zero_line = FALSE;
        waveview->region_start = 0;
        waveview->samples_per_unit = 1.0;
        waveview->amplitude_above_axis = 1.0;
        waveview->region_start = 0;
        waveview->samples_per_unit = 1.0;
        waveview->amplitude_above_axis = 1.0;
@@ -342,21 +385,18 @@ gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview)
        waveview->screen_width = gdk_screen_width ();
        waveview->reload_cache_in_render = FALSE;
 
        waveview->screen_width = gdk_screen_width ();
        waveview->reload_cache_in_render = FALSE;
 
-       waveview->wave_color = RGBA_TO_UINT(44,35,126,255);
-       waveview->clip_color = RGBA_TO_UINT(44,0,0,100);
-       waveview->zero_color = RGBA_TO_UINT(44,0,128,100);
+       waveview->wave_color = 0;
+       waveview->clip_color = 0;
+       waveview->zero_color = 0;
+       waveview->fill_color = 0;
 }
 
 static void
 gnome_canvas_waveview_destroy (GtkObject *object)
 {
 }
 
 static void
 gnome_canvas_waveview_destroy (GtkObject *object)
 {
-       GnomeCanvasWaveView *waveview;
-
        g_return_if_fail (object != NULL);
        g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
 
        g_return_if_fail (object != NULL);
        g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
 
-       waveview = GNOME_CANVAS_WAVEVIEW (object);
-
        if (GTK_OBJECT_CLASS (parent_class)->destroy)
                (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
 }
        if (GTK_OBJECT_CLASS (parent_class)->destroy)
                (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
 }
@@ -364,7 +404,8 @@ gnome_canvas_waveview_destroy (GtkObject *object)
 #define DEBUG_CACHE 0
 #undef CACHE_MEMMOVE_OPTIMIZATION
 
 #define DEBUG_CACHE 0
 #undef CACHE_MEMMOVE_OPTIMIZATION
 
-static gint32
+/** @return cache index of start_sample within the cache */
+static guint32
 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_sample, gulong end_sample)
 {
        gulong required_cache_entries;
 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_sample, gulong end_sample)
 {
        gulong required_cache_entries;
@@ -389,12 +430,12 @@ gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_
 #if DEBUG_CACHE
        // printf("waveview->region_start == %lu\n",waveview->region_start);
        // c_stacktrace ();
 #if DEBUG_CACHE
        // printf("waveview->region_start == %lu\n",waveview->region_start);
        // c_stacktrace ();
-       printf ("\n\n=> 0x%x cache @ 0x%x range: %lu - %lu request: %lu - %lu (%lu frames)\n", 
+       printf ("\n\n=> 0x%x cache @ 0x%x range: %lu - %lu request: %lu - %lu (%lu frames)\n",
                waveview, cache,
                cache->start, cache->end,
                start_sample, end_sample, end_sample - start_sample);
 #endif
                waveview, cache,
                cache->start, cache->end,
                start_sample, end_sample, end_sample - start_sample);
 #endif
-               
+
        if (cache->start <= start_sample && cache->end >= end_sample) {
 #if DEBUG_CACHE
                // printf ("0x%x: cache hit for %lu-%lu (cache holds: %lu-%lu\n",
        if (cache->start <= start_sample && cache->end >= end_sample) {
 #if DEBUG_CACHE
                // printf ("0x%x: cache hit for %lu-%lu (cache holds: %lu-%lu\n",
@@ -404,13 +445,13 @@ gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_
        }
 
        /* make sure the cache is at least twice as wide as the screen width, and put the start sample
        }
 
        /* make sure the cache is at least twice as wide as the screen width, and put the start sample
-          in the middle, ensuring that we cover the end_sample. 
+          in the middle, ensuring that we cover the end_sample.
        */
 
        /* Note the assumption that we have a 1:1 units:pixel ratio for the canvas. Its everywhere ... */
        */
 
        /* Note the assumption that we have a 1:1 units:pixel ratio for the canvas. Its everywhere ... */
-       
+
        half_width = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit)/2.0 + 0.5);
        half_width = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit)/2.0 + 0.5);
-       
+
        if (start_sample < half_width) {
                new_cache_start = 0;
        } else {
        if (start_sample < half_width) {
                new_cache_start = 0;
        } else {
@@ -433,7 +474,7 @@ gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_
        }
 
 #if DEBUG_CACHE
        }
 
 #if DEBUG_CACHE
-       fprintf (stderr, "AVAILABLE FRAMES = %lu of %lu, start = %lu, sstart = %lu, cstart = %lu\n", 
+       fprintf (stderr, "AVAILABLE FRAMES = %lu of %lu, start = %lu, sstart = %lu, cstart = %lu\n",
                 rf3, waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit),
                 waveview->region_start, start_sample, new_cache_start);
 #endif
                 rf3, waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit),
                 waveview->region_start, start_sample, new_cache_start);
 #endif
@@ -460,7 +501,7 @@ gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_
        ostart = new_cache_start;
 
 #ifdef CACHE_MEMMOVE_OPTIMIZATION
        ostart = new_cache_start;
 
 #ifdef CACHE_MEMMOVE_OPTIMIZATION
-       
+
        /* data is not entirely in the cache, so go fetch it, making sure to fill the cache */
 
        /* some of the required cache entries are in the cache, but in the wrong
        /* data is not entirely in the cache, so go fetch it, making sure to fill the cache */
 
        /* some of the required cache entries are in the cache, but in the wrong
@@ -468,24 +509,24 @@ gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_
        */
 
        if (cache->start < new_cache_start && new_cache_start < cache->end) {
        */
 
        if (cache->start < new_cache_start && new_cache_start < cache->end) {
-               
-               /* case one: the common area is at the end of the existing cache. move it 
+
+               /* case one: the common area is at the end of the existing cache. move it
                   to the beginning of the cache, and set up to refill whatever remains.
                   to the beginning of the cache, and set up to refill whatever remains.
-                  
-                  
+
+
                           wv->cache_start                                        wv->cache_end
                           |-------------------------------------------------------| cache
                                                               |--------------------------------| requested
                                                               <------------------->
                                                                     "present"
                           wv->cache_start                                        wv->cache_end
                           |-------------------------------------------------------| cache
                                                               |--------------------------------| requested
                                                               <------------------->
                                                                     "present"
-                                                           new_cache_start                      new_cache_end       
+                                                           new_cache_start                      new_cache_end
                */
                */
-                               
+
 
                present_frames = cache->end - new_cache_start;
                present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
 
 
                present_frames = cache->end - new_cache_start;
                present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
 
-#if DEBUG_CACHE                
+#if DEBUG_CACHE
                fprintf (stderr, "existing material at end of current cache, move to start of new cache\n"
                         "\tcopy from %lu to start\n", cache->data_size - present_entries);
 #endif
                fprintf (stderr, "existing material at end of current cache, move to start of new cache\n"
                         "\tcopy from %lu to start\n", cache->data_size - present_entries);
 #endif
@@ -493,7 +534,7 @@ gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_
                memmove (&cache->data[0],
                         &cache->data[cache->data_size - present_entries],
                         present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
                memmove (&cache->data[0],
                         &cache->data[cache->data_size - present_entries],
                         present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
-               
+
 #if DEBUG_CACHE
                fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
                         present_frames, required_frames, present_entries, new_cache_start + present_entries,
 #if DEBUG_CACHE
                fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
                         present_frames, required_frames, present_entries, new_cache_start + present_entries,
@@ -507,8 +548,8 @@ gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_
 
        } else if (new_cache_end > cache->start && new_cache_end < cache->end) {
 
 
        } else if (new_cache_end > cache->start && new_cache_end < cache->end) {
 
-               /* case two: the common area lives at the beginning of the existing cache. 
-                  
+               /* case two: the common area lives at the beginning of the existing cache.
+
                                             wv->cache_start                                      wv->cache_end
                                             |-----------------------------------------------------|
                               |--------------------------------|
                                             wv->cache_start                                      wv->cache_end
                                             |-----------------------------------------------------|
                               |--------------------------------|
@@ -517,15 +558,15 @@ gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_
 
                              new_cache_start                      new_cache_end
                */
 
                              new_cache_start                      new_cache_end
                */
-               
+
                present_frames = new_cache_end - cache->start;
                present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
 
                memmove (&cache->data[cache->data_size - present_entries],
                         &cache->data[0],
                         present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
                present_frames = new_cache_end - cache->start;
                present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
 
                memmove (&cache->data[cache->data_size - present_entries],
                         &cache->data[0],
                         present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
-               
-#if DEBUG_CACHE                
+
+#if DEBUG_CACHE
                fprintf (stderr, "existing material at start of current cache, move to start of end cache\n");
 #endif
 
                fprintf (stderr, "existing material at start of current cache, move to start of end cache\n");
 #endif
 
@@ -539,7 +580,7 @@ gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_
                offset = 0;
                required_frames -= present_frames;
 
                offset = 0;
                required_frames -= present_frames;
 
-               
+
        } else {
                copied = 0;
                offset = 0;
        } else {
                copied = 0;
                offset = 0;
@@ -576,7 +617,7 @@ gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_
                waveview->peak_function (waveview->data_src, npeaks, new_cache_start, required_frames, cache->data + offset, waveview->channel,waveview->samples_per_unit);
 
                /* take into account any copied peaks */
                waveview->peak_function (waveview->data_src, npeaks, new_cache_start, required_frames, cache->data + offset, waveview->channel,waveview->samples_per_unit);
 
                /* take into account any copied peaks */
-               
+
                npeaks += copied;
        } else {
                npeaks = copied;
                npeaks += copied;
        } else {
                npeaks = copied;
@@ -605,27 +646,27 @@ gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_
                }
 
                free (gain);
                }
 
                free (gain);
-       
+
        }
 
        /* do optional log scaling.  this implementation is not particularly efficient */
        }
 
        /* do optional log scaling.  this implementation is not particularly efficient */
-       
+
        if (waveview->logscaled) {
                guint32 n;
                GnomeCanvasWaveViewCacheEntry* buf = cache->data;
        if (waveview->logscaled) {
                guint32 n;
                GnomeCanvasWaveViewCacheEntry* buf = cache->data;
-               
+
                for (n = 0; n < cache->data_size; ++n) {
 
                        if (buf[n].max > 0.0f) {
                for (n = 0; n < cache->data_size; ++n) {
 
                        if (buf[n].max > 0.0f) {
-                               buf[n].max = alt_log_meter(coefficient_to_dB(buf[n].max));
+                               buf[n].max = alt_log_meter(fast_coefficient_to_dB(buf[n].max));
                        } else if (buf[n].max < 0.0f) {
                        } else if (buf[n].max < 0.0f) {
-                               buf[n].max = -alt_log_meter(coefficient_to_dB(-buf[n].max));
+                               buf[n].max = -alt_log_meter(fast_coefficient_to_dB(-buf[n].max));
                        }
                        }
-                       
+
                        if (buf[n].min > 0.0f) {
                        if (buf[n].min > 0.0f) {
-                               buf[n].min = alt_log_meter(coefficient_to_dB(buf[n].min));
+                               buf[n].min = alt_log_meter(fast_coefficient_to_dB(buf[n].min));
                        } else if (buf[n].min < 0.0f) {
                        } else if (buf[n].min < 0.0f) {
-                               buf[n].min = -alt_log_meter(coefficient_to_dB(-buf[n].min));
+                               buf[n].min = -alt_log_meter(fast_coefficient_to_dB(-buf[n].min));
                        }
                }
        }
                        }
                }
        }
@@ -635,10 +676,10 @@ gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_
 
   out:
 #if DEBUG_CACHE
 
   out:
 #if DEBUG_CACHE
-       fprintf (stderr, "return cache index = %d\n", 
-                (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5));
+       fprintf (stderr, "return cache index = %d\n",
+                (guint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5));
 #endif
 #endif
-       return (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5);
+       return (guint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5);
 
 }
 
 
 }
 
@@ -651,7 +692,7 @@ gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *waveview, void *data_sr
                        waveview->reload_cache_in_render = TRUE;
                        return;
                }
                        waveview->reload_cache_in_render = TRUE;
                        return;
                }
-       
+
                waveview->cache->start  = 0;
                waveview->cache->end = 0;
        }
                waveview->cache->start  = 0;
                waveview->cache->end = 0;
        }
@@ -665,11 +706,11 @@ gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *waveview, guint32 chan)
        if (waveview->channel == chan) {
                return;
        }
        if (waveview->channel == chan) {
                return;
        }
-       
+
        waveview->channel = chan;
 }
 
        waveview->channel = chan;
 }
 
-static void 
+static void
 gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
 
 {
 gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
 
 {
@@ -698,8 +739,8 @@ gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
        gnome_canvas_update_bbox (item, Ix1, Iy1, Ix2, Iy2);
 }
 
        gnome_canvas_update_bbox (item, Ix1, Iy1, Ix2, Iy2);
 }
 
-/* 
- * CANVAS CALLBACKS 
+/*
+ * CANVAS CALLBACKS
  */
 
 static void
  */
 
 static void
@@ -709,6 +750,8 @@ gnome_canvas_waveview_set_property (GObject      *object,
                                    GParamSpec   *pspec)
 
 {
                                    GParamSpec   *pspec)
 
 {
+       (void) pspec;
+
        GnomeCanvasItem *item;
        GnomeCanvasWaveView *waveview;
        int redraw = FALSE;
        GnomeCanvasItem *item;
        GnomeCanvasWaveView *waveview;
        int redraw = FALSE;
@@ -720,6 +763,7 @@ gnome_canvas_waveview_set_property (GObject      *object,
        item = GNOME_CANVAS_ITEM (object);
        waveview = GNOME_CANVAS_WAVEVIEW (object);
 
        item = GNOME_CANVAS_ITEM (object);
        waveview = GNOME_CANVAS_WAVEVIEW (object);
 
+       void * ptr;
        switch (prop_id) {
        case PROP_DATA_SRC:
                gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
        switch (prop_id) {
        case PROP_DATA_SRC:
                gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
@@ -732,22 +776,27 @@ gnome_canvas_waveview_set_property (GObject      *object,
                break;
 
        case PROP_LENGTH_FUNCTION:
                break;
 
        case PROP_LENGTH_FUNCTION:
-               waveview->length_function = g_value_get_pointer(value);
+               ptr = g_value_get_pointer(value);
+               waveview->length_function = POSIX_FUNC_PTR_CAST(waveview_length_function_t, ptr);
                redraw = TRUE;
                break;
                redraw = TRUE;
                break;
+
        case PROP_SOURCEFILE_LENGTH_FUNCTION:
        case PROP_SOURCEFILE_LENGTH_FUNCTION:
-               waveview->sourcefile_length_function = g_value_get_pointer(value);
+               ptr = g_value_get_pointer(value);
+               waveview->sourcefile_length_function = POSIX_FUNC_PTR_CAST(waveview_sourcefile_length_function_t, ptr);
                redraw = TRUE;
                break;
 
        case PROP_PEAK_FUNCTION:
                redraw = TRUE;
                break;
 
        case PROP_PEAK_FUNCTION:
-               waveview->peak_function = g_value_get_pointer(value);
+               ptr = g_value_get_pointer(value);
+               waveview->peak_function = POSIX_FUNC_PTR_CAST(waveview_peak_function_t, ptr);
                redraw = TRUE;
                break;
 
        case PROP_GAIN_FUNCTION:
                redraw = TRUE;
                break;
 
        case PROP_GAIN_FUNCTION:
-               waveview->gain_curve_function = g_value_get_pointer(value);
-               redraw = TRUE;
+               ptr = g_value_get_pointer(value);
+               waveview->gain_curve_function = POSIX_FUNC_PTR_CAST(waveview_gain_curve_function_t, ptr);
+                        redraw = TRUE;
                break;
 
        case PROP_GAIN_SRC:
                break;
 
        case PROP_GAIN_SRC:
@@ -830,12 +879,34 @@ gnome_canvas_waveview_set_property (GObject      *object,
                }
                break;
 
                }
                break;
 
+       case PROP_FILL_COLOR:
+               if (waveview->fill_color != g_value_get_uint(value)) {
+                       waveview->fill_color = g_value_get_uint(value);
+                       redraw = TRUE;
+               }
+               break;
+
+       case PROP_FILLED:
+               if (waveview->filled != g_value_get_boolean(value)) {
+                       waveview->filled = g_value_get_boolean(value);
+                       redraw = TRUE;
+               }
+               break;
+
        case PROP_RECTIFIED:
                if (waveview->rectified != g_value_get_boolean(value)) {
                        waveview->rectified = g_value_get_boolean(value);
                        redraw = TRUE;
                }
                break;
        case PROP_RECTIFIED:
                if (waveview->rectified != g_value_get_boolean(value)) {
                        waveview->rectified = g_value_get_boolean(value);
                        redraw = TRUE;
                }
                break;
+
+       case PROP_ZERO_LINE:
+               if (waveview->zero_line != g_value_get_boolean(value)) {
+                       waveview->zero_line = g_value_get_boolean(value);
+                       redraw = TRUE;
+               }
+               break;
+
        case PROP_LOGSCALED:
                if (waveview->logscaled != g_value_get_boolean(value)) {
                        waveview->logscaled = g_value_get_boolean(value);
        case PROP_LOGSCALED:
                if (waveview->logscaled != g_value_get_boolean(value)) {
                        waveview->logscaled = g_value_get_boolean(value);
@@ -869,61 +940,62 @@ gnome_canvas_waveview_set_property (GObject      *object,
 }
 
 static void
 }
 
 static void
-gnome_canvas_waveview_get_property (GObject      *object,
-                                   guint         prop_id,
-                                   GValue       *value,
-                                   GParamSpec   *pspec)
+gnome_canvas_waveview_get_property (
+               GObject      *object,
+               guint         prop_id,
+               GValue       *value,
+               GParamSpec   *pspec)
 {
 {
-       
-   
+
+
        g_return_if_fail (object != NULL);
        g_return_if_fail (object != NULL);
-        g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
+       g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
 
        GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
 
        switch (prop_id) {
        case PROP_DATA_SRC:
 
        GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
 
        switch (prop_id) {
        case PROP_DATA_SRC:
-               g_value_set_pointer(value, waveview->data_src);
+               g_value_set_pointer(value, waveview->data_src);
                break;
 
        case PROP_CHANNEL:
                break;
 
        case PROP_CHANNEL:
-               g_value_set_uint(value, waveview->channel);
+               g_value_set_uint(value, waveview->channel);
                break;
 
        case PROP_LENGTH_FUNCTION:
                break;
 
        case PROP_LENGTH_FUNCTION:
-               g_value_set_pointer(value, waveview->length_function);
+               g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->length_function));
                break;
 
        case PROP_SOURCEFILE_LENGTH_FUNCTION:
                break;
 
        case PROP_SOURCEFILE_LENGTH_FUNCTION:
-               g_value_set_pointer(value, waveview->sourcefile_length_function);
+               g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->sourcefile_length_function));
                break;
 
        case PROP_PEAK_FUNCTION:
                break;
 
        case PROP_PEAK_FUNCTION:
-               g_value_set_pointer(value, waveview->peak_function);
+               g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->peak_function));
                break;
 
        case PROP_GAIN_FUNCTION:
                break;
 
        case PROP_GAIN_FUNCTION:
-               g_value_set_pointer(value, waveview->gain_curve_function);
+               g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->gain_curve_function));
                break;
 
        case PROP_GAIN_SRC:
                break;
 
        case PROP_GAIN_SRC:
-               g_value_set_pointer(value, waveview->gain_src);
+               g_value_set_pointer(value, waveview->gain_src);
                break;
 
        case PROP_CACHE:
                break;
 
        case PROP_CACHE:
-               g_value_set_pointer(value, waveview->cache);
+               g_value_set_pointer(value, waveview->cache);
                break;
 
        case PROP_CACHE_UPDATER:
                break;
 
        case PROP_CACHE_UPDATER:
-               g_value_set_boolean(value, waveview->cache_updater);
+               g_value_set_boolean(value, waveview->cache_updater);
                break;
 
        case PROP_SAMPLES_PER_UNIT:
                break;
 
        case PROP_SAMPLES_PER_UNIT:
-               g_value_set_double(value, waveview->samples_per_unit);
+               g_value_set_double(value, waveview->samples_per_unit);
                break;
 
        case PROP_AMPLITUDE_ABOVE_AXIS:
                break;
 
        case PROP_AMPLITUDE_ABOVE_AXIS:
-               g_value_set_double(value, waveview->amplitude_above_axis);
+               g_value_set_double(value, waveview->amplitude_above_axis);
                break;
 
        case PROP_X:
                break;
 
        case PROP_X:
@@ -950,10 +1022,22 @@ gnome_canvas_waveview_get_property (GObject      *object,
                g_value_set_uint (value, waveview->zero_color);
                break;
 
                g_value_set_uint (value, waveview->zero_color);
                break;
 
+       case PROP_FILL_COLOR:
+               g_value_set_uint (value, waveview->fill_color);
+               break;
+
+       case PROP_FILLED:
+               g_value_set_boolean (value, waveview->filled);
+               break;
+
        case PROP_RECTIFIED:
                g_value_set_boolean (value, waveview->rectified);
                break;
 
        case PROP_RECTIFIED:
                g_value_set_boolean (value, waveview->rectified);
                break;
 
+       case PROP_ZERO_LINE:
+               g_value_set_boolean (value, waveview->zero_line);
+               break;
+
        case PROP_LOGSCALED:
                g_value_set_boolean (value, waveview->logscaled);
                break;
        case PROP_LOGSCALED:
                g_value_set_boolean (value, waveview->logscaled);
                break;
@@ -1012,22 +1096,24 @@ gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *cli
                      &waveview->wave_a);
        UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
                      &waveview->clip_a);
                      &waveview->wave_a);
        UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
                      &waveview->clip_a);
+       UINT_TO_RGBA (waveview->fill_color, &waveview->fill_r, &waveview->fill_g, &waveview->fill_b,
+                     &waveview->fill_a);
 
 //     check_cache (waveview, "end of update");
 }
 
 static void
 
 //     check_cache (waveview, "end of update");
 }
 
 static void
-gnome_canvas_waveview_render (GnomeCanvasItem *item,
-                           GnomeCanvasBuf *buf)
+gnome_canvas_waveview_gradient_render (GnomeCanvasItem *item,
+                                      GnomeCanvasBuf *buf)
 {
        GnomeCanvasWaveView *waveview;
        gulong s1, s2;
        int clip_length = 0;
        int pymin, pymax;
 {
        GnomeCanvasWaveView *waveview;
        gulong s1, s2;
        int clip_length = 0;
        int pymin, pymax;
-       int cache_index;
+       guint cache_index;
        double half_height;
        double half_height;
-       int x, end, begin;
-       int zbegin, zend;
+       int x;
+       char rectify;
 
        waveview = GNOME_CANVAS_WAVEVIEW (item);
 
 
        waveview = GNOME_CANVAS_WAVEVIEW (item);
 
@@ -1042,31 +1128,29 @@ gnome_canvas_waveview_render (GnomeCanvasItem *item,
                buf->is_bg = FALSE;
        }
 
                buf->is_bg = FALSE;
        }
 
-       begin = MAX(waveview->bbox_ulx, buf->rect.x0);
+       /* a "unit" means a pixel */
 
 
-       if (begin == waveview->bbox_ulx) {
-               zbegin = begin + 1;
-       } else {
-               zbegin = begin;
-       }
+       /* begin: render start x (units) */
+       int const begin = MAX (waveview->bbox_ulx, buf->rect.x0);
 
 
-       if (waveview->bbox_lrx >= 0) {
-               end = MIN(waveview->bbox_lrx,buf->rect.x1);
-       } else {
-               end = buf->rect.x1;
-       }
+        /* zbegin: start x for zero line (units) */
+       int const zbegin = (begin == waveview->bbox_ulx) ? (begin + 1) : begin;
 
 
-       if (end == waveview->bbox_lrx) {
-               zend = end - 1;
-       } else {
-               zend = end;
-       }
+       /* end: render end x (units) */
+       int const end = (waveview->bbox_lrx >= 0) ? MIN (waveview->bbox_lrx,buf->rect.x1) : buf->rect.x1;
+
+       /* zend: end x for zero-line (units) */
+       int const zend = (end == waveview->bbox_lrx) ? (end - 1) : end;
 
        if (begin == end) {
                return;
        }
 
 
        if (begin == end) {
                return;
        }
 
-       s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit) ;
+       /* s1: start sample
+          s2: end sample
+       */
+
+       s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit);
 
        // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
 
 
        // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
 
@@ -1107,11 +1191,19 @@ gnome_canvas_waveview_render (GnomeCanvasItem *item,
                waveview->reload_cache_in_render = FALSE;
        }
 
                waveview->reload_cache_in_render = FALSE;
        }
 
-       cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
-
 //     check_cache (waveview, "post-ensure");
 
 //     check_cache (waveview, "post-ensure");
 
-       /* 
+       /* don't rectify at single-sample zoom */
+       if (waveview->rectified && waveview->samples_per_unit > 1) {
+               rectify = TRUE;
+       }
+       else {
+               rectify = FALSE;
+       }
+
+       clip_length = MIN(5,(waveview->height/4));
+
+       /*
           Now draw each line, clipping it appropriately. The clipping
           is done by the macros PAINT_FOO().
        */
           Now draw each line, clipping it appropriately. The clipping
           is done by the macros PAINT_FOO().
        */
@@ -1119,141 +1211,1237 @@ gnome_canvas_waveview_render (GnomeCanvasItem *item,
        half_height = waveview->half_height;
 
 /* this makes it slightly easier to comprehend whats going on */
        half_height = waveview->half_height;
 
 /* this makes it slightly easier to comprehend whats going on */
-
 #define origin half_height
 
 #define origin half_height
 
+       if (waveview->filled && !rectify) {
+               int prev_pymin = 1;
+               int prev_pymax = 0;
+               int last_pymin = 1;
+               int last_pymax = 0;
+               int next_pymin, next_pymax;
+               double max, min;
+               int next_clip_max = 0;
+               int next_clip_min = 0;
+
+               int wave_middle = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
+               int wave_top = (int) rint ((item->y1) * item->canvas->pixels_per_unit);
 
 
-       for (x = begin; x < end; x++) {
+               if (s1 < waveview->samples_per_unit) {
+                       /* we haven't got a prev vars to compare with, so outline the whole line here */
+                       prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
+                       prev_pymin = prev_pymax;
+               }
+               else {
+                       s1 -= waveview->samples_per_unit;
+               }
 
 
-               double max, min;
-               int clip_max, clip_min;
-               
-               clip_max = 0;
-               clip_min = 0;
+               if(end == waveview->bbox_lrx) {
+                       /* we don't have the NEXT vars for the last sample */
+                       last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
+                       last_pymin = last_pymax;
+               }
+               else {
+                       s2 += waveview->samples_per_unit;
+               }
+
+               cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
+
+               /*
+                * Compute the variables outside the rendering rect
+                */
+               if(prev_pymax != prev_pymin) {
+
+                       prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
+                       prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
+                       ++cache_index;
+               }
+               if(last_pymax != last_pymin) {
+                       /* take the index of one sample right of what we render */
+                       guint index = cache_index + (end - begin);
+
+                       if (index >= waveview->cache->data_size) {
+
+                               /* the data we want is off the end of the cache, which must mean its beyond
+                                  the end of the region's source; hence the peak values are 0 */
+                               last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
+                               last_pymin = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
 
 
+                       } else {
+
+                               last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
+                               last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
+
+                       }
+
+               }
+
+               /*
+                * initialize NEXT* variables for the first run, duplicated in the loop for speed
+                */
                max = waveview->cache->data[cache_index].max;
                min = waveview->cache->data[cache_index].min;
                max = waveview->cache->data[cache_index].max;
                min = waveview->cache->data[cache_index].min;
-               
+
                if (max >= 1.0) {
                        max = 1.0;
                if (max >= 1.0) {
                        max = 1.0;
-                       clip_max = 1;
+                       next_clip_max = 1;
                }
 
                if (min <= -1.0) {
                        min = -1.0;
                }
 
                if (min <= -1.0) {
                        min = -1.0;
-                       clip_min = 1;
+                       next_clip_min = 1;
+               }
+
+               max *= half_height;
+               min *= half_height;
+
+               next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
+               next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
+
+               /*
+                * And now the loop
+                */
+               for(x = begin; x < end; ++x) {
+                       int clip_max = next_clip_max;
+                       int clip_min = next_clip_min;
+                       int fill_max, fill_min;
+
+                       pymax = next_pymax;
+                       pymin = next_pymin;
+
+                       /* compute next */
+                       if(x == end - 1) {
+                               /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
+                               next_pymax = last_pymax;
+                               next_pymin = last_pymin;
+                       }
+                       else {
+                               ++cache_index;
+
+                               if (cache_index < waveview->cache->data_size) {
+                                       max = waveview->cache->data[cache_index].max;
+                                       min = waveview->cache->data[cache_index].min;
+                               } else {
+                                       max = min = 0;
+                               }
+
+                               next_clip_max = 0;
+                               next_clip_min = 0;
+
+                               if (max >= 1.0) {
+                                       max = 1.0;
+                                       next_clip_max = 1;
+                               }
+
+                               if (min <= -1.0) {
+                                       min = -1.0;
+                                       next_clip_min = 1;
+                               }
+
+                               max *= half_height;
+                               min *= half_height;
+
+                               next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
+                               next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
+                       }
+
+                       /* render */
+                       if (pymax == pymin) {
+                               PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
+                       } else {
+                               if((prev_pymax < pymax && next_pymax < pymax) ||
+                                  (prev_pymax == pymax && next_pymax == pymax)) {
+                                       fill_max = pymax + 1;
+                                       PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
+                               }
+                               else {
+                                       fill_max = MAX(prev_pymax, next_pymax);
+                                       if(pymax == fill_max) {
+                                               PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
+                                               ++fill_max;
+                                       } else {
+                                               PAINT_VERTA_GR(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max, wave_middle, wave_top);
+                                       }
+                                       
+                               }
+
+                               if((prev_pymin > pymin && next_pymin > pymin) ||
+                                  (prev_pymin == pymin && next_pymin == pymin)) {
+                                       fill_min = pymin - 1;
+                                       PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
+                               }
+                               else {
+                                       fill_min = MIN(prev_pymin, next_pymin);
+                                       if(pymin == fill_min) {
+                                               PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
+                                       }
+                                       else {
+                                               PAINT_VERTA_GR(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin, wave_middle, wave_top);
+                                       }
+                               }
+
+                               if(fill_max < fill_min) {
+                                       PAINT_VERTA_GR(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min, wave_middle, wave_top);
+                               }
+                               else if(fill_max == fill_min) {
+                                       PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
+                               }
+                       }
+
+                       if (clip_max) {
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax + clip_length);
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x + 1, pymax, pymax + (clip_length -1));
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x - 1, pymax, pymax + (clip_length - 1));
+
+                       }
+
+                       if (clip_min) {
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a , x, pymin - clip_length, pymin);
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x + 1, pymin - (clip_length - 1), pymin);
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x - 1, pymin - (clip_length - 1), pymin);
+                       }
+
+                       prev_pymax = pymax;
+                       prev_pymin = pymin;
+               }
+
+       } else if (waveview->filled && rectify) {
+
+               int prev_pymax = -1;
+               int last_pymax = -1;
+               int next_pymax;
+               double max, min;
+               int next_clip_max = 0;
+               int next_clip_min = 0;
+
+               int wave_middle = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
+               int wave_top = (int) rint ((item->y1) * item->canvas->pixels_per_unit);
+
+               // for rectified, this stays constant throughout the loop
+               pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
+
+               if(s1 < waveview->samples_per_unit) {
+                       /* we haven't got a prev vars to compare with, so outline the whole line here */
+                       prev_pymax = pymin;
+               }
+               else {
+                       s1 -= waveview->samples_per_unit;
+               }
+
+               if(end == waveview->bbox_lrx) {
+                       /* we don't have the NEXT vars for the last sample */
+                       last_pymax = pymin;
+               }
+               else {
+                       s2 += waveview->samples_per_unit;
                }
 
                }
 
-               /* don't rectify at single-sample zoom */
+               cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
 
 
-               if (waveview->rectified && waveview->samples_per_unit > 1) {
+               /*
+                * Compute the variables outside the rendering rect
+                */
+               if(prev_pymax < 0) {
+                       max = MIN(waveview->cache->data[cache_index].max, 1.0);
+                       min = MAX(waveview->cache->data[cache_index].min, -1.0);
 
                        if (fabs (min) > fabs (max)) {
                                max = fabs (min);
 
                        if (fabs (min) > fabs (max)) {
                                max = fabs (min);
-                       } 
+                       }
 
 
-                       max = max * waveview->height;
+                       prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
+                       ++cache_index;
+               }
+               if(last_pymax < 0) {
+                       /* take the index of one sample right of what we render */
+                       int index = cache_index + (end - begin);
 
 
-                       pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
-                       pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
+                       max = MIN(waveview->cache->data[index].max, 1.0);
+                       min = MAX(waveview->cache->data[index].min, -1.0);
 
 
-               } else {
-                       
-                       max = max * half_height;
-                       min = min * half_height;
-                       
-                       pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
-                       pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
+                       if (fabs (min) > fabs (max)) {
+                               max = fabs (min);
+                       }
+
+                       last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
                }
 
                }
 
-               /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
-                  or, if samples_per_unit == 1, then a dot at each location.
-               */
+               /*
+                * initialize NEXT* variables for the first run, duplicated in the loop for speed
+                */
+               max = waveview->cache->data[cache_index].max;
+               min = waveview->cache->data[cache_index].min;
 
 
-               if (pymax == pymin) {
-                       PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
-               } else {
-                       PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
+               if (max >= 1.0) {
+                       max = 1.0;
+                       next_clip_max = 1;
                }
                }
-               
-               /* show clipped waveforms with small red lines */
 
 
-               if (clip_max || clip_min) {
-                       clip_length = MIN(5,(waveview->height/4));
+               if (min <= -1.0) {
+                       min = -1.0;
+                       next_clip_min = 1;
                }
 
                }
 
-               if (clip_max) {
-                       PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
+               if (fabs (min) > fabs (max)) {
+                       max = fabs (min);
                }
 
                }
 
-               if (clip_min) {
-                       PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
+               next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
+
+               /*
+                * And now the loop
+                */
+               for(x = begin; x < end; ++x) {
+                       int clip_max = next_clip_max;
+                       int clip_min = next_clip_min;
+                       int fill_max;
+
+                       pymax = next_pymax;
+
+                       /* compute next */
+                       if(x == end - 1) {
+                               /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
+                               next_pymax = last_pymax;
+                       }
+                       else {
+                               ++cache_index;
+
+                               max = waveview->cache->data[cache_index].max;
+                               min = waveview->cache->data[cache_index].min;
+
+                               if (max >= 1.0) {
+                                       max = 1.0;
+                                       next_clip_max = 1;
+                               }
+
+                               if (min <= -1.0) {
+                                       min = -1.0;
+                                       next_clip_min = 1;
+                               }
+
+                               if (fabs (min) > fabs (max)) {
+                                       max = fabs (min);
+                               }
+
+                               next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
+                       }
+
+                       /* render */
+                       if (pymax == pymin) {
+                               PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
+                       } else {
+                               if((prev_pymax < pymax && next_pymax < pymax) ||
+                                  (prev_pymax == pymax && next_pymax == pymax)) {
+                                       fill_max = pymax + 1;
+                                       PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
+                               }
+                               else {
+                                       fill_max = MAX(prev_pymax, next_pymax);
+                                       if(pymax == fill_max) {
+                                               PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
+                                               ++fill_max;
+                                       }
+                                       else {
+                                               PAINT_VERTA_GR(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max, wave_middle, wave_top);
+                                       }
+                               }
+
+                               if(fill_max < pymin) {
+                                       PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
+                               }
+                               else if(fill_max == pymin) {
+                                       PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
+                               }
+                       }
+
+                       if (clip_max) {
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax + clip_length);
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x + 1, pymax, pymax + (clip_length -1));
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x - 1, pymax, pymax + (clip_length - 1));
+                       }
+
+                       if (clip_min) { 
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a , x, pymin - clip_length, pymin);
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x + 1, pymin - (clip_length - 1), pymin);
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x - 1, pymin - (clip_length - 1), pymin);
+                       }
+
+                       prev_pymax = pymax;
                }
                }
+       }
+       else {
+               cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
 
 
-               /* presto, we're done */
-               
-               cache_index++;
+               for (x = begin; x < end; x++) {
+
+                       double max, min;
+                       int clip_max, clip_min;
+
+                       clip_max = 0;
+                       clip_min = 0;
+
+                       max = waveview->cache->data[cache_index].max;
+                       min = waveview->cache->data[cache_index].min;
+
+                       if (max >= 1.0) {
+                               max = 1.0;
+                               clip_max = 1;
+                       }
+
+                       if (min <= -1.0) {
+                               min = -1.0;
+                               clip_min = 1;
+                       }
+
+                       if (rectify) {
+
+                               if (fabs (min) > fabs (max)) {
+                                       max = fabs (min);
+                               }
+
+                               max = max * waveview->height;
+
+                               pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
+                               pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
+
+                       } else {
+
+                               max = max * half_height;
+                               min = min * half_height;
+
+                               pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
+                               pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
+                       }
+
+                       /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
+                          or, if samples_per_unit == 1, then a dot at each location.
+                       */
+
+                       if (pymax == pymin) {
+                               PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
+                       } else {
+                               PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
+                       }
+
+                       /* show clipped waveforms with small red lines */
+
+                       if (clip_max) {
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
+                       }
+
+                       if (clip_min) {
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
+                       }
+
+                       /* presto, we're done */
+
+                       cache_index++;
+               }
        }
 
        }
 
-       if (!waveview->rectified) {
+       if (!waveview->rectified && waveview->zero_line && waveview->height >= 100) {
                // Paint zeroline.
                // Paint zeroline.
-               //PAINT_HORIZA(buf, waveview->zero_r, waveview->zero_g, waveview->zero_b, waveview->zero_a, begin, endi-1, origin );
-               
+
                unsigned char zero_r, zero_g, zero_b, zero_a;
                unsigned char zero_r, zero_g, zero_b, zero_a;
-               UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a );
+               UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a);
                int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
                int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
-               PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, end, zeroline_y);
+               PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, zend, zeroline_y);
        }
 #undef origin
 
 }
 
 static void
        }
 #undef origin
 
 }
 
 static void
-gnome_canvas_waveview_draw (GnomeCanvasItem *item,
-                         GdkDrawable *drawable,
-                         int x, int y,
-                         int width, int height)
+gnome_canvas_waveview_flat_render (GnomeCanvasItem *item,
+                                  GnomeCanvasBuf *buf)
 {
        GnomeCanvasWaveView *waveview;
 {
        GnomeCanvasWaveView *waveview;
+       gulong s1, s2;
+       int clip_length = 0;
+       int pymin, pymax;
+       guint cache_index;
+       double half_height;
+       int x;
+       char rectify;
 
        waveview = GNOME_CANVAS_WAVEVIEW (item);
 
 
        waveview = GNOME_CANVAS_WAVEVIEW (item);
 
-       if (parent_class->draw) {
-               (* parent_class->draw) (item, drawable, x, y, width, height);
+//     check_cache (waveview, "start of render");
+
+       if (parent_class->render) {
+               (*parent_class->render) (item, buf);
        }
 
        }
 
-       fprintf (stderr, "please don't use the CanvasWaveView item in a non-aa Canvas\n");
-       abort ();
-}
+       if (buf->is_bg) {
+               gnome_canvas_buf_ensure_buf (buf);
+               buf->is_bg = FALSE;
+       }
 
 
-static void
-gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
-{
-       GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
+       /* a "unit" means a pixel */
 
 
-       *x1 = waveview->x;
-       *y1 = waveview->y;
+       /* begin: render start x (units) */
+       int const begin = MAX (waveview->bbox_ulx, buf->rect.x0);
 
 
-       *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
-       *y2 = *y1 + waveview->height;
+        /* zbegin: start x for zero line (units) */
+       int const zbegin = (begin == waveview->bbox_ulx) ? (begin + 1) : begin;
 
 
-#if 0
-       x = 0; y = 0;
-       gnome_canvas_item_i2w (item, &x, &y);
-       gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
-       x = *x2;
-       y = *y2;
-       gnome_canvas_item_i2w (item, &x, &y);
-       gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
+       /* end: render end x (units) */
+       int const end = (waveview->bbox_lrx >= 0) ? MIN (waveview->bbox_lrx,buf->rect.x1) : buf->rect.x1;
+
+       /* zend: end x for zero-line (units) */
+       int const zend = (end == waveview->bbox_lrx) ? (end - 1) : end;
+
+       if (begin == end) {
+               return;
+       }
+
+       /* s1: start sample
+          s2: end sample
+       */
+
+       s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit);
+
+       // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
+
+       if (end == waveview->bbox_lrx) {
+               /* This avoids minor rounding errors when we have the
+                  entire region visible.
+               */
+               s2 = waveview->samples;
+       } else {
+               s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
+       }
+
+#if 0
+       printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
+               " b/e %d..%d s= %lu..%lu @ %f\n",
+               waveview,
+               buf->rect.x0,
+               buf->rect.x1,
+               buf->rect.y0,
+               buf->rect.y1,
+               waveview->bbox_ulx,
+               waveview->bbox_lrx,
+               waveview->bbox_uly,
+               waveview->bbox_lry,
+               begin, end, s1, s2,
+               waveview->samples_per_unit);
+#endif
+
+       /* now ensure that the cache is full and properly
+          positioned.
+       */
+
+//     check_cache (waveview, "pre-ensure");
+
+       if (waveview->cache_updater && waveview->reload_cache_in_render) {
+               waveview->cache->start = 0;
+               waveview->cache->end = 0;
+               waveview->reload_cache_in_render = FALSE;
+       }
+
+//     check_cache (waveview, "post-ensure");
+
+       /* don't rectify at single-sample zoom */
+       if (waveview->rectified && waveview->samples_per_unit > 1) {
+               rectify = TRUE;
+       }
+       else {
+               rectify = FALSE;
+       }
+
+       clip_length = MIN(5,(waveview->height/4));
+
+       /*
+          Now draw each line, clipping it appropriately. The clipping
+          is done by the macros PAINT_FOO().
+       */
+
+       half_height = waveview->half_height;
+
+/* this makes it slightly easier to comprehend whats going on */
+#define origin half_height
+
+       if (waveview->filled && !rectify) {
+               int prev_pymin = 1;
+               int prev_pymax = 0;
+               int last_pymin = 1;
+               int last_pymax = 0;
+               int next_pymin, next_pymax;
+               double max, min;
+               int next_clip_max = 0;
+               int next_clip_min = 0;
+
+               if (s1 < waveview->samples_per_unit) {
+                       /* we haven't got a prev vars to compare with, so outline the whole line here */
+                       prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
+                       prev_pymin = prev_pymax;
+               }
+               else {
+                       s1 -= waveview->samples_per_unit;
+               }
+
+               if(end == waveview->bbox_lrx) {
+                       /* we don't have the NEXT vars for the last sample */
+                       last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
+                       last_pymin = last_pymax;
+               }
+               else {
+                       s2 += waveview->samples_per_unit;
+               }
+
+               cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
+
+               /*
+                * Compute the variables outside the rendering rect
+                */
+               if(prev_pymax != prev_pymin) {
+
+                       prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
+                       prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
+                       ++cache_index;
+               }
+               if(last_pymax != last_pymin) {
+                       /* take the index of one sample right of what we render */
+                       guint index = cache_index + (end - begin);
+
+                       if (index >= waveview->cache->data_size) {
+
+                               /* the data we want is off the end of the cache, which must mean its beyond
+                                  the end of the region's source; hence the peak values are 0 */
+                               last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
+                               last_pymin = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
+
+                       } else {
+
+                               last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
+                               last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
+
+                       }
+
+               }
+
+               /*
+                * initialize NEXT* variables for the first run, duplicated in the loop for speed
+                */
+               max = waveview->cache->data[cache_index].max;
+               min = waveview->cache->data[cache_index].min;
+
+               if (max >= 1.0) {
+                       max = 1.0;
+                       next_clip_max = 1;
+               }
+
+               if (min <= -1.0) {
+                       min = -1.0;
+                       next_clip_min = 1;
+               }
+
+               max *= half_height;
+               min *= half_height;
+
+               next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
+               next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
+
+               /*
+                * And now the loop
+                */
+               for(x = begin; x < end; ++x) {
+                       int clip_max = next_clip_max;
+                       int clip_min = next_clip_min;
+                       int fill_max, fill_min;
+
+                       pymax = next_pymax;
+                       pymin = next_pymin;
+
+                       /* compute next */
+                       if(x == end - 1) {
+                               /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
+                               next_pymax = last_pymax;
+                               next_pymin = last_pymin;
+                       }
+                       else {
+                               ++cache_index;
+
+                               if (cache_index < waveview->cache->data_size) {
+                                       max = waveview->cache->data[cache_index].max;
+                                       min = waveview->cache->data[cache_index].min;
+                               } else {
+                                       max = min = 0;
+                               }
+
+                               next_clip_max = 0;
+                               next_clip_min = 0;
+
+                               if (max >= 1.0) {
+                                       max = 1.0;
+                                       next_clip_max = 1;
+                               }
+
+                               if (min <= -1.0) {
+                                       min = -1.0;
+                                       next_clip_min = 1;
+                               }
+
+                               max *= half_height;
+                               min *= half_height;
+
+                               next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
+                               next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
+                       }
+
+                       /* render */
+                       if (pymax == pymin) {
+                               PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
+                       } else {
+                               if((prev_pymax < pymax && next_pymax < pymax) ||
+                                  (prev_pymax == pymax && next_pymax == pymax)) {
+                                       fill_max = pymax + 1;
+                                       PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
+                               }
+                               else {
+                                       fill_max = MAX(prev_pymax, next_pymax);
+                                       if(pymax == fill_max) {
+                                               PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
+                                               ++fill_max;
+                                       }
+                                       else {
+                                               PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
+                                       }
+                               }
+
+                               if((prev_pymin > pymin && next_pymin > pymin) ||
+                                  (prev_pymin == pymin && next_pymin == pymin)) {
+                                       fill_min = pymin - 1;
+                                       PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
+                               }
+                               else {
+                                       fill_min = MIN(prev_pymin, next_pymin);
+                                       if(pymin == fill_min) {
+                                               PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
+                                       }
+                                       else {
+                                               PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin);
+                                       }
+                               }
+
+                               if(fill_max < fill_min) {
+                                       PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min);
+                               }
+                               else if(fill_max == fill_min) {
+                                       PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
+                               }
+                       }
+
+                       if (clip_max) {
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
+                       }
+
+                       if (clip_min) {
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
+                       }
+
+                       prev_pymax = pymax;
+                       prev_pymin = pymin;
+               }
+
+       } else if (waveview->filled && rectify) {
+
+               int prev_pymax = -1;
+               int last_pymax = -1;
+               int next_pymax;
+               double max, min;
+               int next_clip_max = 0;
+               int next_clip_min = 0;
+
+               // for rectified, this stays constant throughout the loop
+               pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
+
+               if(s1 < waveview->samples_per_unit) {
+                       /* we haven't got a prev vars to compare with, so outline the whole line here */
+                       prev_pymax = pymin;
+               }
+               else {
+                       s1 -= waveview->samples_per_unit;
+               }
+
+               if(end == waveview->bbox_lrx) {
+                       /* we don't have the NEXT vars for the last sample */
+                       last_pymax = pymin;
+               }
+               else {
+                       s2 += waveview->samples_per_unit;
+               }
+
+               cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
+
+               /*
+                * Compute the variables outside the rendering rect
+                */
+               if(prev_pymax < 0) {
+                       max = MIN(waveview->cache->data[cache_index].max, 1.0);
+                       min = MAX(waveview->cache->data[cache_index].min, -1.0);
+
+                       if (fabs (min) > fabs (max)) {
+                               max = fabs (min);
+                       }
+
+                       prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
+                       ++cache_index;
+               }
+               if(last_pymax < 0) {
+                       /* take the index of one sample right of what we render */
+                       int index = cache_index + (end - begin);
+
+                       max = MIN(waveview->cache->data[index].max, 1.0);
+                       min = MAX(waveview->cache->data[index].min, -1.0);
+
+                       if (fabs (min) > fabs (max)) {
+                               max = fabs (min);
+                       }
+
+                       last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
+               }
+
+               /*
+                * initialize NEXT* variables for the first run, duplicated in the loop for speed
+                */
+               max = waveview->cache->data[cache_index].max;
+               min = waveview->cache->data[cache_index].min;
+
+               if (max >= 1.0) {
+                       max = 1.0;
+                       next_clip_max = 1;
+               }
+
+               if (min <= -1.0) {
+                       min = -1.0;
+                       next_clip_min = 1;
+               }
+
+               if (fabs (min) > fabs (max)) {
+                       max = fabs (min);
+               }
+
+               next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
+
+               /*
+                * And now the loop
+                */
+               for(x = begin; x < end; ++x) {
+                       int clip_max = next_clip_max;
+                       int clip_min = next_clip_min;
+                       int fill_max;
+
+                       pymax = next_pymax;
+
+                       /* compute next */
+                       if(x == end - 1) {
+                               /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
+                               next_pymax = last_pymax;
+                       }
+                       else {
+                               ++cache_index;
+
+                               max = waveview->cache->data[cache_index].max;
+                               min = waveview->cache->data[cache_index].min;
+
+                               if (max >= 1.0) {
+                                       max = 1.0;
+                                       next_clip_max = 1;
+                               }
+
+                               if (min <= -1.0) {
+                                       min = -1.0;
+                                       next_clip_min = 1;
+                               }
+
+                               if (fabs (min) > fabs (max)) {
+                                       max = fabs (min);
+                               }
+
+                               next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
+                       }
+
+                       /* render */
+                       if (pymax == pymin) {
+                               PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
+                       } else {
+                               if((prev_pymax < pymax && next_pymax < pymax) ||
+                                  (prev_pymax == pymax && next_pymax == pymax)) {
+                                       fill_max = pymax + 1;
+                                       PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
+                               }
+                               else {
+                                       fill_max = MAX(prev_pymax, next_pymax);
+                                       if(pymax == fill_max) {
+                                               PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
+                                               ++fill_max;
+                                       }
+                                       else {
+                                               PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
+                                       }
+                               }
+
+                               if(fill_max < pymin) {
+                                       PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
+                               }
+                               else if(fill_max == pymin) {
+                                       PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
+                               }
+                       }
+
+                       if (clip_max) {
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
+                       }
+
+                       if (clip_min) {
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
+                       }
+
+                       prev_pymax = pymax;
+               }
+       }
+       else {
+               cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
+
+               for (x = begin; x < end; x++) {
+
+                       double max, min;
+                       int clip_max, clip_min;
+
+                       clip_max = 0;
+                       clip_min = 0;
+
+                       max = waveview->cache->data[cache_index].max;
+                       min = waveview->cache->data[cache_index].min;
+
+                       if (max >= 1.0) {
+                               max = 1.0;
+                               clip_max = 1;
+                       }
+
+                       if (min <= -1.0) {
+                               min = -1.0;
+                               clip_min = 1;
+                       }
+
+                       if (rectify) {
+
+                               if (fabs (min) > fabs (max)) {
+                                       max = fabs (min);
+                               }
+
+                               max = max * waveview->height;
+
+                               pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
+                               pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
+
+                       } else {
+
+                               max = max * half_height;
+                               min = min * half_height;
+
+                               pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
+                               pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
+                       }
+
+                       /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
+                          or, if samples_per_unit == 1, then a dot at each location.
+                       */
+
+                       if (pymax == pymin) {
+                               PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
+                       } else {
+                               PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
+                       }
+
+                       /* show clipped waveforms with small red lines */
+
+                       if (clip_max) {
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
+                       }
+
+                       if (clip_min) {
+                               PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
+                       }
+
+                       /* presto, we're done */
+
+                       cache_index++;
+               }
+       }
+
+       if (!waveview->rectified && waveview->zero_line && waveview->height >= 100) {
+               // Paint zeroline.
+
+               unsigned char zero_r, zero_g, zero_b, zero_a;
+               UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a);
+               int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
+               PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, zend, zeroline_y);
+       }
+#undef origin
+}
+
+static void
+gnome_canvas_waveview_render (GnomeCanvasItem *item,
+                             GnomeCanvasBuf *buf)
+{
+       if (_gradient_rendering) {
+               gnome_canvas_waveview_gradient_render (item, buf);
+       } else {
+               gnome_canvas_waveview_flat_render (item, buf);
+       }
+}
+
+static void
+gnome_canvas_waveview_draw (GnomeCanvasItem *item,
+                           GdkDrawable *drawable,
+                           int x, int y,
+                           int width, int height)
+{
+       GnomeCanvasWaveView *waveview;
+       cairo_t* cr;
+       gulong s1, s2;
+       int cache_index;
+       gboolean rectify;
+       double origin;
+       double xoff;
+       double yoff = 0.0;
+       double ulx;
+       double uly;
+       double lrx;
+       double lry;
+
+       waveview = GNOME_CANVAS_WAVEVIEW (item);
+
+       /* compute intersection of Drawable area and waveview,
+          in canvas coordinate space
+       */
+
+       if (x > waveview->bbox_ulx) {
+               ulx = x;
+       } else {
+               ulx = waveview->bbox_ulx;
+       }
+
+       if (y > waveview->bbox_uly) {
+               uly = y;
+       } else {
+               uly = waveview->bbox_uly;
+       }
+
+       if (x + width > waveview->bbox_lrx) {
+               lrx = waveview->bbox_lrx;
+       } else {
+               lrx = x + width;
+       }
+
+       if (y + height > waveview->bbox_lry) {
+               lry = waveview->bbox_lry;
+       } else {
+               lry = y + height;
+       }
+
+       /* figure out which samples we need for the resulting intersection */
+
+       s1 = floor ((ulx - waveview->bbox_ulx) * waveview->samples_per_unit) ;
+
+       if (lrx == waveview->bbox_lrx) {
+               /* This avoids minor rounding errors when we have the
+                  entire region visible.
+               */
+               s2 = waveview->samples;
+       } else {
+               s2 = s1 + floor ((lrx - ulx) * waveview->samples_per_unit);
+       }
+
+       /* translate back to buffer coordinate space */
+
+       ulx -= x;
+       uly -= y;
+       lrx -= x;
+       lry -= y;
+
+       /* don't rectify at single-sample zoom */
+       if(waveview->rectified && waveview->samples_per_unit > 1.0) {
+               rectify = TRUE;
+       } else {
+               rectify = FALSE;
+       }
+
+       cr = gdk_cairo_create (drawable);
+       cairo_set_line_width (cr, 0.5);
+
+       origin = waveview->bbox_uly - y + waveview->half_height;
+
+       cairo_rectangle (cr, ulx, uly, lrx - ulx, lry - uly);
+       cairo_clip (cr);
+
+       if (waveview->cache_updater && waveview->reload_cache_in_render) {
+               waveview->cache->start = 0;
+               waveview->cache->end = 0;
+               waveview->reload_cache_in_render = FALSE;
+       }
+
+       cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
+
+#if 0
+       printf ("%p r (%d,%d)(%d,%d)[%d x %d] bbox (%d,%d)(%d,%d)[%d x %d]"
+               " draw (%.1f,%.1f)(%.1f,%.1f)[%.1f x %.1f] s= %lu..%lu\n",
+               waveview,
+               x, y,
+               x + width,
+               y + height,
+               width,
+               height,
+               waveview->bbox_ulx,
+               waveview->bbox_uly,
+               waveview->bbox_lrx,
+               waveview->bbox_lry,
+               waveview->bbox_lrx - waveview->bbox_ulx,
+               waveview->bbox_lry - waveview->bbox_uly,
+               ulx, uly,
+               lrx, lry,
+               lrx - ulx,
+               lry - uly,
+               s1, s2);
+#endif
+
+       /* draw the top half */
+
+       for (xoff = ulx; xoff < lrx; xoff++) {
+               double max, min;
+
+               max = waveview->cache->data[cache_index].max;
+               min = waveview->cache->data[cache_index].min;
+
+               if (min <= -1.0) {
+                       min = -1.0;
+               }
+
+               if (max >= 1.0) {
+                       max = 1.0;
+               }
+
+               if (rectify) {
+                       if (fabs (min) > fabs (max)) {
+                               max = fabs (min);
+                       }
+               }
+
+               yoff = origin - (waveview->half_height * max) + 0.5;
+
+               if (xoff == ulx) {
+                       /* first point */
+                       cairo_move_to (cr, xoff+0.5, yoff);
+               } else {
+                       cairo_line_to (cr, xoff+0.5, yoff);
+               }
+
+               cache_index++;
+       }
+
+       /* from the final top point, move out of the clip zone */
+
+       cairo_line_to (cr, xoff + 10, yoff);
+
+       /* now draw the bottom half */
+
+       for (--xoff, --cache_index; xoff >= ulx; --xoff) {
+               double min;
+
+               min = waveview->cache->data[cache_index].min;
+
+               if (min <= -1.0) {
+                       min = -1.0;
+               }
+
+               yoff = origin - (waveview->half_height * min) + 0.5;
+
+               cairo_line_to (cr, xoff+0.5, yoff);
+               cache_index--;
+       }
+
+       /* from the final lower point, move out of the clip zone */
+
+       cairo_line_to (cr, xoff - 10, yoff);
+
+       /* close path to fill */
+
+       cairo_close_path (cr);
+
+       /* fill and stroke */
+
+       cairo_set_source_rgba (cr,
+                              (waveview->fill_r/255.0),
+                              (waveview->fill_g/255.0),
+                              (waveview->fill_b/255.0),
+                              (waveview->fill_a/255.0));
+       cairo_fill_preserve (cr);
+       cairo_set_source_rgba (cr,
+                              (waveview->wave_r/255.0),
+                              (waveview->wave_g/255.0),
+                              (waveview->wave_b/255.0),
+                              (waveview->wave_a/255.0));
+       cairo_stroke (cr);
+
+       cairo_destroy (cr);
+}
+
+#if 0
+               if (clip_max || clip_min) {
+                       cairo_set_source_rgba (cr, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a);
+               }
+
+               if (clip_max) {
+                       cairo_move_to (cr, xoff, yoff1);
+                       cairo_line_to (cr, xoff, yoff1 + clip_length);
+                       cairo_stroke (cr);
+               }
+
+               if (clip_min) {
+                       cairo_move_to (cr, xoff, yoff2);
+                       cairo_line_to (cr, xoff, yoff2 - clip_length);
+                       cairo_stroke (cr);
+               }
+
+#endif
+
+static void
+gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+       GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
+
+       *x1 = waveview->x;
+       *y1 = waveview->y;
+
+       *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
+       *y2 = *y1 + waveview->height;
+
+#if 0
+       x = 0; y = 0;
+       gnome_canvas_item_i2w (item, &x, &y);
+       gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
+       x = *x2;
+       y = *y2;
+       gnome_canvas_item_i2w (item, &x, &y);
+       gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
        printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
        printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
-#endif         
+#endif
 
 }
 
 static double
 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
 {
 
 }
 
 static double
 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
 {
-       /* XXX for now, point is never inside the wave 
+       (void) item;
+       (void) x;
+       (void) y;
+       (void) cx;
+       (void) cy;
+       (void) actual_item;
+
+       /* XXX for now, point is never inside the wave
        GnomeCanvasWaveView *waveview;
        double x1, y1, x2, y2;
        double dx, dy;
        GnomeCanvasWaveView *waveview;
        double x1, y1, x2, y2;
        double dx, dy;
@@ -1271,7 +2459,7 @@ gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx,
        gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
 
        /* Is point inside rectangle */
        gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
 
        /* Is point inside rectangle */
-       
+
        if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
                return 0.0;
        }
        if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
                return 0.0;
        }