a boatload of changes and fixes related to SAE menu redesign (several new operations...
[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 <string.h>
25 #include <limits.h>
26
27 #include <ardour/dB.h>
28
29 #include "logmeter.h"
30 #include "canvas-waveview.h"
31 #include "rgb_macros.h"
32
33
34 extern void c_stacktrace();
35
36 enum {
37          PROP_0,
38          PROP_DATA_SRC,
39          PROP_CHANNEL,
40          PROP_LENGTH_FUNCTION,
41          PROP_SOURCEFILE_LENGTH_FUNCTION,
42          PROP_PEAK_FUNCTION,
43          PROP_GAIN_FUNCTION,
44          PROP_GAIN_SRC,
45          PROP_CACHE,
46          PROP_CACHE_UPDATER,
47          PROP_SAMPLES_PER_UNIT,
48          PROP_AMPLITUDE_ABOVE_AXIS,
49          PROP_X,
50          PROP_Y,
51          PROP_HEIGHT,
52          PROP_WAVE_COLOR,
53          PROP_CLIP_COLOR,
54          PROP_ZERO_COLOR,
55          PROP_FILL_COLOR,
56          PROP_FILLED,
57          PROP_RECTIFIED,
58          PROP_ZERO_LINE,
59          PROP_REGION_START,
60          PROP_LOGSCALED,
61 };
62
63 static void gnome_canvas_waveview_class_init     (GnomeCanvasWaveViewClass *class);
64
65 static void gnome_canvas_waveview_init           (GnomeCanvasWaveView      *waveview);
66
67 static void gnome_canvas_waveview_destroy        (GtkObject            *object);
68
69 static void gnome_canvas_waveview_set_property   (GObject        *object,
70                                                    guint           prop_id,
71                                                    const GValue   *value,
72                                                    GParamSpec     *pspec);
73 static void gnome_canvas_waveview_get_property   (GObject        *object,
74                                                    guint           prop_id,
75                                                    GValue         *value,
76                                                    GParamSpec     *pspec);
77
78 static void   gnome_canvas_waveview_update       (GnomeCanvasItem *item,
79                                                    double          *affine,
80                                                    ArtSVP          *clip_path,
81                                                    int              flags);
82
83 static void   gnome_canvas_waveview_bounds       (GnomeCanvasItem *item,
84                                                    double          *x1,
85                                                    double          *y1,
86                                                    double          *x2,
87                                                    double          *y2);
88
89 static double gnome_canvas_waveview_point        (GnomeCanvasItem  *item,
90                                                    double            x,
91                                                    double            y,
92                                                    int               cx,
93                                                    int               cy,
94                                                    GnomeCanvasItem **actual_item);
95
96 static void gnome_canvas_waveview_render         (GnomeCanvasItem *item,
97                                                    GnomeCanvasBuf  *buf);
98
99 static void gnome_canvas_waveview_draw           (GnomeCanvasItem *item,
100                                                    GdkDrawable     *drawable,
101                                                    int              x,
102                                                    int              y,
103                                                    int              w,
104                                                    int              h);
105
106 static void gnome_canvas_waveview_set_data_src   (GnomeCanvasWaveView *,
107                                                    void *);
108
109 static void gnome_canvas_waveview_set_channel    (GnomeCanvasWaveView *,
110                                                    guint32);
111
112 static gint32 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview,
113                                                    gulong               start_sample,
114                                                    gulong               end_sample);
115
116 static GnomeCanvasItemClass *parent_class;
117
118 GType
119 gnome_canvas_waveview_get_type (void)
120 {
121          static GType waveview_type;
122
123          if (!waveview_type) {
124                  static const GTypeInfo object_info = {
125                          sizeof (GnomeCanvasWaveViewClass),
126                          (GBaseInitFunc) NULL,
127                          (GBaseFinalizeFunc) NULL,
128                          (GClassInitFunc) gnome_canvas_waveview_class_init,
129                          (GClassFinalizeFunc) NULL,
130                          NULL,                  /* class_data */
131                          sizeof (GnomeCanvasWaveView),
132                          0,                     /* n_preallocs */
133                          (GInstanceInitFunc) gnome_canvas_waveview_init,
134                          NULL                   /* value_table */
135                  };
136
137                  waveview_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasWaveView",
138                                                          &object_info, 0);
139          }
140
141          return waveview_type;
142  }
143
144 static void
145 gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class)
146 {
147          GObjectClass *gobject_class;
148          GtkObjectClass *object_class;
149          GnomeCanvasItemClass *item_class;
150
151          gobject_class = (GObjectClass *) class;
152          object_class = (GtkObjectClass *) class;
153          item_class = (GnomeCanvasItemClass *) class;
154
155          parent_class = g_type_class_peek_parent (class);
156
157          gobject_class->set_property = gnome_canvas_waveview_set_property;
158          gobject_class->get_property = gnome_canvas_waveview_get_property;
159
160          g_object_class_install_property
161                  (gobject_class,
162                   PROP_DATA_SRC,
163                   g_param_spec_pointer ("data_src", NULL, NULL,
164                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
165          
166          g_object_class_install_property
167                  (gobject_class,
168                   PROP_CHANNEL,
169                   g_param_spec_uint ("channel", NULL, NULL,
170                                      0, G_MAXUINT, 0,
171                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
172          
173          g_object_class_install_property
174                  (gobject_class,
175                   PROP_LENGTH_FUNCTION,
176                   g_param_spec_pointer ("length_function", NULL, NULL,
177                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
178          
179          g_object_class_install_property
180                 (gobject_class,
181                  PROP_SOURCEFILE_LENGTH_FUNCTION,
182                  g_param_spec_pointer ("sourcefile_length_function", NULL, NULL,
183                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
184          
185          g_object_class_install_property
186                  (gobject_class,
187                   PROP_PEAK_FUNCTION,
188                   g_param_spec_pointer ("peak_function", NULL, NULL,
189                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
190          
191          g_object_class_install_property
192                  (gobject_class,
193                   PROP_GAIN_FUNCTION,
194                   g_param_spec_pointer ("gain_function", NULL, NULL,
195                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
196          
197          g_object_class_install_property
198                  (gobject_class,
199                  PROP_GAIN_SRC,
200                  g_param_spec_pointer ("gain_src", NULL, NULL,
201                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
202         
203          g_object_class_install_property
204                  (gobject_class,
205                   PROP_CACHE,
206                   g_param_spec_pointer ("cache", NULL, NULL,
207                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
208          
209          g_object_class_install_property
210                  (gobject_class,
211                   PROP_CACHE_UPDATER,
212                  g_param_spec_boolean ("cache_updater", NULL, NULL,
213                                        FALSE,
214                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
215          
216          g_object_class_install_property
217                  (gobject_class,
218                   PROP_SAMPLES_PER_UNIT,
219                   g_param_spec_double ("samples_per_unit", NULL, NULL,
220                                        0.0, G_MAXDOUBLE, 0.0,
221                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
222          
223          g_object_class_install_property
224                  (gobject_class,
225                   PROP_AMPLITUDE_ABOVE_AXIS,
226                   g_param_spec_double ("amplitude_above_axis", NULL, NULL,
227                                        0.0, G_MAXDOUBLE, 0.0,
228                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
229          
230          g_object_class_install_property
231                  (gobject_class,
232                   PROP_X,
233                   g_param_spec_double ("x", NULL, NULL,
234                                        0.0, G_MAXDOUBLE, 0.0,
235                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
236          
237          g_object_class_install_property
238                  (gobject_class,
239                   PROP_Y,
240                   g_param_spec_double ("y", NULL, NULL,
241                                        0.0, G_MAXDOUBLE, 0.0,
242                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
243          
244          g_object_class_install_property
245                  (gobject_class,
246                   PROP_HEIGHT,
247                   g_param_spec_double ("height", NULL, NULL,
248                                        0.0, G_MAXDOUBLE, 0.0,
249                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
250          
251          g_object_class_install_property
252                  (gobject_class,
253                   PROP_WAVE_COLOR,
254                   g_param_spec_uint ("wave_color", NULL, NULL,
255                                      0, G_MAXUINT, 0,
256                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
257          
258          g_object_class_install_property
259                  (gobject_class,
260                   PROP_CLIP_COLOR,
261                   g_param_spec_uint ("clip_color", NULL, NULL,
262                                      0, G_MAXUINT, 0,
263                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
264          
265          g_object_class_install_property
266                  (gobject_class,
267                   PROP_ZERO_COLOR,
268                   g_param_spec_uint ("zero_color", NULL, NULL,
269                                      0, G_MAXUINT, 0,
270                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
271
272          g_object_class_install_property
273                  (gobject_class,
274                   PROP_FILL_COLOR,
275                   g_param_spec_uint ("fill_color", NULL, NULL,
276                                      0, G_MAXUINT, 0,
277                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
278
279          g_object_class_install_property
280                  (gobject_class,
281                   PROP_FILLED,
282                   g_param_spec_boolean ("filled", NULL, NULL,
283                                         FALSE,
284                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
285          
286          g_object_class_install_property
287                  (gobject_class,
288                   PROP_RECTIFIED,
289                   g_param_spec_boolean ("rectified", NULL, NULL,
290                                         FALSE,
291                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
292
293          g_object_class_install_property
294                  (gobject_class,
295                   PROP_ZERO_LINE,
296                   g_param_spec_boolean ("zero_line", NULL, NULL,
297                                         FALSE,
298                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
299
300          g_object_class_install_property
301                  (gobject_class,
302                   PROP_LOGSCALED,
303                   g_param_spec_boolean ("logscaled", NULL, NULL,
304                                         FALSE,
305                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
306          
307          g_object_class_install_property
308                  (gobject_class,
309                   PROP_REGION_START,
310                   g_param_spec_uint ("region_start", NULL, NULL,
311                                      0, G_MAXUINT, 0,
312                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
313          
314          object_class->destroy = gnome_canvas_waveview_destroy;
315          
316          item_class->update = gnome_canvas_waveview_update;
317          item_class->bounds = gnome_canvas_waveview_bounds;
318          item_class->point = gnome_canvas_waveview_point;
319          item_class->render = gnome_canvas_waveview_render;
320          item_class->draw = gnome_canvas_waveview_draw;
321 }
322
323 GnomeCanvasWaveViewCache*
324 gnome_canvas_waveview_cache_new ()
325 {
326         GnomeCanvasWaveViewCache *c;
327
328         c = g_malloc (sizeof (GnomeCanvasWaveViewCache));
329
330         c->allocated = 2048;
331         c->data = g_malloc (sizeof (GnomeCanvasWaveViewCacheEntry) * c->allocated);
332         c->data_size = 0;
333         c->start = 0;
334         c->end = 0;
335
336         return c;
337 }
338
339 void
340 gnome_canvas_waveview_cache_destroy (GnomeCanvasWaveViewCache* cache)
341 {
342         g_free (cache->data);
343         g_free (cache);
344 }
345
346 static void
347 gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview)
348 {
349         waveview->x = 0.0;
350         waveview->y = 0.0;
351         waveview->cache = 0;
352         waveview->cache_updater = FALSE;
353         waveview->data_src = NULL;
354         waveview->channel = 0;
355         waveview->peak_function = NULL;
356         waveview->length_function = NULL;
357         waveview->sourcefile_length_function = NULL;
358         waveview->gain_curve_function = NULL;
359         waveview->gain_src = NULL;
360         waveview->rectified = FALSE;
361         waveview->logscaled = FALSE;
362         waveview->filled = TRUE;
363         waveview->zero_line = FALSE;
364         waveview->region_start = 0;
365         waveview->samples_per_unit = 1.0;
366         waveview->amplitude_above_axis = 1.0;
367         waveview->height = 100.0;
368         waveview->screen_width = gdk_screen_width ();
369         waveview->reload_cache_in_render = FALSE;
370
371         waveview->wave_color = 0;
372         waveview->clip_color = 0;
373         waveview->zero_color = 0;
374         waveview->fill_color = 0;
375 }
376
377 static void
378 gnome_canvas_waveview_destroy (GtkObject *object)
379 {
380         GnomeCanvasWaveView *waveview;
381
382         g_return_if_fail (object != NULL);
383         g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
384
385         waveview = GNOME_CANVAS_WAVEVIEW (object);
386
387         if (GTK_OBJECT_CLASS (parent_class)->destroy)
388                 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
389 }
390
391 #define DEBUG_CACHE 0
392 #undef CACHE_MEMMOVE_OPTIMIZATION
393
394 static gint32
395 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_sample, gulong end_sample)
396 {
397         gulong required_cache_entries;
398         gulong rf1, rf2,rf3, required_frames;
399         gulong new_cache_start, new_cache_end;
400         gulong half_width;
401         gulong npeaks;
402         gulong offset;
403         gulong ostart;
404         gulong copied;
405         GnomeCanvasWaveViewCache *cache;
406         float* gain;
407 #ifdef CACHE_MEMMOVE_OPTIMIZATION
408         gulong present_frames;
409         gulong present_entries;
410 #endif
411
412         cache = waveview->cache;
413
414         start_sample = start_sample + waveview->region_start;
415         end_sample = end_sample + waveview->region_start;
416 #if DEBUG_CACHE
417         // printf("waveview->region_start == %lu\n",waveview->region_start);
418         // c_stacktrace ();
419         printf ("\n\n=> 0x%x cache @ 0x%x range: %lu - %lu request: %lu - %lu (%lu frames)\n", 
420                 waveview, cache,
421                 cache->start, cache->end,
422                 start_sample, end_sample, end_sample - start_sample);
423 #endif
424                 
425         if (cache->start <= start_sample && cache->end >= end_sample) {
426 #if DEBUG_CACHE
427                 // printf ("0x%x: cache hit for %lu-%lu (cache holds: %lu-%lu\n",
428                 // waveview, start_sample, end_sample, cache->start, cache->end);
429 #endif
430                 goto out;
431         }
432
433         /* make sure the cache is at least twice as wide as the screen width, and put the start sample
434            in the middle, ensuring that we cover the end_sample. 
435         */
436
437         /* Note the assumption that we have a 1:1 units:pixel ratio for the canvas. Its everywhere ... */
438         
439         half_width = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit)/2.0 + 0.5);
440         
441         if (start_sample < half_width) {
442                 new_cache_start = 0;
443         } else {
444                 new_cache_start = start_sample - half_width;
445         }
446
447         /* figure out how many frames we want */
448
449         rf1 = end_sample - start_sample + 1;
450         rf2 = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit * 2.0f));
451         required_frames = MAX(rf1,rf2);
452
453         /* but make sure it doesn't extend beyond the end of the source material */
454
455         rf3 = (gulong) (waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit)) + 1;
456         if (rf3 < new_cache_start) {
457                 rf3 = 0;
458         } else {
459                 rf3 -= new_cache_start;
460         }
461
462 #if DEBUG_CACHE
463         fprintf (stderr, "AVAILABLE FRAMES = %lu of %lu, start = %lu, sstart = %lu, cstart = %lu\n", 
464                  rf3, waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit),
465                  waveview->region_start, start_sample, new_cache_start);
466 #endif
467
468         required_frames = MIN(required_frames,rf3);
469
470         new_cache_end = new_cache_start + required_frames - 1;
471
472         required_cache_entries = (gulong) floor (required_frames / waveview->samples_per_unit );
473
474 #if DEBUG_CACHE
475         fprintf (stderr, "new cache = %lu - %lu\n", new_cache_start, new_cache_end);
476         fprintf(stderr,"required_cach_entries = %lu, samples_per_unit = %f req frames = %lu\n",
477                 required_cache_entries,waveview->samples_per_unit, required_frames);
478 #endif
479
480         if (required_cache_entries > cache->allocated) {
481                 cache->data = g_realloc (cache->data, sizeof (GnomeCanvasWaveViewCacheEntry) * required_cache_entries);
482                 cache->allocated = required_cache_entries;
483                 // cache->start = 0;
484                 // cache->end = 0;
485         }
486
487         ostart = new_cache_start;
488
489 #ifdef CACHE_MEMMOVE_OPTIMIZATION
490         
491         /* data is not entirely in the cache, so go fetch it, making sure to fill the cache */
492
493         /* some of the required cache entries are in the cache, but in the wrong
494            locations. use memmove to fix this.
495         */
496
497         if (cache->start < new_cache_start && new_cache_start < cache->end) {
498                 
499                 /* case one: the common area is at the end of the existing cache. move it 
500                    to the beginning of the cache, and set up to refill whatever remains.
501                    
502                    
503                            wv->cache_start                                        wv->cache_end
504                            |-------------------------------------------------------| cache
505                                                                |--------------------------------| requested
506                                                                <------------------->
507                                                                      "present"
508                                                             new_cache_start                      new_cache_end       
509                 */
510                                 
511
512                 present_frames = cache->end - new_cache_start;
513                 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
514
515 #if DEBUG_CACHE         
516                 fprintf (stderr, "existing material at end of current cache, move to start of new cache\n"
517                          "\tcopy from %lu to start\n", cache->data_size - present_entries);
518 #endif
519
520                 memmove (&cache->data[0],
521                          &cache->data[cache->data_size - present_entries],
522                          present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
523                 
524 #if DEBUG_CACHE
525                 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
526                          present_frames, required_frames, present_entries, new_cache_start + present_entries,
527                          cache->data + present_entries);
528 #endif
529
530                 copied = present_entries;
531                 offset = present_entries;
532                 new_cache_start += present_frames;
533                 required_frames -= present_frames;
534
535         } else if (new_cache_end > cache->start && new_cache_end < cache->end) {
536
537                 /* case two: the common area lives at the beginning of the existing cache. 
538                    
539                                             wv->cache_start                                      wv->cache_end
540                                              |-----------------------------------------------------|
541                               |--------------------------------|
542                                              <----------------->
543                                                 "present"
544
545                              new_cache_start                      new_cache_end
546                 */
547                 
548                 present_frames = new_cache_end - cache->start;
549                 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
550
551                 memmove (&cache->data[cache->data_size - present_entries],
552                          &cache->data[0],
553                          present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
554                 
555 #if DEBUG_CACHE         
556                 fprintf (stderr, "existing material at start of current cache, move to start of end cache\n");
557 #endif
558
559 #if DEBUG_CACHE
560                 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
561                          present_entries, required_frames, present_entries, new_cache_start + present_entries,
562                          cache->data + present_entries);
563 #endif
564
565                 copied = present_entries;
566                 offset = 0;
567                 required_frames -= present_frames;
568
569                 
570         } else {
571                 copied = 0;
572                 offset = 0;
573
574         }
575
576 #else
577         copied = 0;
578         offset = 0;
579
580 #endif /* CACHE_MEMMOVE_OPTIMIZATION */
581
582 //      fprintf(stderr,"length == %lu\n",waveview->length_function (waveview->data_src));
583 //      required_frames = MIN (waveview->length_function (waveview->data_src) - new_cache_start, required_frames);
584
585         npeaks = (gulong) floor (required_frames / waveview->samples_per_unit);
586         required_frames = npeaks * waveview->samples_per_unit;
587
588 #if DEBUG_CACHE
589
590
591         printf ("requesting %lu/%f to cover %lu-%lu at %f spu (request was %lu-%lu) into cache + %lu\n",
592                 required_frames, required_frames/waveview->samples_per_unit, new_cache_start, new_cache_end,
593                 waveview->samples_per_unit, start_sample, end_sample, offset);
594 #endif
595
596 #if DEBUG_CACHE
597 //      printf ("cache holds %lu entries, requesting %lu to cover %lu-%lu (request was %lu-%lu)\n",
598 //              cache->data_size, npeaks, new_cache_start, new_cache_end,
599 //              start_sample, end_sample);
600 #endif
601
602         if (required_frames) {
603                 waveview->peak_function (waveview->data_src, npeaks, new_cache_start, required_frames, cache->data + offset, waveview->channel,waveview->samples_per_unit);
604
605                 /* take into account any copied peaks */
606                 
607                 npeaks += copied;
608         } else {
609                 npeaks = copied;
610         }
611
612         if (npeaks < cache->allocated) {
613 #if DEBUG_CACHE
614                 fprintf (stderr, "zero fill cache for %lu at %lu\n", cache->allocated - npeaks, npeaks);
615 #endif
616                 memset (&cache->data[npeaks], 0, sizeof (GnomeCanvasWaveViewCacheEntry) * (cache->allocated - npeaks));
617                 cache->data_size = npeaks;
618         } else {
619                 cache->data_size = cache->allocated;
620         }
621
622         if (waveview->gain_curve_function) {
623                 guint32 n;
624
625                 gain = (float*) malloc (sizeof (float) * cache->data_size);
626
627                 waveview->gain_curve_function (waveview->gain_src, new_cache_start, new_cache_end, gain, cache->data_size);
628
629                 for (n = 0; n < cache->data_size; ++n) {
630                         cache->data[n].min *= gain[n];
631                         cache->data[n].max *= gain[n];
632                 }
633
634                 free (gain);
635         
636         }
637
638         /* do optional log scaling.  this implementation is not particularly efficient */
639         
640         if (waveview->logscaled) {
641                 guint32 n;
642                 GnomeCanvasWaveViewCacheEntry* buf = cache->data;
643                 
644                 for (n = 0; n < cache->data_size; ++n) {
645
646                         if (buf[n].max > 0.0f) {
647                                 buf[n].max = alt_log_meter(coefficient_to_dB(buf[n].max));
648                         } else if (buf[n].max < 0.0f) {
649                                 buf[n].max = -alt_log_meter(coefficient_to_dB(-buf[n].max));
650                         }
651                         
652                         if (buf[n].min > 0.0f) {
653                                 buf[n].min = alt_log_meter(coefficient_to_dB(buf[n].min));
654                         } else if (buf[n].min < 0.0f) {
655                                 buf[n].min = -alt_log_meter(coefficient_to_dB(-buf[n].min));
656                         }
657                 }
658         }
659
660         cache->start = ostart;
661         cache->end = new_cache_end;
662
663   out:
664 #if DEBUG_CACHE
665         fprintf (stderr, "return cache index = %d\n", 
666                  (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5));
667 #endif
668         return (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5);
669
670 }
671
672 void
673 gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *waveview, void *data_src)
674 {
675
676         if (waveview->cache_updater) {
677                 if (waveview->data_src == data_src) {
678                         waveview->reload_cache_in_render = TRUE;
679                         return;
680                 }
681         
682                 waveview->cache->start  = 0;
683                 waveview->cache->end = 0;
684         }
685
686         waveview->data_src = data_src;
687 }
688
689 void
690 gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *waveview, guint32 chan)
691 {
692         if (waveview->channel == chan) {
693                 return;
694         }
695         
696         waveview->channel = chan;
697 }
698
699 static void 
700 gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
701
702 {
703         double x1, x2, y1, y2;
704         ArtPoint i1, i2;
705         ArtPoint w1, w2;
706         int Ix1, Ix2, Iy1, Iy2;
707         double i2w[6];
708
709         gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
710
711         i1.x = x1;
712         i1.y = y1;
713         i2.x = x2;
714         i2.y = y2;
715
716         gnome_canvas_item_i2w_affine (item, i2w);
717         art_affine_point (&w1, &i1, i2w);
718         art_affine_point (&w2, &i2, i2w);
719
720         Ix1 = (int) rint(w1.x);
721         Ix2 = (int) rint(w2.x);
722         Iy1 = (int) rint(w1.y);
723         Iy2 = (int) rint(w2.y);
724
725         gnome_canvas_update_bbox (item, Ix1, Iy1, Ix2, Iy2);
726 }
727
728 /* 
729  * CANVAS CALLBACKS 
730  */
731
732 static void
733 gnome_canvas_waveview_set_property (GObject      *object,
734                                     guint         prop_id,
735                                     const GValue *value,
736                                     GParamSpec   *pspec)
737
738 {
739         GnomeCanvasItem *item;
740         GnomeCanvasWaveView *waveview;
741         int redraw = FALSE;
742         int calc_bounds = FALSE;
743
744         g_return_if_fail (object != NULL);
745         g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
746
747         item = GNOME_CANVAS_ITEM (object);
748         waveview = GNOME_CANVAS_WAVEVIEW (object);
749
750         switch (prop_id) {
751         case PROP_DATA_SRC:
752                 gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
753                 redraw = TRUE;
754                 break;
755
756         case PROP_CHANNEL:
757                 gnome_canvas_waveview_set_channel (waveview, g_value_get_uint(value));
758                 redraw = TRUE;
759                 break;
760
761         case PROP_LENGTH_FUNCTION:
762                 waveview->length_function = g_value_get_pointer(value);
763                 redraw = TRUE;
764                 break;
765         case PROP_SOURCEFILE_LENGTH_FUNCTION:
766                 waveview->sourcefile_length_function = g_value_get_pointer(value);
767                 redraw = TRUE;
768                 break;
769
770         case PROP_PEAK_FUNCTION:
771                 waveview->peak_function = g_value_get_pointer(value);
772                 redraw = TRUE;
773                 break;
774
775         case PROP_GAIN_FUNCTION:
776                 waveview->gain_curve_function = g_value_get_pointer(value);
777                 redraw = TRUE;
778                 break;
779
780         case PROP_GAIN_SRC:
781                 waveview->gain_src = g_value_get_pointer(value);
782                 if (waveview->cache_updater) {
783                         waveview->cache->start = 0;
784                         waveview->cache->end = 0;
785                 }
786                 redraw = TRUE;
787                 calc_bounds = TRUE;
788                 break;
789
790         case PROP_CACHE:
791                 waveview->cache = g_value_get_pointer(value);
792                 redraw = TRUE;
793                 break;
794
795
796         case PROP_CACHE_UPDATER:
797                 waveview->cache_updater = g_value_get_boolean(value);
798                 redraw = TRUE;
799                 break;
800
801         case PROP_SAMPLES_PER_UNIT:
802                 if ((waveview->samples_per_unit = g_value_get_double(value)) < 1.0) {
803                         waveview->samples_per_unit = 1.0;
804                 }
805                 if (waveview->cache_updater) {
806                         waveview->cache->start = 0;
807                         waveview->cache->end = 0;
808                 }
809                 redraw = TRUE;
810                 calc_bounds = TRUE;
811                 break;
812
813         case PROP_AMPLITUDE_ABOVE_AXIS:
814                 waveview->amplitude_above_axis = g_value_get_double(value);
815                 redraw = TRUE;
816                 break;
817
818         case PROP_X:
819                 if (waveview->x != g_value_get_double (value)) {
820                         waveview->x = g_value_get_double (value);
821                         calc_bounds = TRUE;
822                 }
823                 break;
824
825         case PROP_Y:
826                 if (waveview->y != g_value_get_double (value)) {
827                         waveview->y = g_value_get_double (value);
828                         calc_bounds = TRUE;
829                 }
830                 break;
831
832         case PROP_HEIGHT:
833                 if (waveview->height != fabs (g_value_get_double (value))) {
834                         waveview->height = fabs (g_value_get_double (value));
835                         redraw = TRUE;
836                 }
837                 break;
838
839         case PROP_WAVE_COLOR:
840                 if (waveview->wave_color != g_value_get_uint(value)) {
841                         waveview->wave_color = g_value_get_uint(value);
842                         redraw = TRUE;
843                 }
844                 break;
845
846         case PROP_CLIP_COLOR:
847                 if (waveview->clip_color != g_value_get_uint(value)) {
848                         waveview->clip_color = g_value_get_uint(value);
849                         redraw = TRUE;
850                 }
851                 break;
852
853         case PROP_ZERO_COLOR:
854                 if (waveview->zero_color != g_value_get_uint(value)) {
855                         waveview->zero_color = g_value_get_uint(value);
856                         redraw = TRUE;
857                 }
858                 break;
859
860         case PROP_FILL_COLOR:
861                 if (waveview->fill_color != g_value_get_uint(value)) {
862                         waveview->fill_color = g_value_get_uint(value);
863                         redraw = TRUE;
864                 }
865                 break;
866
867         case PROP_FILLED:
868                 if (waveview->filled != g_value_get_boolean(value)) {
869                         waveview->filled = g_value_get_boolean(value);
870                         redraw = TRUE;
871                 }
872                 break;
873
874         case PROP_RECTIFIED:
875                 if (waveview->rectified != g_value_get_boolean(value)) {
876                         waveview->rectified = g_value_get_boolean(value);
877                         redraw = TRUE;
878                 }
879                 break;
880
881         case PROP_ZERO_LINE:
882                 if (waveview->zero_line != g_value_get_boolean(value)) {
883                         waveview->zero_line = g_value_get_boolean(value);
884                         redraw = TRUE;
885                 }
886                 break;
887
888         case PROP_LOGSCALED:
889                 if (waveview->logscaled != g_value_get_boolean(value)) {
890                         waveview->logscaled = g_value_get_boolean(value);
891                         if (waveview->cache_updater) {
892                                 waveview->cache->start = 0;
893                                 waveview->cache->end = 0;
894                         }
895                         redraw = TRUE;
896                         calc_bounds = TRUE;
897                 }
898                 break;
899         case PROP_REGION_START:
900                 waveview->region_start = g_value_get_uint(value);
901                 redraw = TRUE;
902                 calc_bounds = TRUE;
903                 break;
904
905
906         default:
907                 break;
908         }
909
910         if (calc_bounds) {
911                 gnome_canvas_waveview_reset_bounds (item);
912         }
913
914         if (redraw) {
915                 gnome_canvas_item_request_update (item);
916         }
917
918 }
919
920 static void
921 gnome_canvas_waveview_get_property (GObject      *object,
922                                     guint         prop_id,
923                                     GValue       *value,
924                                     GParamSpec   *pspec)
925 {
926         
927    
928         g_return_if_fail (object != NULL);
929         g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
930
931         GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
932
933         switch (prop_id) {
934         case PROP_DATA_SRC:
935                 g_value_set_pointer(value, waveview->data_src);
936                 break;
937
938         case PROP_CHANNEL:
939                 g_value_set_uint(value, waveview->channel);
940                 break;
941
942         case PROP_LENGTH_FUNCTION:
943                 g_value_set_pointer(value, waveview->length_function);
944                 break;
945
946         case PROP_SOURCEFILE_LENGTH_FUNCTION:
947                 g_value_set_pointer(value, waveview->sourcefile_length_function);
948                 break;
949
950         case PROP_PEAK_FUNCTION:
951                 g_value_set_pointer(value, waveview->peak_function);
952                 break;
953
954         case PROP_GAIN_FUNCTION:
955                 g_value_set_pointer(value, waveview->gain_curve_function);
956                 break;
957
958         case PROP_GAIN_SRC:
959                 g_value_set_pointer(value, waveview->gain_src);
960                 break;
961
962         case PROP_CACHE:
963                 g_value_set_pointer(value, waveview->cache);
964                 break;
965
966         case PROP_CACHE_UPDATER:
967                 g_value_set_boolean(value, waveview->cache_updater);
968                 break;
969
970         case PROP_SAMPLES_PER_UNIT:
971                 g_value_set_double(value, waveview->samples_per_unit);
972                 break;
973
974         case PROP_AMPLITUDE_ABOVE_AXIS:
975                 g_value_set_double(value, waveview->amplitude_above_axis);
976                 break;
977
978         case PROP_X:
979                 g_value_set_double (value, waveview->x);
980                 break;
981
982         case PROP_Y:
983                 g_value_set_double (value, waveview->y);
984                 break;
985
986         case PROP_HEIGHT:
987                 g_value_set_double (value, waveview->height);
988                 break;
989
990         case PROP_WAVE_COLOR:
991                 g_value_set_uint (value, waveview->wave_color);
992                 break;
993
994         case PROP_CLIP_COLOR:
995                 g_value_set_uint (value, waveview->clip_color);
996                 break;
997
998         case PROP_ZERO_COLOR:
999                 g_value_set_uint (value, waveview->zero_color);
1000                 break;
1001
1002         case PROP_FILL_COLOR:
1003                 g_value_set_uint (value, waveview->fill_color);
1004                 break;
1005
1006         case PROP_FILLED:
1007                 g_value_set_boolean (value, waveview->filled);
1008                 break;
1009
1010         case PROP_RECTIFIED:
1011                 g_value_set_boolean (value, waveview->rectified);
1012                 break;
1013
1014         case PROP_ZERO_LINE:
1015                 g_value_set_boolean (value, waveview->zero_line);
1016                 break;
1017
1018         case PROP_LOGSCALED:
1019                 g_value_set_boolean (value, waveview->logscaled);
1020                 break;
1021
1022         case PROP_REGION_START:
1023                 g_value_set_uint (value, waveview->region_start);
1024                 break;
1025
1026         default:
1027                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1028                 break;
1029         }
1030 }
1031
1032 static void
1033 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1034 {
1035         GnomeCanvasWaveView *waveview;
1036         double x, y;
1037
1038         waveview = GNOME_CANVAS_WAVEVIEW (item);
1039
1040 //      check_cache (waveview, "start of update");
1041
1042         if (parent_class->update)
1043                 (* parent_class->update) (item, affine, clip_path, flags);
1044
1045         gnome_canvas_waveview_reset_bounds (item);
1046
1047         /* get the canvas coordinates of the view. Do NOT use affines
1048            for this, because they do not round to the integer units used
1049            by the canvas, resulting in subtle pixel-level errors later.
1050         */
1051
1052         x = waveview->x;
1053         y = waveview->y;
1054
1055         gnome_canvas_item_i2w (item, &x, &y);
1056         gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
1057
1058         waveview->samples = waveview->length_function (waveview->data_src);
1059
1060         x = waveview->x + (waveview->samples / waveview->samples_per_unit);
1061         y = waveview->y + waveview->height;
1062
1063         gnome_canvas_item_i2w (item, &x, &y);
1064         gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
1065
1066         /* cache the half-height and the end point in canvas units */
1067
1068         waveview->half_height = waveview->height / 2.0;
1069
1070         /* parse the color */
1071
1072         UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
1073                       &waveview->wave_a);
1074         UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
1075                       &waveview->clip_a);
1076         UINT_TO_RGBA (waveview->fill_color, &waveview->fill_r, &waveview->fill_g, &waveview->fill_b,
1077                       &waveview->fill_a);
1078
1079 //      check_cache (waveview, "end of update");
1080 }                                  
1081
1082 static void
1083 gnome_canvas_waveview_render (GnomeCanvasItem *item,
1084                             GnomeCanvasBuf *buf)
1085 {
1086         GnomeCanvasWaveView *waveview;
1087         gulong s1, s2;
1088         int clip_length = 0;
1089         int pymin, pymax;
1090         int cache_index;
1091         double half_height;
1092         int x, end, begin;
1093         int zbegin, zend;
1094         char rectify;
1095
1096         waveview = GNOME_CANVAS_WAVEVIEW (item);
1097
1098 //      check_cache (waveview, "start of render");
1099
1100         if (parent_class->render) {
1101                 (*parent_class->render) (item, buf);
1102         }
1103
1104         if (buf->is_bg) {
1105                 gnome_canvas_buf_ensure_buf (buf);
1106                 buf->is_bg = FALSE;
1107         }
1108
1109         begin = MAX(waveview->bbox_ulx, buf->rect.x0);
1110
1111         if (begin == waveview->bbox_ulx) {
1112                 zbegin = begin + 1;
1113         } else {
1114                 zbegin = begin;
1115         }
1116
1117         if (waveview->bbox_lrx >= 0) {
1118                 end = MIN(waveview->bbox_lrx,buf->rect.x1);
1119         } else {
1120                 end = buf->rect.x1;
1121         }
1122
1123         if (end == waveview->bbox_lrx) {
1124                 zend = end - 1;
1125         } else {
1126                 zend = end;
1127         }
1128
1129         if (begin == end) {
1130                 return;
1131         }
1132
1133         s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1134
1135         // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
1136
1137         if (end == waveview->bbox_lrx) {
1138                 /* This avoids minor rounding errors when we have the
1139                    entire region visible.
1140                 */
1141                 s2 = waveview->samples;
1142         } else {
1143                 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
1144         }
1145
1146 #if 0
1147         printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
1148                 " b/e %d..%d s= %lu..%lu @ %f\n",
1149                 waveview,
1150                 buf->rect.x0,
1151                 buf->rect.x1,
1152                 buf->rect.y0,
1153                 buf->rect.y1,
1154                 waveview->bbox_ulx,
1155                 waveview->bbox_lrx,
1156                 waveview->bbox_uly,
1157                 waveview->bbox_lry,
1158                 begin, end, s1, s2,
1159                 waveview->samples_per_unit);
1160 #endif
1161
1162         /* now ensure that the cache is full and properly
1163            positioned.
1164         */
1165
1166 //      check_cache (waveview, "pre-ensure");
1167
1168         if (waveview->cache_updater && waveview->reload_cache_in_render) {
1169                 waveview->cache->start = 0;
1170                 waveview->cache->end = 0;
1171                 waveview->reload_cache_in_render = FALSE;
1172         }
1173
1174 //      check_cache (waveview, "post-ensure");
1175
1176         /* don't rectify at single-sample zoom */
1177         if(waveview->rectified && waveview->samples_per_unit > 1) {
1178                 rectify = TRUE;
1179         }
1180         else {
1181                 rectify = FALSE;
1182         }
1183
1184         clip_length = MIN(5,(waveview->height/4));
1185
1186         /* 
1187            Now draw each line, clipping it appropriately. The clipping
1188            is done by the macros PAINT_FOO().
1189         */
1190
1191         half_height = waveview->half_height;
1192
1193 /* this makes it slightly easier to comprehend whats going on */
1194 #define origin half_height
1195
1196         if(waveview->filled && !rectify) {
1197                 int prev_pymin = 1;
1198                 int prev_pymax = 0;
1199                 int last_pymin = 1;
1200                 int last_pymax = 0;
1201                 int next_pymin, next_pymax;
1202                 double max, min;
1203                 int next_clip_max = 0;
1204                 int next_clip_min = 0;
1205
1206                 if(s1 < waveview->samples_per_unit) {
1207                         /* we haven't got a prev vars to compare with, so outline the whole line here */
1208                         prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1209                         prev_pymin = prev_pymax;
1210                 }
1211                 else {
1212                         s1 -= waveview->samples_per_unit;
1213                 }
1214
1215                 if(end == waveview->bbox_lrx) {
1216                         /* we don't have the NEXT vars for the last sample */
1217                         last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1218                         last_pymin = last_pymax;
1219                 }
1220                 else {
1221                         s2 += waveview->samples_per_unit;
1222                 }
1223
1224                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1225
1226                 /*
1227                  * Compute the variables outside the rendering rect
1228                  */
1229                 if(prev_pymax != prev_pymin) {
1230                         prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1231                         prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1232                         ++cache_index;
1233                 }
1234                 if(last_pymax != last_pymin) {
1235                         /* take the index of one sample right of what we render */
1236                         int index = cache_index + (end - begin);
1237                         
1238                         last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1239                         last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1240                 }
1241
1242                 /* 
1243                  * initialize NEXT* variables for the first run, duplicated in the loop for speed
1244                  */
1245                 max = waveview->cache->data[cache_index].max;
1246                 min = waveview->cache->data[cache_index].min;
1247                 
1248                 if (max >= 1.0) {
1249                         max = 1.0;
1250                         next_clip_max = 1;
1251                 }
1252                 
1253                 if (min <= -1.0) {
1254                         min = -1.0;
1255                         next_clip_min = 1;
1256                 }
1257                 
1258                 max *= half_height;
1259                 min *= half_height;
1260                 
1261                 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1262                 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1263
1264                 /*
1265                  * And now the loop
1266                  */
1267                 for(x = begin; x < end; ++x) {
1268                         int clip_max = next_clip_max;
1269                         int clip_min = next_clip_min;
1270                         int fill_max, fill_min;
1271
1272                         pymax = next_pymax;
1273                         pymin = next_pymin;
1274
1275                         /* compute next */
1276                         if(x == end - 1) {
1277                                 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1278                                 next_pymax = last_pymax;
1279                                 next_pymin = last_pymin;
1280                         }
1281                         else {
1282                                 ++cache_index;
1283
1284                                 max = waveview->cache->data[cache_index].max;
1285                                 min = waveview->cache->data[cache_index].min;
1286
1287                                 next_clip_max = 0;
1288                                 next_clip_min = 0;
1289
1290                                 if (max >= 1.0) {
1291                                         max = 1.0;
1292                                         next_clip_max = 1;
1293                                 }
1294                                 
1295                                 if (min <= -1.0) {
1296                                         min = -1.0;
1297                                         next_clip_min = 1;
1298                                 }
1299
1300                                 max *= half_height;
1301                                 min *= half_height;
1302                                 
1303                                 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1304                                 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1305                         }
1306                         
1307                         /* render */
1308                         if (pymax == pymin) {
1309                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1310                         } else {
1311                                 if((prev_pymax < pymax && next_pymax < pymax) ||
1312                                    (prev_pymax == pymax && next_pymax == pymax)) {
1313                                         fill_max = pymax + 1;
1314                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1315                                 }
1316                                 else {
1317                                         fill_max = MAX(prev_pymax, next_pymax);
1318                                         if(pymax == fill_max) {
1319                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1320                                                 ++fill_max;
1321                                         }
1322                                         else {
1323                                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1324                                         }
1325                                 }
1326
1327                                 if((prev_pymin > pymin && next_pymin > pymin) ||
1328                                    (prev_pymin == pymin && next_pymin == pymin)) {
1329                                         fill_min = pymin - 1;
1330                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1331                                 }
1332                                 else {
1333                                         fill_min = MIN(prev_pymin, next_pymin);
1334                                         if(pymin == fill_min) {
1335                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1336                                         }
1337                                         else {
1338                                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin);
1339                                         }
1340                                 }
1341
1342                                 if(fill_max < fill_min) {
1343                                         PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min);
1344                                 }
1345                                 else if(fill_max == fill_min) {
1346                                         PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1347                                 }
1348                         }
1349
1350                         if (clip_max) {
1351                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1352                         }
1353                         
1354                         if (clip_min) {
1355                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1356                         }
1357
1358                         prev_pymax = pymax;
1359                         prev_pymin = pymin;
1360                 }
1361         }
1362         else if(waveview->filled && rectify) {
1363                 int prev_pymax = -1;
1364                 int last_pymax = -1;
1365                 int next_pymax;
1366                 double max, min;
1367                 int next_clip_max = 0;
1368                 int next_clip_min = 0;
1369
1370                 // for rectified, this stays constant throughout the loop
1371                 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1372
1373                 if(s1 < waveview->samples_per_unit) {
1374                         /* we haven't got a prev vars to compare with, so outline the whole line here */
1375                         prev_pymax = pymin;
1376                 }
1377                 else {
1378                         s1 -= waveview->samples_per_unit;
1379                 }
1380
1381                 if(end == waveview->bbox_lrx) {
1382                         /* we don't have the NEXT vars for the last sample */
1383                         last_pymax = pymin;
1384                 }
1385                 else {
1386                         s2 += waveview->samples_per_unit;
1387                 }
1388
1389                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1390
1391                 /*
1392                  * Compute the variables outside the rendering rect
1393                  */
1394                 if(prev_pymax < 0) {
1395                         max = MIN(waveview->cache->data[cache_index].max, 1.0);
1396                         min = MAX(waveview->cache->data[cache_index].min, -1.0);
1397
1398                         if (fabs (min) > fabs (max)) {
1399                                 max = fabs (min);
1400                         }
1401
1402                         prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1403                         ++cache_index;
1404                 }
1405                 if(last_pymax < 0) {
1406                         /* take the index of one sample right of what we render */
1407                         int index = cache_index + (end - begin);
1408                         
1409                         max = MIN(waveview->cache->data[index].max, 1.0);
1410                         min = MAX(waveview->cache->data[index].min, -1.0);
1411
1412                         if (fabs (min) > fabs (max)) {
1413                                 max = fabs (min);
1414                         }
1415
1416                         last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1417                 }
1418
1419                 /* 
1420                  * initialize NEXT* variables for the first run, duplicated in the loop for speed
1421                  */
1422                 max = waveview->cache->data[cache_index].max;
1423                 min = waveview->cache->data[cache_index].min;
1424                 
1425                 if (max >= 1.0) {
1426                         max = 1.0;
1427                         next_clip_max = 1;
1428                 }
1429                 
1430                 if (min <= -1.0) {
1431                         min = -1.0;
1432                         next_clip_min = 1;
1433                 }
1434                 
1435                 if (fabs (min) > fabs (max)) {
1436                         max = fabs (min);
1437                 } 
1438                 
1439                 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1440
1441                 /*
1442                  * And now the loop
1443                  */
1444                 for(x = begin; x < end; ++x) {
1445                         int clip_max = next_clip_max;
1446                         int clip_min = next_clip_min;
1447                         int fill_max;
1448
1449                         pymax = next_pymax;
1450
1451                         /* compute next */
1452                         if(x == end - 1) {
1453                                 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1454                                 next_pymax = last_pymax;
1455                         }
1456                         else {
1457                                 ++cache_index;
1458
1459                                 max = waveview->cache->data[cache_index].max;
1460                                 min = waveview->cache->data[cache_index].min;
1461                                 
1462                                 if (max >= 1.0) {
1463                                         max = 1.0;
1464                                         next_clip_max = 1;
1465                                 }
1466                                 
1467                                 if (min <= -1.0) {
1468                                         min = -1.0;
1469                                         next_clip_min = 1;
1470                                 }
1471                                 
1472                                 if (fabs (min) > fabs (max)) {
1473                                         max = fabs (min);
1474                                 } 
1475                                 
1476                                 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1477                         }
1478                         
1479                         /* render */
1480                         if (pymax == pymin) {
1481                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1482                         } else {
1483                                 if((prev_pymax < pymax && next_pymax < pymax) ||
1484                                    (prev_pymax == pymax && next_pymax == pymax)) {
1485                                         fill_max = pymax + 1;
1486                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1487                                 }
1488                                 else {
1489                                         fill_max = MAX(prev_pymax, next_pymax);
1490                                         if(pymax == fill_max) {
1491                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1492                                                 ++fill_max;
1493                                         }
1494                                         else {
1495                                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1496                                         }
1497                                 }
1498
1499                                 if(fill_max < pymin) {
1500                                         PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
1501                                 }
1502                                 else if(fill_max == pymin) {
1503                                         PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
1504                                 }
1505                         }
1506
1507                         if (clip_max) {
1508                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1509                         }
1510                         
1511                         if (clip_min) {
1512                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1513                         }
1514
1515                         prev_pymax = pymax;
1516                 }
1517         }
1518         else {
1519                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1520
1521                 for (x = begin; x < end; x++) {
1522                         
1523                         double max, min;
1524                         int clip_max, clip_min;
1525                         
1526                         clip_max = 0;
1527                         clip_min = 0;
1528                         
1529                         max = waveview->cache->data[cache_index].max;
1530                         min = waveview->cache->data[cache_index].min;
1531                         
1532                         if (max >= 1.0) {
1533                                 max = 1.0;
1534                                 clip_max = 1;
1535                         }
1536                         
1537                         if (min <= -1.0) {
1538                                 min = -1.0;
1539                                 clip_min = 1;
1540                         }
1541                         
1542                         if (rectify) {
1543                                 
1544                                 if (fabs (min) > fabs (max)) {
1545                                         max = fabs (min);
1546                                 } 
1547                                 
1548                                 max = max * waveview->height;
1549                                 
1550                                 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1551                                 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1552                                 
1553                         } else {
1554                                 
1555                                 max = max * half_height;
1556                                 min = min * half_height;
1557                                 
1558                                 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1559                                 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1560                         }
1561                         
1562                         /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1563                            or, if samples_per_unit == 1, then a dot at each location.
1564                         */
1565                         
1566                         if (pymax == pymin) {
1567                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1568                         } else {
1569                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1570                         }
1571                         
1572                         /* show clipped waveforms with small red lines */
1573                         
1574                         if (clip_max) {
1575                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1576                         }
1577                         
1578                         if (clip_min) {
1579                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1580                         }
1581
1582                         /* presto, we're done */
1583                 
1584                         cache_index++;
1585                 }
1586         }
1587
1588         if (!waveview->rectified && waveview->zero_line) {
1589                 // Paint zeroline.
1590                 //PAINT_HORIZA(buf, waveview->zero_r, waveview->zero_g, waveview->zero_b, waveview->zero_a, begin, endi-1, origin );
1591                 
1592                 unsigned char zero_r, zero_g, zero_b, zero_a;
1593                 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a );
1594                 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1595                 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, end, zeroline_y);
1596         }
1597 #undef origin
1598
1599 }
1600
1601 static void
1602 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1603                           GdkDrawable *drawable,
1604                           int x, int y,
1605                           int width, int height)
1606 {
1607         GnomeCanvasWaveView *waveview;
1608
1609         waveview = GNOME_CANVAS_WAVEVIEW (item);
1610
1611         if (parent_class->draw) {
1612                 (* parent_class->draw) (item, drawable, x, y, width, height);
1613         }
1614
1615         fprintf (stderr, "please don't use the CanvasWaveView item in a non-aa Canvas\n");
1616         abort ();
1617 }
1618
1619 static void
1620 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1621 {
1622         GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1623
1624         *x1 = waveview->x;
1625         *y1 = waveview->y;
1626
1627         *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1628         *y2 = *y1 + waveview->height;
1629
1630 #if 0
1631         x = 0; y = 0;
1632         gnome_canvas_item_i2w (item, &x, &y);
1633         gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1634         x = *x2;
1635         y = *y2;
1636         gnome_canvas_item_i2w (item, &x, &y);
1637         gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1638         printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1639 #endif          
1640
1641 }
1642
1643 static double
1644 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1645 {
1646         /* XXX for now, point is never inside the wave 
1647         GnomeCanvasWaveView *waveview;
1648         double x1, y1, x2, y2;
1649         double dx, dy;
1650         */
1651
1652         return DBL_MAX;
1653
1654 #if 0
1655         waveview = GNOME_CANVAS_WAVEVIEW (item);
1656
1657         *actual_item = item;
1658
1659         /* Find the bounds for the rectangle plus its outline width */
1660
1661         gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1662
1663         /* Is point inside rectangle */
1664         
1665         if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1666                 return 0.0;
1667         }
1668
1669         /* Point is outside rectangle */
1670
1671         if (x < x1)
1672                 dx = x1 - x;
1673         else if (x > x2)
1674                 dx = x - x2;
1675         else
1676                 dx = 0.0;
1677
1678         if (y < y1)
1679                 dy = y1 - y;
1680         else if (y > y2)
1681                 dy = y - y2;
1682         else
1683                 dy = 0.0;
1684
1685         return sqrt (dx * dx + dy * dy);
1686 #endif
1687 }
1688