fix OSX compilation
[ardour.git] / gtk2_ardour / canvas-waveview.c
1 /*
2      Copyright (C) 2000-2002 Paul Davis
3
4      This program is free software; you can redistribute it and/or modify
5      it under the terms of the GNU General Public License as published by
6      the Free Software Foundation; either version 2 of the License, or
7      (at your option) any later version.
8
9      This program is distributed in the hope that it will be useful,
10      but WITHOUT ANY WARRANTY; without even the implied warranty of
11      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12      GNU General Public License for more details.
13
14      You should have received a copy of the GNU General Public License
15      along with this program; if not, write to the Free Software
16      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18      $Id$
19 */
20
21 #include <stdio.h>
22 #include <math.h>
23 #include <libgnomecanvas/libgnomecanvas.h>
24 #include <cairo.h>
25 #include <string.h>
26 #include <limits.h>
27 #include <unistd.h>
28
29 #include "ardour/dB.h"
30
31 #include "logmeter.h"
32 #include "canvas-waveview.h"
33 #include "rgb_macros.h"
34
35 /* POSIX guarantees casting between void* and function pointers, ISO C doesn't
36  * We can work around warnings by going one step deeper in our casts
37  */
38 #if defined(_POSIX_VERSION) || defined(COMPILER_MINGW)
39 #define POSIX_FUNC_PTR_CAST(type, object) *((type*) &(object))
40 #endif // _POSIX_VERSION
41
42 extern void c_stacktrace(void);
43
44 enum {
45          PROP_0,
46          PROP_DATA_SRC,
47          PROP_CHANNEL,
48          PROP_LENGTH_FUNCTION,
49          PROP_SOURCEFILE_LENGTH_FUNCTION,
50          PROP_PEAK_FUNCTION,
51          PROP_GAIN_FUNCTION,
52          PROP_GAIN_SRC,
53          PROP_CACHE,
54          PROP_CACHE_UPDATER,
55          PROP_SAMPLES_PER_UNIT,
56          PROP_AMPLITUDE_ABOVE_AXIS,
57          PROP_X,
58          PROP_Y,
59          PROP_HEIGHT,
60          PROP_WAVE_COLOR,
61          PROP_CLIP_COLOR,
62          PROP_ZERO_COLOR,
63          PROP_FILL_COLOR,
64          PROP_FILLED,
65          PROP_RECTIFIED,
66          PROP_ZERO_LINE,
67          PROP_REGION_START,
68          PROP_LOGSCALED,
69 };
70
71 static void gnome_canvas_waveview_class_init     (GnomeCanvasWaveViewClass *class);
72
73 static void gnome_canvas_waveview_init           (GnomeCanvasWaveView      *waveview);
74
75 static void gnome_canvas_waveview_destroy        (GtkObject            *object);
76
77 static void gnome_canvas_waveview_set_property   (GObject        *object,
78                                                    guint           prop_id,
79                                                    const GValue   *value,
80                                                    GParamSpec     *pspec);
81 static void gnome_canvas_waveview_get_property   (GObject        *object,
82                                                    guint           prop_id,
83                                                    GValue         *value,
84                                                    GParamSpec     *pspec);
85
86 static void   gnome_canvas_waveview_update       (GnomeCanvasItem *item,
87                                                    double          *affine,
88                                                    ArtSVP          *clip_path,
89                                                    int              flags);
90
91 static void   gnome_canvas_waveview_bounds       (GnomeCanvasItem *item,
92                                                    double          *x1,
93                                                    double          *y1,
94                                                    double          *x2,
95                                                    double          *y2);
96
97 static double gnome_canvas_waveview_point        (GnomeCanvasItem  *item,
98                                                    double            x,
99                                                    double            y,
100                                                    int               cx,
101                                                    int               cy,
102                                                    GnomeCanvasItem **actual_item);
103
104 static void gnome_canvas_waveview_render         (GnomeCanvasItem *item,
105                                                    GnomeCanvasBuf  *buf);
106
107 static void gnome_canvas_waveview_draw           (GnomeCanvasItem *item,
108                                                    GdkDrawable     *drawable,
109                                                    int              x,
110                                                    int              y,
111                                                    int              w,
112                                                    int              h);
113
114 static void gnome_canvas_waveview_set_data_src   (GnomeCanvasWaveView *,
115                                                    void *);
116
117 static void gnome_canvas_waveview_set_channel    (GnomeCanvasWaveView *,
118                                                    guint32);
119
120 static guint32 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview,
121                                                    gulong               start_sample,
122                                                    gulong               end_sample);
123
124
125 static int _gradient_rendering = 0;
126
127 static GnomeCanvasItemClass *parent_class;
128
129 GType
130 gnome_canvas_waveview_get_type (void)
131 {
132          static GType waveview_type;
133
134          if (!waveview_type) {
135                  static const GTypeInfo object_info = {
136                          sizeof (GnomeCanvasWaveViewClass),
137                          (GBaseInitFunc) NULL,
138                          (GBaseFinalizeFunc) NULL,
139                          (GClassInitFunc) gnome_canvas_waveview_class_init,
140                          (GClassFinalizeFunc) NULL,
141                          NULL,                  /* class_data */
142                          sizeof (GnomeCanvasWaveView),
143                          0,                     /* n_preallocs */
144                          (GInstanceInitFunc) gnome_canvas_waveview_init,
145                          NULL                   /* value_table */
146                  };
147
148                  waveview_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasWaveView",
149                                                          &object_info, 0);
150          }
151
152          return waveview_type;
153  }
154
155 static void
156 gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class)
157 {
158          GObjectClass *gobject_class;
159          GtkObjectClass *object_class;
160          GnomeCanvasItemClass *item_class;
161
162          gobject_class = (GObjectClass *) class;
163          object_class = (GtkObjectClass *) class;
164          item_class = (GnomeCanvasItemClass *) class;
165
166          parent_class = g_type_class_peek_parent (class);
167
168          gobject_class->set_property = gnome_canvas_waveview_set_property;
169          gobject_class->get_property = gnome_canvas_waveview_get_property;
170
171          g_object_class_install_property
172                  (gobject_class,
173                   PROP_DATA_SRC,
174                   g_param_spec_pointer ("data_src", NULL, NULL,
175                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
176
177          g_object_class_install_property
178                  (gobject_class,
179                   PROP_CHANNEL,
180                   g_param_spec_uint ("channel", NULL, NULL,
181                                      0, G_MAXUINT, 0,
182                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
183
184          g_object_class_install_property
185                  (gobject_class,
186                   PROP_LENGTH_FUNCTION,
187                   g_param_spec_pointer ("length_function", NULL, NULL,
188                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
189
190          g_object_class_install_property
191                 (gobject_class,
192                  PROP_SOURCEFILE_LENGTH_FUNCTION,
193                  g_param_spec_pointer ("sourcefile_length_function", NULL, NULL,
194                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
195
196          g_object_class_install_property
197                  (gobject_class,
198                   PROP_PEAK_FUNCTION,
199                   g_param_spec_pointer ("peak_function", NULL, NULL,
200                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
201
202          g_object_class_install_property
203                  (gobject_class,
204                   PROP_GAIN_FUNCTION,
205                   g_param_spec_pointer ("gain_function", NULL, NULL,
206                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
207
208          g_object_class_install_property
209                  (gobject_class,
210                  PROP_GAIN_SRC,
211                  g_param_spec_pointer ("gain_src", NULL, NULL,
212                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
213
214          g_object_class_install_property
215                  (gobject_class,
216                   PROP_CACHE,
217                   g_param_spec_pointer ("cache", NULL, NULL,
218                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
219
220          g_object_class_install_property
221                  (gobject_class,
222                   PROP_CACHE_UPDATER,
223                  g_param_spec_boolean ("cache_updater", NULL, NULL,
224                                        FALSE,
225                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
226
227          g_object_class_install_property
228                  (gobject_class,
229                   PROP_SAMPLES_PER_UNIT,
230                   g_param_spec_double ("samples_per_unit", NULL, NULL,
231                                        0.0, G_MAXDOUBLE, 0.0,
232                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
233
234          g_object_class_install_property
235                  (gobject_class,
236                   PROP_AMPLITUDE_ABOVE_AXIS,
237                   g_param_spec_double ("amplitude_above_axis", NULL, NULL,
238                                        0.0, G_MAXDOUBLE, 0.0,
239                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
240
241          g_object_class_install_property
242                  (gobject_class,
243                   PROP_X,
244                   g_param_spec_double ("x", NULL, NULL,
245                                        0.0, G_MAXDOUBLE, 0.0,
246                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
247
248          g_object_class_install_property
249                  (gobject_class,
250                   PROP_Y,
251                   g_param_spec_double ("y", NULL, NULL,
252                                        0.0, G_MAXDOUBLE, 0.0,
253                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
254
255          g_object_class_install_property
256                  (gobject_class,
257                   PROP_HEIGHT,
258                   g_param_spec_double ("height", NULL, NULL,
259                                        0.0, G_MAXDOUBLE, 0.0,
260                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
261
262          g_object_class_install_property
263                  (gobject_class,
264                   PROP_WAVE_COLOR,
265                   g_param_spec_uint ("wave_color", NULL, NULL,
266                                      0, G_MAXUINT, 0,
267                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
268
269          g_object_class_install_property
270                  (gobject_class,
271                   PROP_CLIP_COLOR,
272                   g_param_spec_uint ("clip_color", NULL, NULL,
273                                      0, G_MAXUINT, 0,
274                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
275
276          g_object_class_install_property
277                  (gobject_class,
278                   PROP_ZERO_COLOR,
279                   g_param_spec_uint ("zero_color", NULL, NULL,
280                                      0, G_MAXUINT, 0,
281                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
282
283          g_object_class_install_property
284                  (gobject_class,
285                   PROP_FILL_COLOR,
286                   g_param_spec_uint ("fill_color", NULL, NULL,
287                                      0, G_MAXUINT, 0,
288                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
289
290          g_object_class_install_property
291                  (gobject_class,
292                   PROP_FILLED,
293                   g_param_spec_boolean ("filled", NULL, NULL,
294                                         FALSE,
295                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
296
297          g_object_class_install_property
298                  (gobject_class,
299                   PROP_RECTIFIED,
300                   g_param_spec_boolean ("rectified", NULL, NULL,
301                                         FALSE,
302                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
303
304          g_object_class_install_property
305                  (gobject_class,
306                   PROP_ZERO_LINE,
307                   g_param_spec_boolean ("zero_line", NULL, NULL,
308                                         FALSE,
309                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
310
311          g_object_class_install_property
312                  (gobject_class,
313                   PROP_LOGSCALED,
314                   g_param_spec_boolean ("logscaled", NULL, NULL,
315                                         FALSE,
316                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
317
318          g_object_class_install_property
319                  (gobject_class,
320                   PROP_REGION_START,
321                   g_param_spec_uint ("region_start", NULL, NULL,
322                                      0, G_MAXUINT, 0,
323                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
324
325          object_class->destroy = gnome_canvas_waveview_destroy;
326
327          item_class->update = gnome_canvas_waveview_update;
328          item_class->bounds = gnome_canvas_waveview_bounds;
329          item_class->point = gnome_canvas_waveview_point;
330          item_class->render = gnome_canvas_waveview_render;
331          item_class->draw = gnome_canvas_waveview_draw;
332 }
333
334 void
335 gnome_canvas_waveview_set_gradient_waveforms (int yn)
336 {
337         _gradient_rendering = yn;
338 }
339
340 GnomeCanvasWaveViewCache*
341 gnome_canvas_waveview_cache_new (void)
342 {
343         GnomeCanvasWaveViewCache *c;
344
345         c = g_malloc (sizeof (GnomeCanvasWaveViewCache));
346
347         c->allocated = 2048;
348         c->data = g_malloc (sizeof (GnomeCanvasWaveViewCacheEntry) * c->allocated);
349         c->data_size = 0;
350         c->start = 0;
351         c->end = 0;
352
353         return c;
354 }
355
356 void
357 gnome_canvas_waveview_cache_destroy (GnomeCanvasWaveViewCache* cache)
358 {
359         g_free (cache->data);
360         g_free (cache);
361 }
362
363 static void
364 gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview)
365 {
366         waveview->x = 0.0;
367         waveview->y = 0.0;
368         waveview->cache = 0;
369         waveview->cache_updater = FALSE;
370         waveview->data_src = NULL;
371         waveview->channel = 0;
372         waveview->peak_function = NULL;
373         waveview->length_function = NULL;
374         waveview->sourcefile_length_function = NULL;
375         waveview->gain_curve_function = NULL;
376         waveview->gain_src = NULL;
377         waveview->rectified = FALSE;
378         waveview->logscaled = FALSE;
379         waveview->filled = TRUE;
380         waveview->zero_line = FALSE;
381         waveview->region_start = 0;
382         waveview->samples_per_unit = 1.0;
383         waveview->amplitude_above_axis = 1.0;
384         waveview->height = 100.0;
385         waveview->screen_width = gdk_screen_width ();
386         waveview->reload_cache_in_render = FALSE;
387
388         waveview->wave_color = 0;
389         waveview->clip_color = 0;
390         waveview->zero_color = 0;
391         waveview->fill_color = 0;
392 }
393
394 static void
395 gnome_canvas_waveview_destroy (GtkObject *object)
396 {
397         g_return_if_fail (object != NULL);
398         g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
399
400         if (GTK_OBJECT_CLASS (parent_class)->destroy)
401                 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
402 }
403
404 #define DEBUG_CACHE 0
405 #undef CACHE_MEMMOVE_OPTIMIZATION
406
407 /** @return cache index of start_sample within the cache */
408 static guint32
409 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_sample, gulong end_sample)
410 {
411         gulong required_cache_entries;
412         gulong rf1, rf2,rf3, required_frames;
413         gulong new_cache_start, new_cache_end;
414         gulong half_width;
415         gulong npeaks;
416         gulong offset;
417         gulong ostart;
418         gulong copied;
419         GnomeCanvasWaveViewCache *cache;
420         float* gain;
421 #ifdef CACHE_MEMMOVE_OPTIMIZATION
422         gulong present_frames;
423         gulong present_entries;
424 #endif
425
426         cache = waveview->cache;
427
428         start_sample = start_sample + waveview->region_start;
429         end_sample = end_sample + waveview->region_start;
430 #if DEBUG_CACHE
431         // printf("waveview->region_start == %lu\n",waveview->region_start);
432         // c_stacktrace ();
433         printf ("\n\n=> 0x%x cache @ 0x%x range: %lu - %lu request: %lu - %lu (%lu frames)\n",
434                 waveview, cache,
435                 cache->start, cache->end,
436                 start_sample, end_sample, end_sample - start_sample);
437 #endif
438
439         if (cache->start <= start_sample && cache->end >= end_sample) {
440 #if DEBUG_CACHE
441                 // printf ("0x%x: cache hit for %lu-%lu (cache holds: %lu-%lu\n",
442                 // waveview, start_sample, end_sample, cache->start, cache->end);
443 #endif
444                 goto out;
445         }
446
447         /* make sure the cache is at least twice as wide as the screen width, and put the start sample
448            in the middle, ensuring that we cover the end_sample.
449         */
450
451         /* Note the assumption that we have a 1:1 units:pixel ratio for the canvas. Its everywhere ... */
452
453         half_width = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit)/2.0 + 0.5);
454
455         if (start_sample < half_width) {
456                 new_cache_start = 0;
457         } else {
458                 new_cache_start = start_sample - half_width;
459         }
460
461         /* figure out how many frames we want */
462
463         rf1 = end_sample - start_sample + 1;
464         rf2 = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit * 2.0f));
465         required_frames = MAX(rf1,rf2);
466
467         /* but make sure it doesn't extend beyond the end of the source material */
468
469         rf3 = (gulong) (waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit)) + 1;
470         if (rf3 < new_cache_start) {
471                 rf3 = 0;
472         } else {
473                 rf3 -= new_cache_start;
474         }
475
476 #if DEBUG_CACHE
477         fprintf (stderr, "AVAILABLE FRAMES = %lu of %lu, start = %lu, sstart = %lu, cstart = %lu\n",
478                  rf3, waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit),
479                  waveview->region_start, start_sample, new_cache_start);
480 #endif
481
482         required_frames = MIN(required_frames,rf3);
483
484         new_cache_end = new_cache_start + required_frames - 1;
485
486         required_cache_entries = (gulong) floor (required_frames / waveview->samples_per_unit );
487
488 #if DEBUG_CACHE
489         fprintf (stderr, "new cache = %lu - %lu\n", new_cache_start, new_cache_end);
490         fprintf(stderr,"required_cach_entries = %lu, samples_per_unit = %f req frames = %lu\n",
491                 required_cache_entries,waveview->samples_per_unit, required_frames);
492 #endif
493
494         if (required_cache_entries > cache->allocated) {
495                 cache->data = g_realloc (cache->data, sizeof (GnomeCanvasWaveViewCacheEntry) * required_cache_entries);
496                 cache->allocated = required_cache_entries;
497                 // cache->start = 0;
498                 // cache->end = 0;
499         }
500
501         ostart = new_cache_start;
502
503 #ifdef CACHE_MEMMOVE_OPTIMIZATION
504
505         /* data is not entirely in the cache, so go fetch it, making sure to fill the cache */
506
507         /* some of the required cache entries are in the cache, but in the wrong
508            locations. use memmove to fix this.
509         */
510
511         if (cache->start < new_cache_start && new_cache_start < cache->end) {
512
513                 /* case one: the common area is at the end of the existing cache. move it
514                    to the beginning of the cache, and set up to refill whatever remains.
515
516
517                            wv->cache_start                                        wv->cache_end
518                            |-------------------------------------------------------| cache
519                                                                |--------------------------------| requested
520                                                                <------------------->
521                                                                      "present"
522                                                             new_cache_start                      new_cache_end
523                 */
524
525
526                 present_frames = cache->end - new_cache_start;
527                 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
528
529 #if DEBUG_CACHE
530                 fprintf (stderr, "existing material at end of current cache, move to start of new cache\n"
531                          "\tcopy from %lu to start\n", cache->data_size - present_entries);
532 #endif
533
534                 memmove (&cache->data[0],
535                          &cache->data[cache->data_size - present_entries],
536                          present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
537
538 #if DEBUG_CACHE
539                 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
540                          present_frames, required_frames, present_entries, new_cache_start + present_entries,
541                          cache->data + present_entries);
542 #endif
543
544                 copied = present_entries;
545                 offset = present_entries;
546                 new_cache_start += present_frames;
547                 required_frames -= present_frames;
548
549         } else if (new_cache_end > cache->start && new_cache_end < cache->end) {
550
551                 /* case two: the common area lives at the beginning of the existing cache.
552
553                                             wv->cache_start                                      wv->cache_end
554                                              |-----------------------------------------------------|
555                               |--------------------------------|
556                                              <----------------->
557                                                 "present"
558
559                              new_cache_start                      new_cache_end
560                 */
561
562                 present_frames = new_cache_end - cache->start;
563                 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
564
565                 memmove (&cache->data[cache->data_size - present_entries],
566                          &cache->data[0],
567                          present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
568
569 #if DEBUG_CACHE
570                 fprintf (stderr, "existing material at start of current cache, move to start of end cache\n");
571 #endif
572
573 #if DEBUG_CACHE
574                 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
575                          present_entries, required_frames, present_entries, new_cache_start + present_entries,
576                          cache->data + present_entries);
577 #endif
578
579                 copied = present_entries;
580                 offset = 0;
581                 required_frames -= present_frames;
582
583
584         } else {
585                 copied = 0;
586                 offset = 0;
587
588         }
589
590 #else
591         copied = 0;
592         offset = 0;
593
594 #endif /* CACHE_MEMMOVE_OPTIMIZATION */
595
596 //      fprintf(stderr,"length == %lu\n",waveview->length_function (waveview->data_src));
597 //      required_frames = MIN (waveview->length_function (waveview->data_src) - new_cache_start, required_frames);
598
599         npeaks = (gulong) floor (required_frames / waveview->samples_per_unit);
600         required_frames = npeaks * waveview->samples_per_unit;
601
602 #if DEBUG_CACHE
603
604
605         printf ("requesting %lu/%f to cover %lu-%lu at %f spu (request was %lu-%lu) into cache + %lu\n",
606                 required_frames, required_frames/waveview->samples_per_unit, new_cache_start, new_cache_end,
607                 waveview->samples_per_unit, start_sample, end_sample, offset);
608 #endif
609
610 #if DEBUG_CACHE
611 //      printf ("cache holds %lu entries, requesting %lu to cover %lu-%lu (request was %lu-%lu)\n",
612 //              cache->data_size, npeaks, new_cache_start, new_cache_end,
613 //              start_sample, end_sample);
614 #endif
615
616         if (required_frames) {
617                 waveview->peak_function (waveview->data_src, npeaks, new_cache_start, required_frames, cache->data + offset, waveview->channel,waveview->samples_per_unit);
618
619                 /* take into account any copied peaks */
620
621                 npeaks += copied;
622         } else {
623                 npeaks = copied;
624         }
625
626         if (npeaks < cache->allocated) {
627 #if DEBUG_CACHE
628                 fprintf (stderr, "zero fill cache for %lu at %lu\n", cache->allocated - npeaks, npeaks);
629 #endif
630                 memset (&cache->data[npeaks], 0, sizeof (GnomeCanvasWaveViewCacheEntry) * (cache->allocated - npeaks));
631                 cache->data_size = npeaks;
632         } else {
633                 cache->data_size = cache->allocated;
634         }
635
636         if (waveview->gain_curve_function) {
637                 guint32 n;
638
639                 gain = (float*) malloc (sizeof (float) * cache->data_size);
640
641                 waveview->gain_curve_function (waveview->gain_src, new_cache_start, new_cache_end, gain, cache->data_size);
642
643                 for (n = 0; n < cache->data_size; ++n) {
644                         cache->data[n].min *= gain[n];
645                         cache->data[n].max *= gain[n];
646                 }
647
648                 free (gain);
649
650         }
651
652         /* do optional log scaling.  this implementation is not particularly efficient */
653
654         if (waveview->logscaled) {
655                 guint32 n;
656                 GnomeCanvasWaveViewCacheEntry* buf = cache->data;
657
658                 for (n = 0; n < cache->data_size; ++n) {
659
660                         if (buf[n].max > 0.0f) {
661                                 buf[n].max = alt_log_meter(fast_coefficient_to_dB(buf[n].max));
662                         } else if (buf[n].max < 0.0f) {
663                                 buf[n].max = -alt_log_meter(fast_coefficient_to_dB(-buf[n].max));
664                         }
665
666                         if (buf[n].min > 0.0f) {
667                                 buf[n].min = alt_log_meter(fast_coefficient_to_dB(buf[n].min));
668                         } else if (buf[n].min < 0.0f) {
669                                 buf[n].min = -alt_log_meter(fast_coefficient_to_dB(-buf[n].min));
670                         }
671                 }
672         }
673
674         cache->start = ostart;
675         cache->end = new_cache_end;
676
677   out:
678 #if DEBUG_CACHE
679         fprintf (stderr, "return cache index = %d\n",
680                  (guint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5));
681 #endif
682         return (guint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5);
683
684 }
685
686 void
687 gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *waveview, void *data_src)
688 {
689
690         if (waveview->cache_updater) {
691                 if (waveview->data_src == data_src) {
692                         waveview->reload_cache_in_render = TRUE;
693                         return;
694                 }
695
696                 waveview->cache->start  = 0;
697                 waveview->cache->end = 0;
698         }
699
700         waveview->data_src = data_src;
701 }
702
703 void
704 gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *waveview, guint32 chan)
705 {
706         if (waveview->channel == chan) {
707                 return;
708         }
709
710         waveview->channel = chan;
711 }
712
713 static void
714 gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
715
716 {
717         double x1, x2, y1, y2;
718         ArtPoint i1, i2;
719         ArtPoint w1, w2;
720         int Ix1, Ix2, Iy1, Iy2;
721         double i2w[6];
722
723         gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
724
725         i1.x = x1;
726         i1.y = y1;
727         i2.x = x2;
728         i2.y = y2;
729
730         gnome_canvas_item_i2w_affine (item, i2w);
731         art_affine_point (&w1, &i1, i2w);
732         art_affine_point (&w2, &i2, i2w);
733
734         Ix1 = (int) rint(w1.x);
735         Ix2 = (int) rint(w2.x);
736         Iy1 = (int) rint(w1.y);
737         Iy2 = (int) rint(w2.y);
738
739         gnome_canvas_update_bbox (item, Ix1, Iy1, Ix2, Iy2);
740 }
741
742 /*
743  * CANVAS CALLBACKS
744  */
745
746 static void
747 gnome_canvas_waveview_set_property (GObject      *object,
748                                     guint         prop_id,
749                                     const GValue *value,
750                                     GParamSpec   *pspec)
751
752 {
753         (void) pspec;
754
755         GnomeCanvasItem *item;
756         GnomeCanvasWaveView *waveview;
757         int redraw = FALSE;
758         int calc_bounds = FALSE;
759
760         g_return_if_fail (object != NULL);
761         g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
762
763         item = GNOME_CANVAS_ITEM (object);
764         waveview = GNOME_CANVAS_WAVEVIEW (object);
765
766         void * ptr;
767         switch (prop_id) {
768         case PROP_DATA_SRC:
769                 gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
770                 redraw = TRUE;
771                 break;
772
773         case PROP_CHANNEL:
774                 gnome_canvas_waveview_set_channel (waveview, g_value_get_uint(value));
775                 redraw = TRUE;
776                 break;
777
778         case PROP_LENGTH_FUNCTION:
779                 ptr = g_value_get_pointer(value);
780                 waveview->length_function = POSIX_FUNC_PTR_CAST(waveview_length_function_t, ptr);
781                 redraw = TRUE;
782                 break;
783
784         case PROP_SOURCEFILE_LENGTH_FUNCTION:
785                 ptr = g_value_get_pointer(value);
786                 waveview->sourcefile_length_function = POSIX_FUNC_PTR_CAST(waveview_sourcefile_length_function_t, ptr);
787                 redraw = TRUE;
788                 break;
789
790         case PROP_PEAK_FUNCTION:
791                 ptr = g_value_get_pointer(value);
792                 waveview->peak_function = POSIX_FUNC_PTR_CAST(waveview_peak_function_t, ptr);
793                 redraw = TRUE;
794                 break;
795
796         case PROP_GAIN_FUNCTION:
797                 ptr = g_value_get_pointer(value);
798                 waveview->gain_curve_function = POSIX_FUNC_PTR_CAST(waveview_gain_curve_function_t, ptr);
799                          redraw = TRUE;
800                 break;
801
802         case PROP_GAIN_SRC:
803                 waveview->gain_src = g_value_get_pointer(value);
804                 if (waveview->cache_updater) {
805                         waveview->cache->start = 0;
806                         waveview->cache->end = 0;
807                 }
808                 redraw = TRUE;
809                 calc_bounds = TRUE;
810                 break;
811
812         case PROP_CACHE:
813                 waveview->cache = g_value_get_pointer(value);
814                 redraw = TRUE;
815                 break;
816
817
818         case PROP_CACHE_UPDATER:
819                 waveview->cache_updater = g_value_get_boolean(value);
820                 redraw = TRUE;
821                 break;
822
823         case PROP_SAMPLES_PER_UNIT:
824                 if ((waveview->samples_per_unit = g_value_get_double(value)) < 1.0) {
825                         waveview->samples_per_unit = 1.0;
826                 }
827                 if (waveview->cache_updater) {
828                         waveview->cache->start = 0;
829                         waveview->cache->end = 0;
830                 }
831                 redraw = TRUE;
832                 calc_bounds = TRUE;
833                 break;
834
835         case PROP_AMPLITUDE_ABOVE_AXIS:
836                 waveview->amplitude_above_axis = g_value_get_double(value);
837                 redraw = TRUE;
838                 break;
839
840         case PROP_X:
841                 if (waveview->x != g_value_get_double (value)) {
842                         waveview->x = g_value_get_double (value);
843                         calc_bounds = TRUE;
844                 }
845                 break;
846
847         case PROP_Y:
848                 if (waveview->y != g_value_get_double (value)) {
849                         waveview->y = g_value_get_double (value);
850                         calc_bounds = TRUE;
851                 }
852                 break;
853
854         case PROP_HEIGHT:
855                 if (waveview->height != fabs (g_value_get_double (value))) {
856                         waveview->height = fabs (g_value_get_double (value));
857                         redraw = TRUE;
858                 }
859                 break;
860
861         case PROP_WAVE_COLOR:
862                 if (waveview->wave_color != g_value_get_uint(value)) {
863                         waveview->wave_color = g_value_get_uint(value);
864                         redraw = TRUE;
865                 }
866                 break;
867
868         case PROP_CLIP_COLOR:
869                 if (waveview->clip_color != g_value_get_uint(value)) {
870                         waveview->clip_color = g_value_get_uint(value);
871                         redraw = TRUE;
872                 }
873                 break;
874
875         case PROP_ZERO_COLOR:
876                 if (waveview->zero_color != g_value_get_uint(value)) {
877                         waveview->zero_color = g_value_get_uint(value);
878                         redraw = TRUE;
879                 }
880                 break;
881
882         case PROP_FILL_COLOR:
883                 if (waveview->fill_color != g_value_get_uint(value)) {
884                         waveview->fill_color = g_value_get_uint(value);
885                         redraw = TRUE;
886                 }
887                 break;
888
889         case PROP_FILLED:
890                 if (waveview->filled != g_value_get_boolean(value)) {
891                         waveview->filled = g_value_get_boolean(value);
892                         redraw = TRUE;
893                 }
894                 break;
895
896         case PROP_RECTIFIED:
897                 if (waveview->rectified != g_value_get_boolean(value)) {
898                         waveview->rectified = g_value_get_boolean(value);
899                         redraw = TRUE;
900                 }
901                 break;
902
903         case PROP_ZERO_LINE:
904                 if (waveview->zero_line != g_value_get_boolean(value)) {
905                         waveview->zero_line = g_value_get_boolean(value);
906                         redraw = TRUE;
907                 }
908                 break;
909
910         case PROP_LOGSCALED:
911                 if (waveview->logscaled != g_value_get_boolean(value)) {
912                         waveview->logscaled = g_value_get_boolean(value);
913                         if (waveview->cache_updater) {
914                                 waveview->cache->start = 0;
915                                 waveview->cache->end = 0;
916                         }
917                         redraw = TRUE;
918                         calc_bounds = TRUE;
919                 }
920                 break;
921         case PROP_REGION_START:
922                 waveview->region_start = g_value_get_uint(value);
923                 redraw = TRUE;
924                 calc_bounds = TRUE;
925                 break;
926
927
928         default:
929                 break;
930         }
931
932         if (calc_bounds) {
933                 gnome_canvas_waveview_reset_bounds (item);
934         }
935
936         if (redraw) {
937                 gnome_canvas_item_request_update (item);
938         }
939
940 }
941
942 static void
943 gnome_canvas_waveview_get_property (
944                 GObject      *object,
945                 guint         prop_id,
946                 GValue       *value,
947                 GParamSpec   *pspec)
948 {
949
950
951         g_return_if_fail (object != NULL);
952         g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
953
954         GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
955
956         switch (prop_id) {
957         case PROP_DATA_SRC:
958                 g_value_set_pointer(value, waveview->data_src);
959                 break;
960
961         case PROP_CHANNEL:
962                 g_value_set_uint(value, waveview->channel);
963                 break;
964
965         case PROP_LENGTH_FUNCTION:
966                 g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->length_function));
967                 break;
968
969         case PROP_SOURCEFILE_LENGTH_FUNCTION:
970                 g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->sourcefile_length_function));
971                 break;
972
973         case PROP_PEAK_FUNCTION:
974                 g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->peak_function));
975                 break;
976
977         case PROP_GAIN_FUNCTION:
978                 g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->gain_curve_function));
979                 break;
980
981         case PROP_GAIN_SRC:
982                 g_value_set_pointer(value, waveview->gain_src);
983                 break;
984
985         case PROP_CACHE:
986                 g_value_set_pointer(value, waveview->cache);
987                 break;
988
989         case PROP_CACHE_UPDATER:
990                 g_value_set_boolean(value, waveview->cache_updater);
991                 break;
992
993         case PROP_SAMPLES_PER_UNIT:
994                 g_value_set_double(value, waveview->samples_per_unit);
995                 break;
996
997         case PROP_AMPLITUDE_ABOVE_AXIS:
998                 g_value_set_double(value, waveview->amplitude_above_axis);
999                 break;
1000
1001         case PROP_X:
1002                 g_value_set_double (value, waveview->x);
1003                 break;
1004
1005         case PROP_Y:
1006                 g_value_set_double (value, waveview->y);
1007                 break;
1008
1009         case PROP_HEIGHT:
1010                 g_value_set_double (value, waveview->height);
1011                 break;
1012
1013         case PROP_WAVE_COLOR:
1014                 g_value_set_uint (value, waveview->wave_color);
1015                 break;
1016
1017         case PROP_CLIP_COLOR:
1018                 g_value_set_uint (value, waveview->clip_color);
1019                 break;
1020
1021         case PROP_ZERO_COLOR:
1022                 g_value_set_uint (value, waveview->zero_color);
1023                 break;
1024
1025         case PROP_FILL_COLOR:
1026                 g_value_set_uint (value, waveview->fill_color);
1027                 break;
1028
1029         case PROP_FILLED:
1030                 g_value_set_boolean (value, waveview->filled);
1031                 break;
1032
1033         case PROP_RECTIFIED:
1034                 g_value_set_boolean (value, waveview->rectified);
1035                 break;
1036
1037         case PROP_ZERO_LINE:
1038                 g_value_set_boolean (value, waveview->zero_line);
1039                 break;
1040
1041         case PROP_LOGSCALED:
1042                 g_value_set_boolean (value, waveview->logscaled);
1043                 break;
1044
1045         case PROP_REGION_START:
1046                 g_value_set_uint (value, waveview->region_start);
1047                 break;
1048
1049         default:
1050                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1051                 break;
1052         }
1053 }
1054
1055 static void
1056 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1057 {
1058         GnomeCanvasWaveView *waveview;
1059         double x, y;
1060
1061         waveview = GNOME_CANVAS_WAVEVIEW (item);
1062
1063 //      check_cache (waveview, "start of update");
1064
1065         if (parent_class->update)
1066                 (* parent_class->update) (item, affine, clip_path, flags);
1067
1068         gnome_canvas_waveview_reset_bounds (item);
1069
1070         /* get the canvas coordinates of the view. Do NOT use affines
1071            for this, because they do not round to the integer units used
1072            by the canvas, resulting in subtle pixel-level errors later.
1073         */
1074
1075         x = waveview->x;
1076         y = waveview->y;
1077
1078         gnome_canvas_item_i2w (item, &x, &y);
1079         gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
1080
1081         waveview->samples = waveview->length_function (waveview->data_src);
1082
1083         x = waveview->x + (waveview->samples / waveview->samples_per_unit);
1084         y = waveview->y + waveview->height;
1085
1086         gnome_canvas_item_i2w (item, &x, &y);
1087         gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
1088
1089         /* cache the half-height and the end point in canvas units */
1090
1091         waveview->half_height = waveview->height / 2.0;
1092
1093         /* parse the color */
1094
1095         UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
1096                       &waveview->wave_a);
1097         UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
1098                       &waveview->clip_a);
1099         UINT_TO_RGBA (waveview->fill_color, &waveview->fill_r, &waveview->fill_g, &waveview->fill_b,
1100                       &waveview->fill_a);
1101
1102 //      check_cache (waveview, "end of update");
1103 }
1104
1105 static void
1106 gnome_canvas_waveview_gradient_render (GnomeCanvasItem *item,
1107                                        GnomeCanvasBuf *buf)
1108 {
1109         GnomeCanvasWaveView *waveview;
1110         gulong s1, s2;
1111         int clip_length = 0;
1112         int pymin, pymax;
1113         guint cache_index;
1114         double half_height;
1115         int x;
1116         char rectify;
1117
1118         waveview = GNOME_CANVAS_WAVEVIEW (item);
1119
1120 //      check_cache (waveview, "start of render");
1121
1122         if (parent_class->render) {
1123                 (*parent_class->render) (item, buf);
1124         }
1125
1126         if (buf->is_bg) {
1127                 gnome_canvas_buf_ensure_buf (buf);
1128                 buf->is_bg = FALSE;
1129         }
1130
1131         /* a "unit" means a pixel */
1132
1133         /* begin: render start x (units) */
1134         int const begin = MAX (waveview->bbox_ulx, buf->rect.x0);
1135
1136         /* zbegin: start x for zero line (units) */
1137         int const zbegin = (begin == waveview->bbox_ulx) ? (begin + 1) : begin;
1138
1139         /* end: render end x (units) */
1140         int const end = (waveview->bbox_lrx >= 0) ? MIN (waveview->bbox_lrx,buf->rect.x1) : buf->rect.x1;
1141
1142         /* zend: end x for zero-line (units) */
1143         int const zend = (end == waveview->bbox_lrx) ? (end - 1) : end;
1144
1145         if (begin == end) {
1146                 return;
1147         }
1148
1149         /* s1: start sample
1150            s2: end sample
1151         */
1152
1153         s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit);
1154
1155         // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
1156
1157         if (end == waveview->bbox_lrx) {
1158                 /* This avoids minor rounding errors when we have the
1159                    entire region visible.
1160                 */
1161                 s2 = waveview->samples;
1162         } else {
1163                 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
1164         }
1165
1166 #if 0
1167         printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
1168                 " b/e %d..%d s= %lu..%lu @ %f\n",
1169                 waveview,
1170                 buf->rect.x0,
1171                 buf->rect.x1,
1172                 buf->rect.y0,
1173                 buf->rect.y1,
1174                 waveview->bbox_ulx,
1175                 waveview->bbox_lrx,
1176                 waveview->bbox_uly,
1177                 waveview->bbox_lry,
1178                 begin, end, s1, s2,
1179                 waveview->samples_per_unit);
1180 #endif
1181
1182         /* now ensure that the cache is full and properly
1183            positioned.
1184         */
1185
1186 //      check_cache (waveview, "pre-ensure");
1187
1188         if (waveview->cache_updater && waveview->reload_cache_in_render) {
1189                 waveview->cache->start = 0;
1190                 waveview->cache->end = 0;
1191                 waveview->reload_cache_in_render = FALSE;
1192         }
1193
1194 //      check_cache (waveview, "post-ensure");
1195
1196         /* don't rectify at single-sample zoom */
1197         if (waveview->rectified && waveview->samples_per_unit > 1) {
1198                 rectify = TRUE;
1199         }
1200         else {
1201                 rectify = FALSE;
1202         }
1203
1204         clip_length = MIN(5,(waveview->height/4));
1205
1206         /*
1207            Now draw each line, clipping it appropriately. The clipping
1208            is done by the macros PAINT_FOO().
1209         */
1210
1211         half_height = waveview->half_height;
1212
1213 /* this makes it slightly easier to comprehend whats going on */
1214 #define origin half_height
1215
1216         if (waveview->filled && !rectify) {
1217                 int prev_pymin = 1;
1218                 int prev_pymax = 0;
1219                 int last_pymin = 1;
1220                 int last_pymax = 0;
1221                 int next_pymin, next_pymax;
1222                 double max, min;
1223                 int next_clip_max = 0;
1224                 int next_clip_min = 0;
1225
1226                 int wave_middle = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1227                 int wave_top = (int) rint ((item->y1) * item->canvas->pixels_per_unit);
1228
1229                 if (s1 < waveview->samples_per_unit) {
1230                         /* we haven't got a prev vars to compare with, so outline the whole line here */
1231                         prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1232                         prev_pymin = prev_pymax;
1233                 }
1234                 else {
1235                         s1 -= waveview->samples_per_unit;
1236                 }
1237
1238                 if(end == waveview->bbox_lrx) {
1239                         /* we don't have the NEXT vars for the last sample */
1240                         last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1241                         last_pymin = last_pymax;
1242                 }
1243                 else {
1244                         s2 += waveview->samples_per_unit;
1245                 }
1246
1247                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1248
1249                 /*
1250                  * Compute the variables outside the rendering rect
1251                  */
1252                 if(prev_pymax != prev_pymin) {
1253
1254                         prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1255                         prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1256                         ++cache_index;
1257                 }
1258                 if(last_pymax != last_pymin) {
1259                         /* take the index of one sample right of what we render */
1260                         guint index = cache_index + (end - begin);
1261
1262                         if (index >= waveview->cache->data_size) {
1263
1264                                 /* the data we want is off the end of the cache, which must mean its beyond
1265                                    the end of the region's source; hence the peak values are 0 */
1266                                 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1267                                 last_pymin = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1268
1269                         } else {
1270
1271                                 last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1272                                 last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1273
1274                         }
1275
1276                 }
1277
1278                 /*
1279                  * initialize NEXT* variables for the first run, duplicated in the loop for speed
1280                  */
1281                 max = waveview->cache->data[cache_index].max;
1282                 min = waveview->cache->data[cache_index].min;
1283
1284                 if (max >= 1.0) {
1285                         max = 1.0;
1286                         next_clip_max = 1;
1287                 }
1288
1289                 if (min <= -1.0) {
1290                         min = -1.0;
1291                         next_clip_min = 1;
1292                 }
1293
1294                 max *= half_height;
1295                 min *= half_height;
1296
1297                 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1298                 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1299
1300                 /*
1301                  * And now the loop
1302                  */
1303                 for(x = begin; x < end; ++x) {
1304                         int clip_max = next_clip_max;
1305                         int clip_min = next_clip_min;
1306                         int fill_max, fill_min;
1307
1308                         pymax = next_pymax;
1309                         pymin = next_pymin;
1310
1311                         /* compute next */
1312                         if(x == end - 1) {
1313                                 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1314                                 next_pymax = last_pymax;
1315                                 next_pymin = last_pymin;
1316                         }
1317                         else {
1318                                 ++cache_index;
1319
1320                                 if (cache_index < waveview->cache->data_size) {
1321                                         max = waveview->cache->data[cache_index].max;
1322                                         min = waveview->cache->data[cache_index].min;
1323                                 } else {
1324                                         max = min = 0;
1325                                 }
1326
1327                                 next_clip_max = 0;
1328                                 next_clip_min = 0;
1329
1330                                 if (max >= 1.0) {
1331                                         max = 1.0;
1332                                         next_clip_max = 1;
1333                                 }
1334
1335                                 if (min <= -1.0) {
1336                                         min = -1.0;
1337                                         next_clip_min = 1;
1338                                 }
1339
1340                                 max *= half_height;
1341                                 min *= half_height;
1342
1343                                 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1344                                 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1345                         }
1346
1347                         /* render */
1348                         if (pymax == pymin) {
1349                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1350                         } else {
1351                                 if((prev_pymax < pymax && next_pymax < pymax) ||
1352                                    (prev_pymax == pymax && next_pymax == pymax)) {
1353                                         fill_max = pymax + 1;
1354                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1355                                 }
1356                                 else {
1357                                         fill_max = MAX(prev_pymax, next_pymax);
1358                                         if(pymax == fill_max) {
1359                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1360                                                 ++fill_max;
1361                                         } else {
1362                                                 PAINT_VERTA_GR(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max, wave_middle, wave_top);
1363                                         }
1364
1365                                 }
1366
1367                                 if((prev_pymin > pymin && next_pymin > pymin) ||
1368                                    (prev_pymin == pymin && next_pymin == pymin)) {
1369                                         fill_min = pymin - 1;
1370                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1371                                 }
1372                                 else {
1373                                         fill_min = MIN(prev_pymin, next_pymin);
1374                                         if(pymin == fill_min) {
1375                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1376                                         }
1377                                         else {
1378                                                 PAINT_VERTA_GR(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin, wave_middle, wave_top);
1379                                         }
1380                                 }
1381
1382                                 if(fill_max < fill_min) {
1383                                         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);
1384                                 }
1385                                 else if(fill_max == fill_min) {
1386                                         PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1387                                 }
1388                         }
1389
1390                         if (clip_max) {
1391                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax + clip_length);
1392                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x + 1, pymax, pymax + (clip_length -1));
1393                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x - 1, pymax, pymax + (clip_length - 1));
1394
1395                         }
1396
1397                         if (clip_min) {
1398                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a , x, pymin - clip_length, pymin);
1399                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x + 1, pymin - (clip_length - 1), pymin);
1400                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x - 1, pymin - (clip_length - 1), pymin);
1401                         }
1402
1403                         prev_pymax = pymax;
1404                         prev_pymin = pymin;
1405                 }
1406
1407         } else if (waveview->filled && rectify) {
1408
1409                 int prev_pymax = -1;
1410                 int last_pymax = -1;
1411                 int next_pymax;
1412                 double max, min;
1413                 int next_clip_max = 0;
1414                 int next_clip_min = 0;
1415
1416                 int wave_middle = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1417                 int wave_top = (int) rint ((item->y1) * item->canvas->pixels_per_unit);
1418
1419                 // for rectified, this stays constant throughout the loop
1420                 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1421
1422                 if(s1 < waveview->samples_per_unit) {
1423                         /* we haven't got a prev vars to compare with, so outline the whole line here */
1424                         prev_pymax = pymin;
1425                 }
1426                 else {
1427                         s1 -= waveview->samples_per_unit;
1428                 }
1429
1430                 if(end == waveview->bbox_lrx) {
1431                         /* we don't have the NEXT vars for the last sample */
1432                         last_pymax = pymin;
1433                 }
1434                 else {
1435                         s2 += waveview->samples_per_unit;
1436                 }
1437
1438                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1439
1440                 /*
1441                  * Compute the variables outside the rendering rect
1442                  */
1443                 if(prev_pymax < 0) {
1444                         max = MIN(waveview->cache->data[cache_index].max, 1.0);
1445                         min = MAX(waveview->cache->data[cache_index].min, -1.0);
1446
1447                         if (fabs (min) > fabs (max)) {
1448                                 max = fabs (min);
1449                         }
1450
1451                         prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1452                         ++cache_index;
1453                 }
1454                 if(last_pymax < 0) {
1455                         /* take the index of one sample right of what we render */
1456                         int index = cache_index + (end - begin);
1457
1458                         max = MIN(waveview->cache->data[index].max, 1.0);
1459                         min = MAX(waveview->cache->data[index].min, -1.0);
1460
1461                         if (fabs (min) > fabs (max)) {
1462                                 max = fabs (min);
1463                         }
1464
1465                         last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1466                 }
1467
1468                 /*
1469                  * initialize NEXT* variables for the first run, duplicated in the loop for speed
1470                  */
1471                 max = waveview->cache->data[cache_index].max;
1472                 min = waveview->cache->data[cache_index].min;
1473
1474                 if (max >= 1.0) {
1475                         max = 1.0;
1476                         next_clip_max = 1;
1477                 }
1478
1479                 if (min <= -1.0) {
1480                         min = -1.0;
1481                         next_clip_min = 1;
1482                 }
1483
1484                 if (fabs (min) > fabs (max)) {
1485                         max = fabs (min);
1486                 }
1487
1488                 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1489
1490                 /*
1491                  * And now the loop
1492                  */
1493                 for(x = begin; x < end; ++x) {
1494                         int clip_max = next_clip_max;
1495                         int clip_min = next_clip_min;
1496                         int fill_max;
1497
1498                         pymax = next_pymax;
1499
1500                         /* compute next */
1501                         if(x == end - 1) {
1502                                 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1503                                 next_pymax = last_pymax;
1504                         }
1505                         else {
1506                                 ++cache_index;
1507
1508                                 max = waveview->cache->data[cache_index].max;
1509                                 min = waveview->cache->data[cache_index].min;
1510
1511                                 if (max >= 1.0) {
1512                                         max = 1.0;
1513                                         next_clip_max = 1;
1514                                 }
1515
1516                                 if (min <= -1.0) {
1517                                         min = -1.0;
1518                                         next_clip_min = 1;
1519                                 }
1520
1521                                 if (fabs (min) > fabs (max)) {
1522                                         max = fabs (min);
1523                                 }
1524
1525                                 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1526                         }
1527
1528                         /* render */
1529                         if (pymax == pymin) {
1530                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1531                         } else {
1532                                 if((prev_pymax < pymax && next_pymax < pymax) ||
1533                                    (prev_pymax == pymax && next_pymax == pymax)) {
1534                                         fill_max = pymax + 1;
1535                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1536                                 }
1537                                 else {
1538                                         fill_max = MAX(prev_pymax, next_pymax);
1539                                         if(pymax == fill_max) {
1540                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1541                                                 ++fill_max;
1542                                         }
1543                                         else {
1544                                                 PAINT_VERTA_GR(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max, wave_middle, wave_top);
1545                                         }
1546                                 }
1547
1548                                 if(fill_max < pymin) {
1549                                         PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
1550                                 }
1551                                 else if(fill_max == pymin) {
1552                                         PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
1553                                 }
1554                         }
1555
1556                         if (clip_max) {
1557                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax + clip_length);
1558                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x + 1, pymax, pymax + (clip_length -1));
1559                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x - 1, pymax, pymax + (clip_length - 1));
1560                         }
1561
1562                         if (clip_min) {
1563                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a , x, pymin - clip_length, pymin);
1564                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x + 1, pymin - (clip_length - 1), pymin);
1565                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x - 1, pymin - (clip_length - 1), pymin);
1566                         }
1567
1568                         prev_pymax = pymax;
1569                 }
1570         }
1571         else {
1572                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1573
1574                 for (x = begin; x < end; x++) {
1575
1576                         double max, min;
1577                         int clip_max, clip_min;
1578
1579                         clip_max = 0;
1580                         clip_min = 0;
1581
1582                         max = waveview->cache->data[cache_index].max;
1583                         min = waveview->cache->data[cache_index].min;
1584
1585                         if (max >= 1.0) {
1586                                 max = 1.0;
1587                                 clip_max = 1;
1588                         }
1589
1590                         if (min <= -1.0) {
1591                                 min = -1.0;
1592                                 clip_min = 1;
1593                         }
1594
1595                         if (rectify) {
1596
1597                                 if (fabs (min) > fabs (max)) {
1598                                         max = fabs (min);
1599                                 }
1600
1601                                 max = max * waveview->height;
1602
1603                                 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1604                                 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1605
1606                         } else {
1607
1608                                 max = max * half_height;
1609                                 min = min * half_height;
1610
1611                                 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1612                                 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1613                         }
1614
1615                         /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1616                            or, if samples_per_unit == 1, then a dot at each location.
1617                         */
1618
1619                         if (pymax == pymin) {
1620                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1621                         } else {
1622                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1623                         }
1624
1625                         /* show clipped waveforms with small red lines */
1626
1627                         if (clip_max) {
1628                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1629                         }
1630
1631                         if (clip_min) {
1632                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1633                         }
1634
1635                         /* presto, we're done */
1636
1637                         cache_index++;
1638                 }
1639         }
1640
1641         if (!waveview->rectified && waveview->zero_line && waveview->height >= 100) {
1642                 // Paint zeroline.
1643
1644                 unsigned char zero_r, zero_g, zero_b, zero_a;
1645                 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a);
1646                 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1647                 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, zend, zeroline_y);
1648         }
1649 #undef origin
1650
1651 }
1652
1653 static void
1654 gnome_canvas_waveview_flat_render (GnomeCanvasItem *item,
1655                                    GnomeCanvasBuf *buf)
1656 {
1657         GnomeCanvasWaveView *waveview;
1658         gulong s1, s2;
1659         int clip_length = 0;
1660         int pymin, pymax;
1661         guint cache_index;
1662         double half_height;
1663         int x;
1664         char rectify;
1665
1666         waveview = GNOME_CANVAS_WAVEVIEW (item);
1667
1668 //      check_cache (waveview, "start of render");
1669
1670         if (parent_class->render) {
1671                 (*parent_class->render) (item, buf);
1672         }
1673
1674         if (buf->is_bg) {
1675                 gnome_canvas_buf_ensure_buf (buf);
1676                 buf->is_bg = FALSE;
1677         }
1678
1679         /* a "unit" means a pixel */
1680
1681         /* begin: render start x (units) */
1682         int const begin = MAX (waveview->bbox_ulx, buf->rect.x0);
1683
1684         /* zbegin: start x for zero line (units) */
1685         int const zbegin = (begin == waveview->bbox_ulx) ? (begin + 1) : begin;
1686
1687         /* end: render end x (units) */
1688         int const end = (waveview->bbox_lrx >= 0) ? MIN (waveview->bbox_lrx,buf->rect.x1) : buf->rect.x1;
1689
1690         /* zend: end x for zero-line (units) */
1691         int const zend = (end == waveview->bbox_lrx) ? (end - 1) : end;
1692
1693         if (begin == end) {
1694                 return;
1695         }
1696
1697         /* s1: start sample
1698            s2: end sample
1699         */
1700
1701         s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit);
1702
1703         // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
1704
1705         if (end == waveview->bbox_lrx) {
1706                 /* This avoids minor rounding errors when we have the
1707                    entire region visible.
1708                 */
1709                 s2 = waveview->samples;
1710         } else {
1711                 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
1712         }
1713
1714 #if 0
1715         printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
1716                 " b/e %d..%d s= %lu..%lu @ %f\n",
1717                 waveview,
1718                 buf->rect.x0,
1719                 buf->rect.x1,
1720                 buf->rect.y0,
1721                 buf->rect.y1,
1722                 waveview->bbox_ulx,
1723                 waveview->bbox_lrx,
1724                 waveview->bbox_uly,
1725                 waveview->bbox_lry,
1726                 begin, end, s1, s2,
1727                 waveview->samples_per_unit);
1728 #endif
1729
1730         /* now ensure that the cache is full and properly
1731            positioned.
1732         */
1733
1734 //      check_cache (waveview, "pre-ensure");
1735
1736         if (waveview->cache_updater && waveview->reload_cache_in_render) {
1737                 waveview->cache->start = 0;
1738                 waveview->cache->end = 0;
1739                 waveview->reload_cache_in_render = FALSE;
1740         }
1741
1742 //      check_cache (waveview, "post-ensure");
1743
1744         /* don't rectify at single-sample zoom */
1745         if (waveview->rectified && waveview->samples_per_unit > 1) {
1746                 rectify = TRUE;
1747         }
1748         else {
1749                 rectify = FALSE;
1750         }
1751
1752         clip_length = MIN(5,(waveview->height/4));
1753
1754         /*
1755            Now draw each line, clipping it appropriately. The clipping
1756            is done by the macros PAINT_FOO().
1757         */
1758
1759         half_height = waveview->half_height;
1760
1761 /* this makes it slightly easier to comprehend whats going on */
1762 #define origin half_height
1763
1764         if (waveview->filled && !rectify) {
1765                 int prev_pymin = 1;
1766                 int prev_pymax = 0;
1767                 int last_pymin = 1;
1768                 int last_pymax = 0;
1769                 int next_pymin, next_pymax;
1770                 double max, min;
1771                 int next_clip_max = 0;
1772                 int next_clip_min = 0;
1773
1774                 if (s1 < waveview->samples_per_unit) {
1775                         /* we haven't got a prev vars to compare with, so outline the whole line here */
1776                         prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1777                         prev_pymin = prev_pymax;
1778                 }
1779                 else {
1780                         s1 -= waveview->samples_per_unit;
1781                 }
1782
1783                 if(end == waveview->bbox_lrx) {
1784                         /* we don't have the NEXT vars for the last sample */
1785                         last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1786                         last_pymin = last_pymax;
1787                 }
1788                 else {
1789                         s2 += waveview->samples_per_unit;
1790                 }
1791
1792                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1793
1794                 /*
1795                  * Compute the variables outside the rendering rect
1796                  */
1797                 if(prev_pymax != prev_pymin) {
1798
1799                         prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1800                         prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1801                         ++cache_index;
1802                 }
1803                 if(last_pymax != last_pymin) {
1804                         /* take the index of one sample right of what we render */
1805                         guint index = cache_index + (end - begin);
1806
1807                         if (index >= waveview->cache->data_size) {
1808
1809                                 /* the data we want is off the end of the cache, which must mean its beyond
1810                                    the end of the region's source; hence the peak values are 0 */
1811                                 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1812                                 last_pymin = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1813
1814                         } else {
1815
1816                                 last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1817                                 last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1818
1819                         }
1820
1821                 }
1822
1823                 /*
1824                  * initialize NEXT* variables for the first run, duplicated in the loop for speed
1825                  */
1826                 max = waveview->cache->data[cache_index].max;
1827                 min = waveview->cache->data[cache_index].min;
1828
1829                 if (max >= 1.0) {
1830                         max = 1.0;
1831                         next_clip_max = 1;
1832                 }
1833
1834                 if (min <= -1.0) {
1835                         min = -1.0;
1836                         next_clip_min = 1;
1837                 }
1838
1839                 max *= half_height;
1840                 min *= half_height;
1841
1842                 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1843                 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1844
1845                 /*
1846                  * And now the loop
1847                  */
1848                 for(x = begin; x < end; ++x) {
1849                         int clip_max = next_clip_max;
1850                         int clip_min = next_clip_min;
1851                         int fill_max, fill_min;
1852
1853                         pymax = next_pymax;
1854                         pymin = next_pymin;
1855
1856                         /* compute next */
1857                         if(x == end - 1) {
1858                                 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1859                                 next_pymax = last_pymax;
1860                                 next_pymin = last_pymin;
1861                         }
1862                         else {
1863                                 ++cache_index;
1864
1865                                 if (cache_index < waveview->cache->data_size) {
1866                                         max = waveview->cache->data[cache_index].max;
1867                                         min = waveview->cache->data[cache_index].min;
1868                                 } else {
1869                                         max = min = 0;
1870                                 }
1871
1872                                 next_clip_max = 0;
1873                                 next_clip_min = 0;
1874
1875                                 if (max >= 1.0) {
1876                                         max = 1.0;
1877                                         next_clip_max = 1;
1878                                 }
1879
1880                                 if (min <= -1.0) {
1881                                         min = -1.0;
1882                                         next_clip_min = 1;
1883                                 }
1884
1885                                 max *= half_height;
1886                                 min *= half_height;
1887
1888                                 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1889                                 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1890                         }
1891
1892                         /* render */
1893                         if (pymax == pymin) {
1894                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1895                         } else {
1896                                 if((prev_pymax < pymax && next_pymax < pymax) ||
1897                                    (prev_pymax == pymax && next_pymax == pymax)) {
1898                                         fill_max = pymax + 1;
1899                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1900                                 }
1901                                 else {
1902                                         fill_max = MAX(prev_pymax, next_pymax);
1903                                         if(pymax == fill_max) {
1904                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1905                                                 ++fill_max;
1906                                         }
1907                                         else {
1908                                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1909                                         }
1910                                 }
1911
1912                                 if((prev_pymin > pymin && next_pymin > pymin) ||
1913                                    (prev_pymin == pymin && next_pymin == pymin)) {
1914                                         fill_min = pymin - 1;
1915                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1916                                 }
1917                                 else {
1918                                         fill_min = MIN(prev_pymin, next_pymin);
1919                                         if(pymin == fill_min) {
1920                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1921                                         }
1922                                         else {
1923                                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin);
1924                                         }
1925                                 }
1926
1927                                 if(fill_max < fill_min) {
1928                                         PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min);
1929                                 }
1930                                 else if(fill_max == fill_min) {
1931                                         PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1932                                 }
1933                         }
1934
1935                         if (clip_max) {
1936                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1937                         }
1938
1939                         if (clip_min) {
1940                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1941                         }
1942
1943                         prev_pymax = pymax;
1944                         prev_pymin = pymin;
1945                 }
1946
1947         } else if (waveview->filled && rectify) {
1948
1949                 int prev_pymax = -1;
1950                 int last_pymax = -1;
1951                 int next_pymax;
1952                 double max, min;
1953                 int next_clip_max = 0;
1954                 int next_clip_min = 0;
1955
1956                 // for rectified, this stays constant throughout the loop
1957                 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1958
1959                 if(s1 < waveview->samples_per_unit) {
1960                         /* we haven't got a prev vars to compare with, so outline the whole line here */
1961                         prev_pymax = pymin;
1962                 }
1963                 else {
1964                         s1 -= waveview->samples_per_unit;
1965                 }
1966
1967                 if(end == waveview->bbox_lrx) {
1968                         /* we don't have the NEXT vars for the last sample */
1969                         last_pymax = pymin;
1970                 }
1971                 else {
1972                         s2 += waveview->samples_per_unit;
1973                 }
1974
1975                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1976
1977                 /*
1978                  * Compute the variables outside the rendering rect
1979                  */
1980                 if(prev_pymax < 0) {
1981                         max = MIN(waveview->cache->data[cache_index].max, 1.0);
1982                         min = MAX(waveview->cache->data[cache_index].min, -1.0);
1983
1984                         if (fabs (min) > fabs (max)) {
1985                                 max = fabs (min);
1986                         }
1987
1988                         prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1989                         ++cache_index;
1990                 }
1991                 if(last_pymax < 0) {
1992                         /* take the index of one sample right of what we render */
1993                         int index = cache_index + (end - begin);
1994
1995                         max = MIN(waveview->cache->data[index].max, 1.0);
1996                         min = MAX(waveview->cache->data[index].min, -1.0);
1997
1998                         if (fabs (min) > fabs (max)) {
1999                                 max = fabs (min);
2000                         }
2001
2002                         last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
2003                 }
2004
2005                 /*
2006                  * initialize NEXT* variables for the first run, duplicated in the loop for speed
2007                  */
2008                 max = waveview->cache->data[cache_index].max;
2009                 min = waveview->cache->data[cache_index].min;
2010
2011                 if (max >= 1.0) {
2012                         max = 1.0;
2013                         next_clip_max = 1;
2014                 }
2015
2016                 if (min <= -1.0) {
2017                         min = -1.0;
2018                         next_clip_min = 1;
2019                 }
2020
2021                 if (fabs (min) > fabs (max)) {
2022                         max = fabs (min);
2023                 }
2024
2025                 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
2026
2027                 /*
2028                  * And now the loop
2029                  */
2030                 for(x = begin; x < end; ++x) {
2031                         int clip_max = next_clip_max;
2032                         int clip_min = next_clip_min;
2033                         int fill_max;
2034
2035                         pymax = next_pymax;
2036
2037                         /* compute next */
2038                         if(x == end - 1) {
2039                                 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
2040                                 next_pymax = last_pymax;
2041                         }
2042                         else {
2043                                 ++cache_index;
2044
2045                                 max = waveview->cache->data[cache_index].max;
2046                                 min = waveview->cache->data[cache_index].min;
2047
2048                                 if (max >= 1.0) {
2049                                         max = 1.0;
2050                                         next_clip_max = 1;
2051                                 }
2052
2053                                 if (min <= -1.0) {
2054                                         min = -1.0;
2055                                         next_clip_min = 1;
2056                                 }
2057
2058                                 if (fabs (min) > fabs (max)) {
2059                                         max = fabs (min);
2060                                 }
2061
2062                                 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
2063                         }
2064
2065                         /* render */
2066                         if (pymax == pymin) {
2067                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
2068                         } else {
2069                                 if((prev_pymax < pymax && next_pymax < pymax) ||
2070                                    (prev_pymax == pymax && next_pymax == pymax)) {
2071                                         fill_max = pymax + 1;
2072                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
2073                                 }
2074                                 else {
2075                                         fill_max = MAX(prev_pymax, next_pymax);
2076                                         if(pymax == fill_max) {
2077                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
2078                                                 ++fill_max;
2079                                         }
2080                                         else {
2081                                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
2082                                         }
2083                                 }
2084
2085                                 if(fill_max < pymin) {
2086                                         PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
2087                                 }
2088                                 else if(fill_max == pymin) {
2089                                         PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
2090                                 }
2091                         }
2092
2093                         if (clip_max) {
2094                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
2095                         }
2096
2097                         if (clip_min) {
2098                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
2099                         }
2100
2101                         prev_pymax = pymax;
2102                 }
2103         }
2104         else {
2105                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
2106
2107                 for (x = begin; x < end; x++) {
2108
2109                         double max, min;
2110                         int clip_max, clip_min;
2111
2112                         clip_max = 0;
2113                         clip_min = 0;
2114
2115                         max = waveview->cache->data[cache_index].max;
2116                         min = waveview->cache->data[cache_index].min;
2117
2118                         if (max >= 1.0) {
2119                                 max = 1.0;
2120                                 clip_max = 1;
2121                         }
2122
2123                         if (min <= -1.0) {
2124                                 min = -1.0;
2125                                 clip_min = 1;
2126                         }
2127
2128                         if (rectify) {
2129
2130                                 if (fabs (min) > fabs (max)) {
2131                                         max = fabs (min);
2132                                 }
2133
2134                                 max = max * waveview->height;
2135
2136                                 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
2137                                 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
2138
2139                         } else {
2140
2141                                 max = max * half_height;
2142                                 min = min * half_height;
2143
2144                                 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
2145                                 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
2146                         }
2147
2148                         /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
2149                            or, if samples_per_unit == 1, then a dot at each location.
2150                         */
2151
2152                         if (pymax == pymin) {
2153                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
2154                         } else {
2155                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
2156                         }
2157
2158                         /* show clipped waveforms with small red lines */
2159
2160                         if (clip_max) {
2161                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
2162                         }
2163
2164                         if (clip_min) {
2165                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
2166                         }
2167
2168                         /* presto, we're done */
2169
2170                         cache_index++;
2171                 }
2172         }
2173
2174         if (!waveview->rectified && waveview->zero_line && waveview->height >= 100) {
2175                 // Paint zeroline.
2176
2177                 unsigned char zero_r, zero_g, zero_b, zero_a;
2178                 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a);
2179                 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
2180                 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, zend, zeroline_y);
2181         }
2182 #undef origin
2183 }
2184
2185 static void
2186 gnome_canvas_waveview_render (GnomeCanvasItem *item,
2187                               GnomeCanvasBuf *buf)
2188 {
2189         if (_gradient_rendering) {
2190                 gnome_canvas_waveview_gradient_render (item, buf);
2191         } else {
2192                 gnome_canvas_waveview_flat_render (item, buf);
2193         }
2194 }
2195
2196 static void
2197 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
2198                             GdkDrawable *drawable,
2199                             int x, int y,
2200                             int width, int height)
2201 {
2202         GnomeCanvasWaveView *waveview;
2203         cairo_t* cr;
2204         gulong s1, s2;
2205         int cache_index;
2206         gboolean rectify;
2207         double origin;
2208         double xoff;
2209         double yoff = 0.0;
2210         double ulx;
2211         double uly;
2212         double lrx;
2213         double lry;
2214
2215         waveview = GNOME_CANVAS_WAVEVIEW (item);
2216
2217         /* compute intersection of Drawable area and waveview,
2218            in canvas coordinate space
2219         */
2220
2221         if (x > waveview->bbox_ulx) {
2222                 ulx = x;
2223         } else {
2224                 ulx = waveview->bbox_ulx;
2225         }
2226
2227         if (y > waveview->bbox_uly) {
2228                 uly = y;
2229         } else {
2230                 uly = waveview->bbox_uly;
2231         }
2232
2233         if (x + width > waveview->bbox_lrx) {
2234                 lrx = waveview->bbox_lrx;
2235         } else {
2236                 lrx = x + width;
2237         }
2238
2239         if (y + height > waveview->bbox_lry) {
2240                 lry = waveview->bbox_lry;
2241         } else {
2242                 lry = y + height;
2243         }
2244
2245         /* figure out which samples we need for the resulting intersection */
2246
2247         s1 = floor ((ulx - waveview->bbox_ulx) * waveview->samples_per_unit) ;
2248
2249         if (lrx == waveview->bbox_lrx) {
2250                 /* This avoids minor rounding errors when we have the
2251                    entire region visible.
2252                 */
2253                 s2 = waveview->samples;
2254         } else {
2255                 s2 = s1 + floor ((lrx - ulx) * waveview->samples_per_unit);
2256         }
2257
2258         /* translate back to buffer coordinate space */
2259
2260         ulx -= x;
2261         uly -= y;
2262         lrx -= x;
2263         lry -= y;
2264
2265         /* don't rectify at single-sample zoom */
2266         if(waveview->rectified && waveview->samples_per_unit > 1.0) {
2267                 rectify = TRUE;
2268         } else {
2269                 rectify = FALSE;
2270         }
2271
2272         cr = gdk_cairo_create (drawable);
2273         cairo_set_line_width (cr, 0.5);
2274
2275         origin = waveview->bbox_uly - y + waveview->half_height;
2276
2277         cairo_rectangle (cr, ulx, uly, lrx - ulx, lry - uly);
2278         cairo_clip (cr);
2279
2280         if (waveview->cache_updater && waveview->reload_cache_in_render) {
2281                 waveview->cache->start = 0;
2282                 waveview->cache->end = 0;
2283                 waveview->reload_cache_in_render = FALSE;
2284         }
2285
2286         cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
2287
2288 #if 0
2289         printf ("%p r (%d,%d)(%d,%d)[%d x %d] bbox (%d,%d)(%d,%d)[%d x %d]"
2290                 " draw (%.1f,%.1f)(%.1f,%.1f)[%.1f x %.1f] s= %lu..%lu\n",
2291                 waveview,
2292                 x, y,
2293                 x + width,
2294                 y + height,
2295                 width,
2296                 height,
2297                 waveview->bbox_ulx,
2298                 waveview->bbox_uly,
2299                 waveview->bbox_lrx,
2300                 waveview->bbox_lry,
2301                 waveview->bbox_lrx - waveview->bbox_ulx,
2302                 waveview->bbox_lry - waveview->bbox_uly,
2303                 ulx, uly,
2304                 lrx, lry,
2305                 lrx - ulx,
2306                 lry - uly,
2307                 s1, s2);
2308 #endif
2309
2310         /* draw the top half */
2311
2312         for (xoff = ulx; xoff < lrx; xoff++) {
2313                 double max, min;
2314
2315                 max = waveview->cache->data[cache_index].max;
2316                 min = waveview->cache->data[cache_index].min;
2317
2318                 if (min <= -1.0) {
2319                         min = -1.0;
2320                 }
2321
2322                 if (max >= 1.0) {
2323                         max = 1.0;
2324                 }
2325
2326                 if (rectify) {
2327                         if (fabs (min) > fabs (max)) {
2328                                 max = fabs (min);
2329                         }
2330                 }
2331
2332                 yoff = origin - (waveview->half_height * max) + 0.5;
2333
2334                 if (xoff == ulx) {
2335                         /* first point */
2336                         cairo_move_to (cr, xoff+0.5, yoff);
2337                 } else {
2338                         cairo_line_to (cr, xoff+0.5, yoff);
2339                 }
2340
2341                 cache_index++;
2342         }
2343
2344         /* from the final top point, move out of the clip zone */
2345
2346         cairo_line_to (cr, xoff + 10, yoff);
2347
2348         /* now draw the bottom half */
2349
2350         for (--xoff, --cache_index; xoff >= ulx; --xoff) {
2351                 double min;
2352
2353                 min = waveview->cache->data[cache_index].min;
2354
2355                 if (min <= -1.0) {
2356                         min = -1.0;
2357                 }
2358
2359                 yoff = origin - (waveview->half_height * min) + 0.5;
2360
2361                 cairo_line_to (cr, xoff+0.5, yoff);
2362                 cache_index--;
2363         }
2364
2365         /* from the final lower point, move out of the clip zone */
2366
2367         cairo_line_to (cr, xoff - 10, yoff);
2368
2369         /* close path to fill */
2370
2371         cairo_close_path (cr);
2372
2373         /* fill and stroke */
2374
2375         cairo_set_source_rgba (cr,
2376                                (waveview->fill_r/255.0),
2377                                (waveview->fill_g/255.0),
2378                                (waveview->fill_b/255.0),
2379                                (waveview->fill_a/255.0));
2380         cairo_fill_preserve (cr);
2381         cairo_set_source_rgba (cr,
2382                                (waveview->wave_r/255.0),
2383                                (waveview->wave_g/255.0),
2384                                (waveview->wave_b/255.0),
2385                                (waveview->wave_a/255.0));
2386         cairo_stroke (cr);
2387
2388         cairo_destroy (cr);
2389 }
2390
2391 #if 0
2392                 if (clip_max || clip_min) {
2393                         cairo_set_source_rgba (cr, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a);
2394                 }
2395
2396                 if (clip_max) {
2397                         cairo_move_to (cr, xoff, yoff1);
2398                         cairo_line_to (cr, xoff, yoff1 + clip_length);
2399                         cairo_stroke (cr);
2400                 }
2401
2402                 if (clip_min) {
2403                         cairo_move_to (cr, xoff, yoff2);
2404                         cairo_line_to (cr, xoff, yoff2 - clip_length);
2405                         cairo_stroke (cr);
2406                 }
2407
2408 #endif
2409
2410 static void
2411 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
2412 {
2413         GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
2414
2415         *x1 = waveview->x;
2416         *y1 = waveview->y;
2417
2418         *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
2419         *y2 = *y1 + waveview->height;
2420
2421 #if 0
2422         x = 0; y = 0;
2423         gnome_canvas_item_i2w (item, &x, &y);
2424         gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
2425         x = *x2;
2426         y = *y2;
2427         gnome_canvas_item_i2w (item, &x, &y);
2428         gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
2429         printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
2430 #endif
2431
2432 }
2433
2434 static double
2435 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
2436 {
2437         (void) item;
2438         (void) x;
2439         (void) y;
2440         (void) cx;
2441         (void) cy;
2442         (void) actual_item;
2443
2444         /* XXX for now, point is never inside the wave
2445         GnomeCanvasWaveView *waveview;
2446         double x1, y1, x2, y2;
2447         double dx, dy;
2448         */
2449
2450         return DBL_MAX;
2451
2452 #if 0
2453         waveview = GNOME_CANVAS_WAVEVIEW (item);
2454
2455         *actual_item = item;
2456
2457         /* Find the bounds for the rectangle plus its outline width */
2458
2459         gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
2460
2461         /* Is point inside rectangle */
2462
2463         if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
2464                 return 0.0;
2465         }
2466
2467         /* Point is outside rectangle */
2468
2469         if (x < x1)
2470                 dx = x1 - x;
2471         else if (x > x2)
2472                 dx = x - x2;
2473         else
2474                 dx = 0.0;
2475
2476         if (y < y1)
2477                 dy = y1 - y;
2478         else if (y > y2)
2479                 dy = y - y2;
2480         else
2481                 dy = 0.0;
2482
2483         return sqrt (dx * dx + dy * dy);
2484 #endif
2485 }
2486