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