5c670440848a5d4fce6afaa89afacc573e02714b
[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 (
923                 GObject      *object,
924                 guint         prop_id,
925                 GValue       *value,
926                 GParamSpec   *pspec)
927 {
928         
929    
930         g_return_if_fail (object != NULL);
931         g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
932
933         GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
934
935         switch (prop_id) {
936         case PROP_DATA_SRC:
937                 g_value_set_pointer(value, waveview->data_src);
938                 break;
939
940         case PROP_CHANNEL:
941                 g_value_set_uint(value, waveview->channel);
942                 break;
943
944         case PROP_LENGTH_FUNCTION:
945                 g_value_set_pointer(value, waveview->length_function);
946                 break;
947
948         case PROP_SOURCEFILE_LENGTH_FUNCTION:
949                 g_value_set_pointer(value, waveview->sourcefile_length_function);
950                 break;
951
952         case PROP_PEAK_FUNCTION:
953                 g_value_set_pointer(value, waveview->peak_function);
954                 break;
955
956         case PROP_GAIN_FUNCTION:
957                 g_value_set_pointer(value, waveview->gain_curve_function);
958                 break;
959
960         case PROP_GAIN_SRC:
961                 g_value_set_pointer(value, waveview->gain_src);
962                 break;
963
964         case PROP_CACHE:
965                 g_value_set_pointer(value, waveview->cache);
966                 break;
967
968         case PROP_CACHE_UPDATER:
969                 g_value_set_boolean(value, waveview->cache_updater);
970                 break;
971
972         case PROP_SAMPLES_PER_UNIT:
973                 g_value_set_double(value, waveview->samples_per_unit);
974                 break;
975
976         case PROP_AMPLITUDE_ABOVE_AXIS:
977                 g_value_set_double(value, waveview->amplitude_above_axis);
978                 break;
979
980         case PROP_X:
981                 g_value_set_double (value, waveview->x);
982                 break;
983
984         case PROP_Y:
985                 g_value_set_double (value, waveview->y);
986                 break;
987
988         case PROP_HEIGHT:
989                 g_value_set_double (value, waveview->height);
990                 break;
991
992         case PROP_WAVE_COLOR:
993                 g_value_set_uint (value, waveview->wave_color);
994                 break;
995
996         case PROP_CLIP_COLOR:
997                 g_value_set_uint (value, waveview->clip_color);
998                 break;
999
1000         case PROP_ZERO_COLOR:
1001                 g_value_set_uint (value, waveview->zero_color);
1002                 break;
1003
1004         case PROP_FILL_COLOR:
1005                 g_value_set_uint (value, waveview->fill_color);
1006                 break;
1007
1008         case PROP_FILLED:
1009                 g_value_set_boolean (value, waveview->filled);
1010                 break;
1011
1012         case PROP_RECTIFIED:
1013                 g_value_set_boolean (value, waveview->rectified);
1014                 break;
1015
1016         case PROP_ZERO_LINE:
1017                 g_value_set_boolean (value, waveview->zero_line);
1018                 break;
1019
1020         case PROP_LOGSCALED:
1021                 g_value_set_boolean (value, waveview->logscaled);
1022                 break;
1023
1024         case PROP_REGION_START:
1025                 g_value_set_uint (value, waveview->region_start);
1026                 break;
1027
1028         default:
1029                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1030                 break;
1031         }
1032 }
1033
1034 static void
1035 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1036 {
1037         GnomeCanvasWaveView *waveview;
1038         double x, y;
1039
1040         waveview = GNOME_CANVAS_WAVEVIEW (item);
1041
1042 //      check_cache (waveview, "start of update");
1043
1044         if (parent_class->update)
1045                 (* parent_class->update) (item, affine, clip_path, flags);
1046
1047         gnome_canvas_waveview_reset_bounds (item);
1048
1049         /* get the canvas coordinates of the view. Do NOT use affines
1050            for this, because they do not round to the integer units used
1051            by the canvas, resulting in subtle pixel-level errors later.
1052         */
1053
1054         x = waveview->x;
1055         y = waveview->y;
1056
1057         gnome_canvas_item_i2w (item, &x, &y);
1058         gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
1059
1060         waveview->samples = waveview->length_function (waveview->data_src);
1061
1062         x = waveview->x + (waveview->samples / waveview->samples_per_unit);
1063         y = waveview->y + waveview->height;
1064
1065         gnome_canvas_item_i2w (item, &x, &y);
1066         gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
1067
1068         /* cache the half-height and the end point in canvas units */
1069
1070         waveview->half_height = waveview->height / 2.0;
1071
1072         /* parse the color */
1073
1074         UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
1075                       &waveview->wave_a);
1076         UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
1077                       &waveview->clip_a);
1078         UINT_TO_RGBA (waveview->fill_color, &waveview->fill_r, &waveview->fill_g, &waveview->fill_b,
1079                       &waveview->fill_a);
1080
1081 //      check_cache (waveview, "end of update");
1082 }                                  
1083
1084 static void
1085 gnome_canvas_waveview_render (GnomeCanvasItem *item,
1086                             GnomeCanvasBuf *buf)
1087 {
1088         GnomeCanvasWaveView *waveview;
1089         gulong s1, s2;
1090         int clip_length = 0;
1091         int pymin, pymax;
1092         int cache_index;
1093         double half_height;
1094         int x, end, begin;
1095         int zbegin, zend;
1096         char rectify;
1097
1098         waveview = GNOME_CANVAS_WAVEVIEW (item);
1099
1100 //      check_cache (waveview, "start of render");
1101
1102         if (parent_class->render) {
1103                 (*parent_class->render) (item, buf);
1104         }
1105
1106         if (buf->is_bg) {
1107                 gnome_canvas_buf_ensure_buf (buf);
1108                 buf->is_bg = FALSE;
1109         }
1110
1111         begin = MAX(waveview->bbox_ulx, buf->rect.x0);
1112
1113         if (begin == waveview->bbox_ulx) {
1114                 zbegin = begin + 1;
1115         } else {
1116                 zbegin = begin;
1117         }
1118
1119         if (waveview->bbox_lrx >= 0) {
1120                 end = MIN(waveview->bbox_lrx,buf->rect.x1);
1121         } else {
1122                 end = buf->rect.x1;
1123         }
1124
1125         if (end == waveview->bbox_lrx) {
1126                 zend = end - 1;
1127         } else {
1128                 zend = end;
1129         }
1130
1131         if (begin == end) {
1132                 return;
1133         }
1134
1135         s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1136
1137         // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
1138
1139         if (end == waveview->bbox_lrx) {
1140                 /* This avoids minor rounding errors when we have the
1141                    entire region visible.
1142                 */
1143                 s2 = waveview->samples;
1144         } else {
1145                 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
1146         }
1147
1148 #if 0
1149         printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
1150                 " b/e %d..%d s= %lu..%lu @ %f\n",
1151                 waveview,
1152                 buf->rect.x0,
1153                 buf->rect.x1,
1154                 buf->rect.y0,
1155                 buf->rect.y1,
1156                 waveview->bbox_ulx,
1157                 waveview->bbox_lrx,
1158                 waveview->bbox_uly,
1159                 waveview->bbox_lry,
1160                 begin, end, s1, s2,
1161                 waveview->samples_per_unit);
1162 #endif
1163
1164         /* now ensure that the cache is full and properly
1165            positioned.
1166         */
1167
1168 //      check_cache (waveview, "pre-ensure");
1169
1170         if (waveview->cache_updater && waveview->reload_cache_in_render) {
1171                 waveview->cache->start = 0;
1172                 waveview->cache->end = 0;
1173                 waveview->reload_cache_in_render = FALSE;
1174         }
1175
1176 //      check_cache (waveview, "post-ensure");
1177
1178         /* don't rectify at single-sample zoom */
1179         if(waveview->rectified && waveview->samples_per_unit > 1) {
1180                 rectify = TRUE;
1181         }
1182         else {
1183                 rectify = FALSE;
1184         }
1185
1186         clip_length = MIN(5,(waveview->height/4));
1187
1188         /* 
1189            Now draw each line, clipping it appropriately. The clipping
1190            is done by the macros PAINT_FOO().
1191         */
1192
1193         half_height = waveview->half_height;
1194
1195 /* this makes it slightly easier to comprehend whats going on */
1196 #define origin half_height
1197
1198         if(waveview->filled && !rectify) {
1199                 int prev_pymin = 1;
1200                 int prev_pymax = 0;
1201                 int last_pymin = 1;
1202                 int last_pymax = 0;
1203                 int next_pymin, next_pymax;
1204                 double max, min;
1205                 int next_clip_max = 0;
1206                 int next_clip_min = 0;
1207
1208                 if(s1 < waveview->samples_per_unit) {
1209                         /* we haven't got a prev vars to compare with, so outline the whole line here */
1210                         prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1211                         prev_pymin = prev_pymax;
1212                 }
1213                 else {
1214                         s1 -= waveview->samples_per_unit;
1215                 }
1216
1217                 if(end == waveview->bbox_lrx) {
1218                         /* we don't have the NEXT vars for the last sample */
1219                         last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1220                         last_pymin = last_pymax;
1221                 }
1222                 else {
1223                         s2 += waveview->samples_per_unit;
1224                 }
1225
1226                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1227
1228                 /*
1229                  * Compute the variables outside the rendering rect
1230                  */
1231                 if(prev_pymax != prev_pymin) {
1232                         prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1233                         prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1234                         ++cache_index;
1235                 }
1236                 if(last_pymax != last_pymin) {
1237                         /* take the index of one sample right of what we render */
1238                         int index = cache_index + (end - begin);
1239                         
1240                         last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1241                         last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1242                 }
1243
1244                 /* 
1245                  * initialize NEXT* variables for the first run, duplicated in the loop for speed
1246                  */
1247                 max = waveview->cache->data[cache_index].max;
1248                 min = waveview->cache->data[cache_index].min;
1249                 
1250                 if (max >= 1.0) {
1251                         max = 1.0;
1252                         next_clip_max = 1;
1253                 }
1254                 
1255                 if (min <= -1.0) {
1256                         min = -1.0;
1257                         next_clip_min = 1;
1258                 }
1259                 
1260                 max *= half_height;
1261                 min *= half_height;
1262                 
1263                 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1264                 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1265
1266                 /*
1267                  * And now the loop
1268                  */
1269                 for(x = begin; x < end; ++x) {
1270                         int clip_max = next_clip_max;
1271                         int clip_min = next_clip_min;
1272                         int fill_max, fill_min;
1273
1274                         pymax = next_pymax;
1275                         pymin = next_pymin;
1276
1277                         /* compute next */
1278                         if(x == end - 1) {
1279                                 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1280                                 next_pymax = last_pymax;
1281                                 next_pymin = last_pymin;
1282                         }
1283                         else {
1284                                 ++cache_index;
1285
1286                                 max = waveview->cache->data[cache_index].max;
1287                                 min = waveview->cache->data[cache_index].min;
1288
1289                                 next_clip_max = 0;
1290                                 next_clip_min = 0;
1291
1292                                 if (max >= 1.0) {
1293                                         max = 1.0;
1294                                         next_clip_max = 1;
1295                                 }
1296                                 
1297                                 if (min <= -1.0) {
1298                                         min = -1.0;
1299                                         next_clip_min = 1;
1300                                 }
1301
1302                                 max *= half_height;
1303                                 min *= half_height;
1304                                 
1305                                 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1306                                 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1307                         }
1308                         
1309                         /* render */
1310                         if (pymax == pymin) {
1311                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1312                         } else {
1313                                 if((prev_pymax < pymax && next_pymax < pymax) ||
1314                                    (prev_pymax == pymax && next_pymax == pymax)) {
1315                                         fill_max = pymax + 1;
1316                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1317                                 }
1318                                 else {
1319                                         fill_max = MAX(prev_pymax, next_pymax);
1320                                         if(pymax == fill_max) {
1321                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1322                                                 ++fill_max;
1323                                         }
1324                                         else {
1325                                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1326                                         }
1327                                 }
1328
1329                                 if((prev_pymin > pymin && next_pymin > pymin) ||
1330                                    (prev_pymin == pymin && next_pymin == pymin)) {
1331                                         fill_min = pymin - 1;
1332                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1333                                 }
1334                                 else {
1335                                         fill_min = MIN(prev_pymin, next_pymin);
1336                                         if(pymin == fill_min) {
1337                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1338                                         }
1339                                         else {
1340                                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin);
1341                                         }
1342                                 }
1343
1344                                 if(fill_max < fill_min) {
1345                                         PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min);
1346                                 }
1347                                 else if(fill_max == fill_min) {
1348                                         PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1349                                 }
1350                         }
1351
1352                         if (clip_max) {
1353                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1354                         }
1355                         
1356                         if (clip_min) {
1357                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1358                         }
1359
1360                         prev_pymax = pymax;
1361                         prev_pymin = pymin;
1362                 }
1363
1364         } else if (waveview->filled && rectify) {
1365
1366                 int prev_pymax = -1;
1367                 int last_pymax = -1;
1368                 int next_pymax;
1369                 double max, min;
1370                 int next_clip_max = 0;
1371                 int next_clip_min = 0;
1372
1373                 // for rectified, this stays constant throughout the loop
1374                 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1375
1376                 if(s1 < waveview->samples_per_unit) {
1377                         /* we haven't got a prev vars to compare with, so outline the whole line here */
1378                         prev_pymax = pymin;
1379                 }
1380                 else {
1381                         s1 -= waveview->samples_per_unit;
1382                 }
1383
1384                 if(end == waveview->bbox_lrx) {
1385                         /* we don't have the NEXT vars for the last sample */
1386                         last_pymax = pymin;
1387                 }
1388                 else {
1389                         s2 += waveview->samples_per_unit;
1390                 }
1391
1392                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1393
1394                 /*
1395                  * Compute the variables outside the rendering rect
1396                  */
1397                 if(prev_pymax < 0) {
1398                         max = MIN(waveview->cache->data[cache_index].max, 1.0);
1399                         min = MAX(waveview->cache->data[cache_index].min, -1.0);
1400
1401                         if (fabs (min) > fabs (max)) {
1402                                 max = fabs (min);
1403                         }
1404
1405                         prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1406                         ++cache_index;
1407                 }
1408                 if(last_pymax < 0) {
1409                         /* take the index of one sample right of what we render */
1410                         int index = cache_index + (end - begin);
1411                         
1412                         max = MIN(waveview->cache->data[index].max, 1.0);
1413                         min = MAX(waveview->cache->data[index].min, -1.0);
1414
1415                         if (fabs (min) > fabs (max)) {
1416                                 max = fabs (min);
1417                         }
1418
1419                         last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1420                 }
1421
1422                 /* 
1423                  * initialize NEXT* variables for the first run, duplicated in the loop for speed
1424                  */
1425                 max = waveview->cache->data[cache_index].max;
1426                 min = waveview->cache->data[cache_index].min;
1427                 
1428                 if (max >= 1.0) {
1429                         max = 1.0;
1430                         next_clip_max = 1;
1431                 }
1432                 
1433                 if (min <= -1.0) {
1434                         min = -1.0;
1435                         next_clip_min = 1;
1436                 }
1437                 
1438                 if (fabs (min) > fabs (max)) {
1439                         max = fabs (min);
1440                 } 
1441                 
1442                 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1443
1444                 /*
1445                  * And now the loop
1446                  */
1447                 for(x = begin; x < end; ++x) {
1448                         int clip_max = next_clip_max;
1449                         int clip_min = next_clip_min;
1450                         int fill_max;
1451
1452                         pymax = next_pymax;
1453
1454                         /* compute next */
1455                         if(x == end - 1) {
1456                                 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1457                                 next_pymax = last_pymax;
1458                         }
1459                         else {
1460                                 ++cache_index;
1461
1462                                 max = waveview->cache->data[cache_index].max;
1463                                 min = waveview->cache->data[cache_index].min;
1464                                 
1465                                 if (max >= 1.0) {
1466                                         max = 1.0;
1467                                         next_clip_max = 1;
1468                                 }
1469                                 
1470                                 if (min <= -1.0) {
1471                                         min = -1.0;
1472                                         next_clip_min = 1;
1473                                 }
1474                                 
1475                                 if (fabs (min) > fabs (max)) {
1476                                         max = fabs (min);
1477                                 } 
1478                                 
1479                                 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1480                         }
1481                         
1482                         /* render */
1483                         if (pymax == pymin) {
1484                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1485                         } else {
1486                                 if((prev_pymax < pymax && next_pymax < pymax) ||
1487                                    (prev_pymax == pymax && next_pymax == pymax)) {
1488                                         fill_max = pymax + 1;
1489                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1490                                 }
1491                                 else {
1492                                         fill_max = MAX(prev_pymax, next_pymax);
1493                                         if(pymax == fill_max) {
1494                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1495                                                 ++fill_max;
1496                                         }
1497                                         else {
1498                                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1499                                         }
1500                                 }
1501
1502                                 if(fill_max < pymin) {
1503                                         PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
1504                                 }
1505                                 else if(fill_max == pymin) {
1506                                         PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
1507                                 }
1508                         }
1509
1510                         if (clip_max) {
1511                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1512                         }
1513                         
1514                         if (clip_min) {
1515                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1516                         }
1517
1518                         prev_pymax = pymax;
1519                 }
1520         }
1521         else {
1522                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1523
1524                 for (x = begin; x < end; x++) {
1525                         
1526                         double max, min;
1527                         int clip_max, clip_min;
1528                         
1529                         clip_max = 0;
1530                         clip_min = 0;
1531                         
1532                         max = waveview->cache->data[cache_index].max;
1533                         min = waveview->cache->data[cache_index].min;
1534                         
1535                         if (max >= 1.0) {
1536                                 max = 1.0;
1537                                 clip_max = 1;
1538                         }
1539                         
1540                         if (min <= -1.0) {
1541                                 min = -1.0;
1542                                 clip_min = 1;
1543                         }
1544                         
1545                         if (rectify) {
1546                                 
1547                                 if (fabs (min) > fabs (max)) {
1548                                         max = fabs (min);
1549                                 } 
1550                                 
1551                                 max = max * waveview->height;
1552                                 
1553                                 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1554                                 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1555                                 
1556                         } else {
1557                                 
1558                                 max = max * half_height;
1559                                 min = min * half_height;
1560                                 
1561                                 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1562                                 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1563                         }
1564                         
1565                         /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1566                            or, if samples_per_unit == 1, then a dot at each location.
1567                         */
1568                         
1569                         if (pymax == pymin) {
1570                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1571                         } else {
1572                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1573                         }
1574                         
1575                         /* show clipped waveforms with small red lines */
1576                         
1577                         if (clip_max) {
1578                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1579                         }
1580                         
1581                         if (clip_min) {
1582                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1583                         }
1584
1585                         /* presto, we're done */
1586                 
1587                         cache_index++;
1588                 }
1589         }
1590
1591         if (!waveview->rectified && waveview->zero_line) {
1592                 // Paint zeroline.
1593                 //PAINT_HORIZA(buf, waveview->zero_r, waveview->zero_g, waveview->zero_b, waveview->zero_a, begin, endi-1, origin );
1594                 
1595                 unsigned char zero_r, zero_g, zero_b, zero_a;
1596                 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a );
1597                 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1598                 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, end, zeroline_y);
1599         }
1600 #undef origin
1601
1602 }
1603
1604 static void
1605 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1606                             GdkDrawable *drawable,
1607                             int x, int y,
1608                             int width, int height)
1609 {
1610         GnomeCanvasWaveView *waveview;
1611         cairo_t* cr;
1612         gulong s1, s2;
1613         int cache_index;
1614         double zbegin, zend;
1615         gboolean rectify;
1616         double origin;
1617         double clip_length;
1618         double xoff;
1619         double yoff = 0.0;
1620         double ulx;
1621         double uly;
1622         double lrx;
1623         double lry;
1624
1625         waveview = GNOME_CANVAS_WAVEVIEW (item);
1626
1627         /* compute intersection of Drawable area and waveview,
1628            in canvas coordinate space
1629         */
1630
1631         if (x > waveview->bbox_ulx) {
1632                 ulx = x;
1633                 zbegin = ulx;
1634         } else {
1635                 ulx = waveview->bbox_ulx;
1636                 zbegin = ulx + 1;
1637         }
1638
1639         if (y > waveview->bbox_uly) {
1640                 uly = y;
1641         } else {
1642                 uly = waveview->bbox_uly;
1643         }
1644
1645         if (x + width > waveview->bbox_lrx) {
1646                 lrx = waveview->bbox_lrx;
1647                 zend = lrx - 1;
1648         } else {
1649                 lrx = x + width;
1650                 zend = lrx;
1651         }
1652
1653         if (y + height > waveview->bbox_lry) {
1654                 lry = waveview->bbox_lry;
1655         } else {
1656                 lry = y + height;
1657         }
1658
1659         /* figure out which samples we need for the resulting intersection */
1660
1661         s1 = floor ((ulx - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1662
1663         if (lrx == waveview->bbox_lrx) {
1664                 /* This avoids minor rounding errors when we have the
1665                    entire region visible.
1666                 */
1667                 s2 = waveview->samples;
1668         } else {
1669                 s2 = s1 + floor ((lrx - ulx) * waveview->samples_per_unit);
1670         }
1671
1672         /* translate back to buffer coordinate space */
1673
1674         ulx -= x;
1675         uly -= y;
1676         lrx -= x;
1677         lry -= y;
1678         zbegin -= x;
1679         zend -= x;
1680
1681         /* don't rectify at single-sample zoom */
1682         if(waveview->rectified && waveview->samples_per_unit > 1.0) {
1683                 rectify = TRUE;
1684         } else {
1685                 rectify = FALSE;
1686         }
1687
1688         clip_length = MIN(5,(waveview->height/4));
1689
1690         cr = gdk_cairo_create (drawable);
1691         cairo_set_line_width (cr, 0.5);
1692
1693         origin = waveview->bbox_uly - y + waveview->half_height;
1694
1695         cairo_rectangle (cr, ulx, uly, lrx - ulx, lry - uly);
1696         cairo_clip (cr);
1697
1698         if (waveview->cache_updater && waveview->reload_cache_in_render) {
1699                 waveview->cache->start = 0;
1700                 waveview->cache->end = 0;
1701                 waveview->reload_cache_in_render = FALSE;
1702         }
1703
1704         cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1705
1706 #if 0
1707         printf ("%p r (%d,%d)(%d,%d)[%d x %d] bbox (%d,%d)(%d,%d)[%d x %d]"
1708                 " draw (%.1f,%.1f)(%.1f,%.1f)[%.1f x %.1f] s= %lu..%lu\n",
1709                 waveview,
1710                 x, y, 
1711                 x + width,
1712                 y + height,
1713                 width,
1714                 height,
1715                 waveview->bbox_ulx,
1716                 waveview->bbox_uly,
1717                 waveview->bbox_lrx,
1718                 waveview->bbox_lry,
1719                 waveview->bbox_lrx - waveview->bbox_ulx,
1720                 waveview->bbox_lry - waveview->bbox_uly,
1721                 ulx, uly,
1722                 lrx, lry,
1723                 lrx - ulx,
1724                 lry - uly,
1725                 s1, s2);
1726 #endif
1727
1728         /* draw the top half */
1729         
1730         for (xoff = ulx; xoff < lrx; xoff++) {
1731                 double max, min;
1732
1733                 max = waveview->cache->data[cache_index].max;
1734                 min = waveview->cache->data[cache_index].min;
1735
1736                 if (min <= -1.0) {
1737                         min = -1.0;
1738                 }
1739                 
1740                 if (max >= 1.0) {
1741                         max = 1.0;
1742                 }
1743                 
1744                 if (rectify) {
1745                         if (fabs (min) > fabs (max)) {
1746                                 max = fabs (min);
1747                         } 
1748                 } 
1749                 
1750                 yoff = origin - (waveview->half_height * max) + 0.5;
1751                 
1752                 if (xoff == ulx) {
1753                         /* first point */
1754                         cairo_move_to (cr, xoff+0.5, yoff);
1755                 } else {
1756                         cairo_line_to (cr, xoff+0.5, yoff);
1757                 }
1758
1759                 cache_index++;
1760         }
1761
1762         /* from the final top point, move out of the clip zone */
1763         
1764         cairo_line_to (cr, xoff + 10, yoff);
1765         
1766         /* now draw the bottom half */
1767
1768         for (--xoff, --cache_index; xoff >= ulx; --xoff) {
1769                 double min;
1770
1771                 min = waveview->cache->data[cache_index].min;
1772
1773                 if (min <= -1.0) {
1774                         min = -1.0;
1775                 }
1776
1777                 yoff = origin - (waveview->half_height * min) + 0.5;
1778
1779                 cairo_line_to (cr, xoff+0.5, yoff);
1780                 cache_index--;
1781         }
1782
1783         /* from the final lower point, move out of the clip zone */
1784
1785         cairo_line_to (cr, xoff - 10, yoff);
1786         
1787         /* close path to fill */
1788
1789         cairo_close_path (cr);
1790
1791         /* fill and stroke */
1792
1793         cairo_set_source_rgba (cr, 
1794                                (waveview->fill_r/255.0), 
1795                                (waveview->fill_g/255.0), 
1796                                (waveview->fill_b/255.0), 
1797                                (waveview->fill_a/255.0));
1798         cairo_fill_preserve (cr);
1799         cairo_set_source_rgba (cr, 
1800                                (waveview->wave_r/255.0), 
1801                                (waveview->wave_g/255.0), 
1802                                (waveview->wave_b/255.0), 
1803                                (waveview->wave_a/255.0));
1804         cairo_stroke (cr);
1805
1806         cairo_destroy (cr);
1807 }
1808
1809 #if 0
1810                 if (clip_max || clip_min) {
1811                         cairo_set_source_rgba (cr, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a);
1812                 }
1813
1814                 if (clip_max) {
1815                         cairo_move_to (cr, xoff, yoff1);
1816                         cairo_line_to (cr, xoff, yoff1 + clip_length);
1817                         cairo_stroke (cr);
1818                 }
1819                 
1820                 if (clip_min) {
1821                         cairo_move_to (cr, xoff, yoff2);
1822                         cairo_line_to (cr, xoff, yoff2 - clip_length);
1823                         cairo_stroke (cr);
1824                 }
1825                 
1826 #endif
1827
1828 static void
1829 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1830 {
1831         GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1832
1833         *x1 = waveview->x;
1834         *y1 = waveview->y;
1835
1836         *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1837         *y2 = *y1 + waveview->height;
1838
1839 #if 0
1840         x = 0; y = 0;
1841         gnome_canvas_item_i2w (item, &x, &y);
1842         gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1843         x = *x2;
1844         y = *y2;
1845         gnome_canvas_item_i2w (item, &x, &y);
1846         gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1847         printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1848 #endif          
1849
1850 }
1851
1852 static double
1853 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1854 {
1855         /* XXX for now, point is never inside the wave 
1856         GnomeCanvasWaveView *waveview;
1857         double x1, y1, x2, y2;
1858         double dx, dy;
1859         */
1860
1861         return DBL_MAX;
1862
1863 #if 0
1864         waveview = GNOME_CANVAS_WAVEVIEW (item);
1865
1866         *actual_item = item;
1867
1868         /* Find the bounds for the rectangle plus its outline width */
1869
1870         gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1871
1872         /* Is point inside rectangle */
1873         
1874         if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1875                 return 0.0;
1876         }
1877
1878         /* Point is outside rectangle */
1879
1880         if (x < x1)
1881                 dx = x1 - x;
1882         else if (x > x2)
1883                 dx = x - x2;
1884         else
1885                 dx = 0.0;
1886
1887         if (y < y1)
1888                 dy = y1 - y;
1889         else if (y > y2)
1890                 dy = y - y2;
1891         else
1892                 dy = 0.0;
1893
1894         return sqrt (dx * dx + dy * dy);
1895 #endif
1896 }
1897