Fix some warnings.
[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 guint32 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 /** @return cache index of start_sample within the cache */
396 static guint32
397 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_sample, gulong end_sample)
398 {
399         gulong required_cache_entries;
400         gulong rf1, rf2,rf3, required_frames;
401         gulong new_cache_start, new_cache_end;
402         gulong half_width;
403         gulong npeaks;
404         gulong offset;
405         gulong ostart;
406         gulong copied;
407         GnomeCanvasWaveViewCache *cache;
408         float* gain;
409 #ifdef CACHE_MEMMOVE_OPTIMIZATION
410         gulong present_frames;
411         gulong present_entries;
412 #endif
413
414         cache = waveview->cache;
415
416         start_sample = start_sample + waveview->region_start;
417         end_sample = end_sample + waveview->region_start;
418 #if DEBUG_CACHE
419         // printf("waveview->region_start == %lu\n",waveview->region_start);
420         // c_stacktrace ();
421         printf ("\n\n=> 0x%x cache @ 0x%x range: %lu - %lu request: %lu - %lu (%lu frames)\n", 
422                 waveview, cache,
423                 cache->start, cache->end,
424                 start_sample, end_sample, end_sample - start_sample);
425 #endif
426                 
427         if (cache->start <= start_sample && cache->end >= end_sample) {
428 #if DEBUG_CACHE
429                 // printf ("0x%x: cache hit for %lu-%lu (cache holds: %lu-%lu\n",
430                 // waveview, start_sample, end_sample, cache->start, cache->end);
431 #endif
432                 goto out;
433         }
434
435         /* make sure the cache is at least twice as wide as the screen width, and put the start sample
436            in the middle, ensuring that we cover the end_sample. 
437         */
438
439         /* Note the assumption that we have a 1:1 units:pixel ratio for the canvas. Its everywhere ... */
440         
441         half_width = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit)/2.0 + 0.5);
442
443         if (start_sample < half_width) {
444                 new_cache_start = 0;
445         } else {
446                 new_cache_start = start_sample - half_width;
447         }
448
449         /* figure out how many frames we want */
450
451         rf1 = end_sample - start_sample + 1;
452         rf2 = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit * 2.0f));
453         required_frames = MAX(rf1,rf2);
454
455         /* but make sure it doesn't extend beyond the end of the source material */
456
457         rf3 = (gulong) (waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit)) + 1;
458         if (rf3 < new_cache_start) {
459                 rf3 = 0;
460         } else {
461                 rf3 -= new_cache_start;
462         }
463
464 #if DEBUG_CACHE
465         fprintf (stderr, "AVAILABLE FRAMES = %lu of %lu, start = %lu, sstart = %lu, cstart = %lu\n", 
466                  rf3, waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit),
467                  waveview->region_start, start_sample, new_cache_start);
468 #endif
469
470         required_frames = MIN(required_frames,rf3);
471
472         new_cache_end = new_cache_start + required_frames - 1;
473
474         required_cache_entries = (gulong) floor (required_frames / waveview->samples_per_unit );
475
476 #if DEBUG_CACHE
477         fprintf (stderr, "new cache = %lu - %lu\n", new_cache_start, new_cache_end);
478         fprintf(stderr,"required_cach_entries = %lu, samples_per_unit = %f req frames = %lu\n",
479                 required_cache_entries,waveview->samples_per_unit, required_frames);
480 #endif
481
482         if (required_cache_entries > cache->allocated) {
483                 cache->data = g_realloc (cache->data, sizeof (GnomeCanvasWaveViewCacheEntry) * required_cache_entries);
484                 cache->allocated = required_cache_entries;
485                 // cache->start = 0;
486                 // cache->end = 0;
487         }
488
489         ostart = new_cache_start;
490
491 #ifdef CACHE_MEMMOVE_OPTIMIZATION
492         
493         /* data is not entirely in the cache, so go fetch it, making sure to fill the cache */
494
495         /* some of the required cache entries are in the cache, but in the wrong
496            locations. use memmove to fix this.
497         */
498
499         if (cache->start < new_cache_start && new_cache_start < cache->end) {
500                 
501                 /* case one: the common area is at the end of the existing cache. move it 
502                    to the beginning of the cache, and set up to refill whatever remains.
503                    
504                    
505                            wv->cache_start                                        wv->cache_end
506                            |-------------------------------------------------------| cache
507                                                                |--------------------------------| requested
508                                                                <------------------->
509                                                                      "present"
510                                                             new_cache_start                      new_cache_end       
511                 */
512                                 
513
514                 present_frames = cache->end - new_cache_start;
515                 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
516
517 #if DEBUG_CACHE         
518                 fprintf (stderr, "existing material at end of current cache, move to start of new cache\n"
519                          "\tcopy from %lu to start\n", cache->data_size - present_entries);
520 #endif
521
522                 memmove (&cache->data[0],
523                          &cache->data[cache->data_size - present_entries],
524                          present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
525                 
526 #if DEBUG_CACHE
527                 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
528                          present_frames, required_frames, present_entries, new_cache_start + present_entries,
529                          cache->data + present_entries);
530 #endif
531
532                 copied = present_entries;
533                 offset = present_entries;
534                 new_cache_start += present_frames;
535                 required_frames -= present_frames;
536
537         } else if (new_cache_end > cache->start && new_cache_end < cache->end) {
538
539                 /* case two: the common area lives at the beginning of the existing cache. 
540                    
541                                             wv->cache_start                                      wv->cache_end
542                                              |-----------------------------------------------------|
543                               |--------------------------------|
544                                              <----------------->
545                                                 "present"
546
547                              new_cache_start                      new_cache_end
548                 */
549                 
550                 present_frames = new_cache_end - cache->start;
551                 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
552
553                 memmove (&cache->data[cache->data_size - present_entries],
554                          &cache->data[0],
555                          present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
556                 
557 #if DEBUG_CACHE         
558                 fprintf (stderr, "existing material at start of current cache, move to start of end cache\n");
559 #endif
560
561 #if DEBUG_CACHE
562                 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
563                          present_entries, required_frames, present_entries, new_cache_start + present_entries,
564                          cache->data + present_entries);
565 #endif
566
567                 copied = present_entries;
568                 offset = 0;
569                 required_frames -= present_frames;
570
571                 
572         } else {
573                 copied = 0;
574                 offset = 0;
575
576         }
577
578 #else
579         copied = 0;
580         offset = 0;
581
582 #endif /* CACHE_MEMMOVE_OPTIMIZATION */
583
584 //      fprintf(stderr,"length == %lu\n",waveview->length_function (waveview->data_src));
585 //      required_frames = MIN (waveview->length_function (waveview->data_src) - new_cache_start, required_frames);
586
587         npeaks = (gulong) floor (required_frames / waveview->samples_per_unit);
588         required_frames = npeaks * waveview->samples_per_unit;
589
590 #if DEBUG_CACHE
591
592
593         printf ("requesting %lu/%f to cover %lu-%lu at %f spu (request was %lu-%lu) into cache + %lu\n",
594                 required_frames, required_frames/waveview->samples_per_unit, new_cache_start, new_cache_end,
595                 waveview->samples_per_unit, start_sample, end_sample, offset);
596 #endif
597
598 #if DEBUG_CACHE
599 //      printf ("cache holds %lu entries, requesting %lu to cover %lu-%lu (request was %lu-%lu)\n",
600 //              cache->data_size, npeaks, new_cache_start, new_cache_end,
601 //              start_sample, end_sample);
602 #endif
603
604         if (required_frames) {
605                 waveview->peak_function (waveview->data_src, npeaks, new_cache_start, required_frames, cache->data + offset, waveview->channel,waveview->samples_per_unit);
606
607                 /* take into account any copied peaks */
608                 
609                 npeaks += copied;
610         } else {
611                 npeaks = copied;
612         }
613
614         if (npeaks < cache->allocated) {
615 #if DEBUG_CACHE
616                 fprintf (stderr, "zero fill cache for %lu at %lu\n", cache->allocated - npeaks, npeaks);
617 #endif
618                 memset (&cache->data[npeaks], 0, sizeof (GnomeCanvasWaveViewCacheEntry) * (cache->allocated - npeaks));
619                 cache->data_size = npeaks;
620         } else {
621                 cache->data_size = cache->allocated;
622         }
623
624         if (waveview->gain_curve_function) {
625                 guint32 n;
626
627                 gain = (float*) malloc (sizeof (float) * cache->data_size);
628
629                 waveview->gain_curve_function (waveview->gain_src, new_cache_start, new_cache_end, gain, cache->data_size);
630
631                 for (n = 0; n < cache->data_size; ++n) {
632                         cache->data[n].min *= gain[n];
633                         cache->data[n].max *= gain[n];
634                 }
635
636                 free (gain);
637         
638         }
639
640         /* do optional log scaling.  this implementation is not particularly efficient */
641         
642         if (waveview->logscaled) {
643                 guint32 n;
644                 GnomeCanvasWaveViewCacheEntry* buf = cache->data;
645                 
646                 for (n = 0; n < cache->data_size; ++n) {
647
648                         if (buf[n].max > 0.0f) {
649                                 buf[n].max = alt_log_meter(coefficient_to_dB(buf[n].max));
650                         } else if (buf[n].max < 0.0f) {
651                                 buf[n].max = -alt_log_meter(coefficient_to_dB(-buf[n].max));
652                         }
653                         
654                         if (buf[n].min > 0.0f) {
655                                 buf[n].min = alt_log_meter(coefficient_to_dB(buf[n].min));
656                         } else if (buf[n].min < 0.0f) {
657                                 buf[n].min = -alt_log_meter(coefficient_to_dB(-buf[n].min));
658                         }
659                 }
660         }
661
662         cache->start = ostart;
663         cache->end = new_cache_end;
664
665   out:
666 #if DEBUG_CACHE
667         fprintf (stderr, "return cache index = %d\n", 
668                  (guint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5));
669 #endif
670         return (guint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5);
671
672 }
673
674 void
675 gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *waveview, void *data_src)
676 {
677
678         if (waveview->cache_updater) {
679                 if (waveview->data_src == data_src) {
680                         waveview->reload_cache_in_render = TRUE;
681                         return;
682                 }
683         
684                 waveview->cache->start  = 0;
685                 waveview->cache->end = 0;
686         }
687
688         waveview->data_src = data_src;
689 }
690
691 void
692 gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *waveview, guint32 chan)
693 {
694         if (waveview->channel == chan) {
695                 return;
696         }
697         
698         waveview->channel = chan;
699 }
700
701 static void 
702 gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
703
704 {
705         double x1, x2, y1, y2;
706         ArtPoint i1, i2;
707         ArtPoint w1, w2;
708         int Ix1, Ix2, Iy1, Iy2;
709         double i2w[6];
710
711         gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
712
713         i1.x = x1;
714         i1.y = y1;
715         i2.x = x2;
716         i2.y = y2;
717
718         gnome_canvas_item_i2w_affine (item, i2w);
719         art_affine_point (&w1, &i1, i2w);
720         art_affine_point (&w2, &i2, i2w);
721
722         Ix1 = (int) rint(w1.x);
723         Ix2 = (int) rint(w2.x);
724         Iy1 = (int) rint(w1.y);
725         Iy2 = (int) rint(w2.y);
726
727         gnome_canvas_update_bbox (item, Ix1, Iy1, Ix2, Iy2);
728 }
729
730 /* 
731  * CANVAS CALLBACKS 
732  */
733
734 static void
735 gnome_canvas_waveview_set_property (GObject      *object,
736                                     guint         prop_id,
737                                     const GValue *value,
738                                     GParamSpec   *pspec)
739
740 {
741         GnomeCanvasItem *item;
742         GnomeCanvasWaveView *waveview;
743         int redraw = FALSE;
744         int calc_bounds = FALSE;
745
746         g_return_if_fail (object != NULL);
747         g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
748
749         item = GNOME_CANVAS_ITEM (object);
750         waveview = GNOME_CANVAS_WAVEVIEW (object);
751
752         switch (prop_id) {
753         case PROP_DATA_SRC:
754                 gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
755                 redraw = TRUE;
756                 break;
757
758         case PROP_CHANNEL:
759                 gnome_canvas_waveview_set_channel (waveview, g_value_get_uint(value));
760                 redraw = TRUE;
761                 break;
762
763         case PROP_LENGTH_FUNCTION:
764                 waveview->length_function = g_value_get_pointer(value);
765                 redraw = TRUE;
766                 break;
767
768         case PROP_SOURCEFILE_LENGTH_FUNCTION:
769                 waveview->sourcefile_length_function = g_value_get_pointer(value);
770                 redraw = TRUE;
771                 break;
772
773         case PROP_PEAK_FUNCTION:
774                 waveview->peak_function = g_value_get_pointer(value);
775                 redraw = TRUE;
776                 break;
777
778         case PROP_GAIN_FUNCTION:
779                 waveview->gain_curve_function = g_value_get_pointer(value);
780                 redraw = TRUE;
781                 break;
782
783         case PROP_GAIN_SRC:
784                 waveview->gain_src = g_value_get_pointer(value);
785                 if (waveview->cache_updater) {
786                         waveview->cache->start = 0;
787                         waveview->cache->end = 0;
788                 }
789                 redraw = TRUE;
790                 calc_bounds = TRUE;
791                 break;
792
793         case PROP_CACHE:
794                 waveview->cache = g_value_get_pointer(value);
795                 redraw = TRUE;
796                 break;
797
798
799         case PROP_CACHE_UPDATER:
800                 waveview->cache_updater = g_value_get_boolean(value);
801                 redraw = TRUE;
802                 break;
803
804         case PROP_SAMPLES_PER_UNIT:
805                 if ((waveview->samples_per_unit = g_value_get_double(value)) < 1.0) {
806                         waveview->samples_per_unit = 1.0;
807                 }
808                 if (waveview->cache_updater) {
809                         waveview->cache->start = 0;
810                         waveview->cache->end = 0;
811                 }
812                 redraw = TRUE;
813                 calc_bounds = TRUE;
814                 break;
815
816         case PROP_AMPLITUDE_ABOVE_AXIS:
817                 waveview->amplitude_above_axis = g_value_get_double(value);
818                 redraw = TRUE;
819                 break;
820
821         case PROP_X:
822                 if (waveview->x != g_value_get_double (value)) {
823                         waveview->x = g_value_get_double (value);
824                         calc_bounds = TRUE;
825                 }
826                 break;
827
828         case PROP_Y:
829                 if (waveview->y != g_value_get_double (value)) {
830                         waveview->y = g_value_get_double (value);
831                         calc_bounds = TRUE;
832                 }
833                 break;
834
835         case PROP_HEIGHT:
836                 if (waveview->height != fabs (g_value_get_double (value))) {
837                         waveview->height = fabs (g_value_get_double (value));
838                         redraw = TRUE;
839                 }
840                 break;
841
842         case PROP_WAVE_COLOR:
843                 if (waveview->wave_color != g_value_get_uint(value)) {
844                         waveview->wave_color = g_value_get_uint(value);
845                         redraw = TRUE;
846                 }
847                 break;
848
849         case PROP_CLIP_COLOR:
850                 if (waveview->clip_color != g_value_get_uint(value)) {
851                         waveview->clip_color = g_value_get_uint(value);
852                         redraw = TRUE;
853                 }
854                 break;
855
856         case PROP_ZERO_COLOR:
857                 if (waveview->zero_color != g_value_get_uint(value)) {
858                         waveview->zero_color = g_value_get_uint(value);
859                         redraw = TRUE;
860                 }
861                 break;
862
863         case PROP_FILL_COLOR:
864                 if (waveview->fill_color != g_value_get_uint(value)) {
865                         waveview->fill_color = g_value_get_uint(value);
866                         redraw = TRUE;
867                 }
868                 break;
869
870         case PROP_FILLED:
871                 if (waveview->filled != g_value_get_boolean(value)) {
872                         waveview->filled = g_value_get_boolean(value);
873                         redraw = TRUE;
874                 }
875                 break;
876
877         case PROP_RECTIFIED:
878                 if (waveview->rectified != g_value_get_boolean(value)) {
879                         waveview->rectified = g_value_get_boolean(value);
880                         redraw = TRUE;
881                 }
882                 break;
883
884         case PROP_ZERO_LINE:
885                 if (waveview->zero_line != g_value_get_boolean(value)) {
886                         waveview->zero_line = g_value_get_boolean(value);
887                         redraw = TRUE;
888                 }
889                 break;
890
891         case PROP_LOGSCALED:
892                 if (waveview->logscaled != g_value_get_boolean(value)) {
893                         waveview->logscaled = g_value_get_boolean(value);
894                         if (waveview->cache_updater) {
895                                 waveview->cache->start = 0;
896                                 waveview->cache->end = 0;
897                         }
898                         redraw = TRUE;
899                         calc_bounds = TRUE;
900                 }
901                 break;
902         case PROP_REGION_START:
903                 waveview->region_start = g_value_get_uint(value);
904                 redraw = TRUE;
905                 calc_bounds = TRUE;
906                 break;
907
908
909         default:
910                 break;
911         }
912
913         if (calc_bounds) {
914                 gnome_canvas_waveview_reset_bounds (item);
915         }
916
917         if (redraw) {
918                 gnome_canvas_item_request_update (item);
919         }
920
921 }
922
923 static void
924 gnome_canvas_waveview_get_property (
925                 GObject      *object,
926                 guint         prop_id,
927                 GValue       *value,
928                 GParamSpec   *pspec)
929 {
930         
931    
932         g_return_if_fail (object != NULL);
933         g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
934
935         GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
936
937         switch (prop_id) {
938         case PROP_DATA_SRC:
939                 g_value_set_pointer(value, waveview->data_src);
940                 break;
941
942         case PROP_CHANNEL:
943                 g_value_set_uint(value, waveview->channel);
944                 break;
945
946         case PROP_LENGTH_FUNCTION:
947                 g_value_set_pointer(value, waveview->length_function);
948                 break;
949
950         case PROP_SOURCEFILE_LENGTH_FUNCTION:
951                 g_value_set_pointer(value, waveview->sourcefile_length_function);
952                 break;
953
954         case PROP_PEAK_FUNCTION:
955                 g_value_set_pointer(value, waveview->peak_function);
956                 break;
957
958         case PROP_GAIN_FUNCTION:
959                 g_value_set_pointer(value, waveview->gain_curve_function);
960                 break;
961
962         case PROP_GAIN_SRC:
963                 g_value_set_pointer(value, waveview->gain_src);
964                 break;
965
966         case PROP_CACHE:
967                 g_value_set_pointer(value, waveview->cache);
968                 break;
969
970         case PROP_CACHE_UPDATER:
971                 g_value_set_boolean(value, waveview->cache_updater);
972                 break;
973
974         case PROP_SAMPLES_PER_UNIT:
975                 g_value_set_double(value, waveview->samples_per_unit);
976                 break;
977
978         case PROP_AMPLITUDE_ABOVE_AXIS:
979                 g_value_set_double(value, waveview->amplitude_above_axis);
980                 break;
981
982         case PROP_X:
983                 g_value_set_double (value, waveview->x);
984                 break;
985
986         case PROP_Y:
987                 g_value_set_double (value, waveview->y);
988                 break;
989
990         case PROP_HEIGHT:
991                 g_value_set_double (value, waveview->height);
992                 break;
993
994         case PROP_WAVE_COLOR:
995                 g_value_set_uint (value, waveview->wave_color);
996                 break;
997
998         case PROP_CLIP_COLOR:
999                 g_value_set_uint (value, waveview->clip_color);
1000                 break;
1001
1002         case PROP_ZERO_COLOR:
1003                 g_value_set_uint (value, waveview->zero_color);
1004                 break;
1005
1006         case PROP_FILL_COLOR:
1007                 g_value_set_uint (value, waveview->fill_color);
1008                 break;
1009
1010         case PROP_FILLED:
1011                 g_value_set_boolean (value, waveview->filled);
1012                 break;
1013
1014         case PROP_RECTIFIED:
1015                 g_value_set_boolean (value, waveview->rectified);
1016                 break;
1017
1018         case PROP_ZERO_LINE:
1019                 g_value_set_boolean (value, waveview->zero_line);
1020                 break;
1021
1022         case PROP_LOGSCALED:
1023                 g_value_set_boolean (value, waveview->logscaled);
1024                 break;
1025
1026         case PROP_REGION_START:
1027                 g_value_set_uint (value, waveview->region_start);
1028                 break;
1029
1030         default:
1031                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1032                 break;
1033         }
1034 }
1035
1036 static void
1037 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1038 {
1039         GnomeCanvasWaveView *waveview;
1040         double x, y;
1041
1042         waveview = GNOME_CANVAS_WAVEVIEW (item);
1043
1044 //      check_cache (waveview, "start of update");
1045
1046         if (parent_class->update)
1047                 (* parent_class->update) (item, affine, clip_path, flags);
1048
1049         gnome_canvas_waveview_reset_bounds (item);
1050
1051         /* get the canvas coordinates of the view. Do NOT use affines
1052            for this, because they do not round to the integer units used
1053            by the canvas, resulting in subtle pixel-level errors later.
1054         */
1055
1056         x = waveview->x;
1057         y = waveview->y;
1058
1059         gnome_canvas_item_i2w (item, &x, &y);
1060         gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
1061
1062         waveview->samples = waveview->length_function (waveview->data_src);
1063
1064         x = waveview->x + (waveview->samples / waveview->samples_per_unit);
1065         y = waveview->y + waveview->height;
1066
1067         gnome_canvas_item_i2w (item, &x, &y);
1068         gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
1069
1070         /* cache the half-height and the end point in canvas units */
1071
1072         waveview->half_height = waveview->height / 2.0;
1073
1074         /* parse the color */
1075
1076         UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
1077                       &waveview->wave_a);
1078         UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
1079                       &waveview->clip_a);
1080         UINT_TO_RGBA (waveview->fill_color, &waveview->fill_r, &waveview->fill_g, &waveview->fill_b,
1081                       &waveview->fill_a);
1082
1083 //      check_cache (waveview, "end of update");
1084 }                                  
1085
1086 static void
1087 gnome_canvas_waveview_render (GnomeCanvasItem *item,
1088                             GnomeCanvasBuf *buf)
1089 {
1090         GnomeCanvasWaveView *waveview;
1091         gulong s1, s2;
1092         int clip_length = 0;
1093         int pymin, pymax;
1094         guint cache_index;
1095         double half_height;
1096         int x;
1097         char rectify;
1098
1099         waveview = GNOME_CANVAS_WAVEVIEW (item);
1100
1101 //      check_cache (waveview, "start of render");
1102
1103         if (parent_class->render) {
1104                 (*parent_class->render) (item, buf);
1105         }
1106
1107         if (buf->is_bg) {
1108                 gnome_canvas_buf_ensure_buf (buf);
1109                 buf->is_bg = FALSE;
1110         }
1111
1112         /* a "unit" means a pixel */
1113
1114         /* begin: render start x (units) */
1115         int const begin = MAX (waveview->bbox_ulx, buf->rect.x0);
1116
1117         /* zbegin: start x for zero line (units) */
1118         int const zbegin = (begin == waveview->bbox_ulx) ? (begin + 1) : begin;
1119
1120         /* end: render end x (units) */
1121         int const end = (waveview->bbox_lrx >= 0) ? MIN (waveview->bbox_lrx,buf->rect.x1) : buf->rect.x1;
1122
1123         /* zend: end x for zero-line (units) */
1124         int const zend = (end == waveview->bbox_lrx) ? (end - 1) : end;
1125
1126         if (begin == end) {
1127                 return;
1128         }
1129
1130         /* s1: start sample
1131            s2: end sample
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
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                         guint index = cache_index + (end - begin);
1239
1240                         if (index >= waveview->cache->data_size) {
1241                                 
1242                                 /* the data we want is off the end of the cache, which must mean its beyond
1243                                    the end of the region's source; hence the peak values are 0 */
1244                                 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1245                                 last_pymin = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1246                                 
1247                         } else {
1248                                 
1249                                 last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1250                                 last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1251                                 
1252                         }
1253                                 
1254                 }
1255
1256                 /* 
1257                  * initialize NEXT* variables for the first run, duplicated in the loop for speed
1258                  */
1259                 max = waveview->cache->data[cache_index].max;
1260                 min = waveview->cache->data[cache_index].min;
1261                 
1262                 if (max >= 1.0) {
1263                         max = 1.0;
1264                         next_clip_max = 1;
1265                 }
1266                 
1267                 if (min <= -1.0) {
1268                         min = -1.0;
1269                         next_clip_min = 1;
1270                 }
1271                 
1272                 max *= half_height;
1273                 min *= half_height;
1274                 
1275                 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1276                 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1277
1278                 /*
1279                  * And now the loop
1280                  */
1281                 for(x = begin; x < end; ++x) {
1282                         int clip_max = next_clip_max;
1283                         int clip_min = next_clip_min;
1284                         int fill_max, fill_min;
1285
1286                         pymax = next_pymax;
1287                         pymin = next_pymin;
1288
1289                         /* compute next */
1290                         if(x == end - 1) {
1291                                 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1292                                 next_pymax = last_pymax;
1293                                 next_pymin = last_pymin;
1294                         }
1295                         else {
1296                                 ++cache_index;
1297
1298                                 if (cache_index < waveview->cache->data_size) {
1299                                         max = waveview->cache->data[cache_index].max;
1300                                         min = waveview->cache->data[cache_index].min;
1301                                 } else {
1302                                         max = min = 0;
1303                                 }
1304
1305                                 next_clip_max = 0;
1306                                 next_clip_min = 0;
1307
1308                                 if (max >= 1.0) {
1309                                         max = 1.0;
1310                                         next_clip_max = 1;
1311                                 }
1312                                 
1313                                 if (min <= -1.0) {
1314                                         min = -1.0;
1315                                         next_clip_min = 1;
1316                                 }
1317
1318                                 max *= half_height;
1319                                 min *= half_height;
1320                                 
1321                                 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1322                                 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1323                         }
1324                         
1325                         /* render */
1326                         if (pymax == pymin) {
1327                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1328                         } else {
1329                                 if((prev_pymax < pymax && next_pymax < pymax) ||
1330                                    (prev_pymax == pymax && next_pymax == pymax)) {
1331                                         fill_max = pymax + 1;
1332                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1333                                 }
1334                                 else {
1335                                         fill_max = MAX(prev_pymax, next_pymax);
1336                                         if(pymax == fill_max) {
1337                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1338                                                 ++fill_max;
1339                                         }
1340                                         else {
1341                                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1342                                         }
1343                                 }
1344
1345                                 if((prev_pymin > pymin && next_pymin > pymin) ||
1346                                    (prev_pymin == pymin && next_pymin == pymin)) {
1347                                         fill_min = pymin - 1;
1348                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1349                                 }
1350                                 else {
1351                                         fill_min = MIN(prev_pymin, next_pymin);
1352                                         if(pymin == fill_min) {
1353                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1354                                         }
1355                                         else {
1356                                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin);
1357                                         }
1358                                 }
1359
1360                                 if(fill_max < fill_min) {
1361                                         PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min);
1362                                 }
1363                                 else if(fill_max == fill_min) {
1364                                         PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1365                                 }
1366                         }
1367
1368                         if (clip_max) {
1369                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1370                         }
1371                         
1372                         if (clip_min) {
1373                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1374                         }
1375
1376                         prev_pymax = pymax;
1377                         prev_pymin = pymin;
1378                 }
1379
1380         } else if (waveview->filled && rectify) {
1381
1382                 int prev_pymax = -1;
1383                 int last_pymax = -1;
1384                 int next_pymax;
1385                 double max, min;
1386                 int next_clip_max = 0;
1387                 int next_clip_min = 0;
1388
1389                 // for rectified, this stays constant throughout the loop
1390                 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1391
1392                 if(s1 < waveview->samples_per_unit) {
1393                         /* we haven't got a prev vars to compare with, so outline the whole line here */
1394                         prev_pymax = pymin;
1395                 }
1396                 else {
1397                         s1 -= waveview->samples_per_unit;
1398                 }
1399
1400                 if(end == waveview->bbox_lrx) {
1401                         /* we don't have the NEXT vars for the last sample */
1402                         last_pymax = pymin;
1403                 }
1404                 else {
1405                         s2 += waveview->samples_per_unit;
1406                 }
1407
1408                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1409
1410                 /*
1411                  * Compute the variables outside the rendering rect
1412                  */
1413                 if(prev_pymax < 0) {
1414                         max = MIN(waveview->cache->data[cache_index].max, 1.0);
1415                         min = MAX(waveview->cache->data[cache_index].min, -1.0);
1416
1417                         if (fabs (min) > fabs (max)) {
1418                                 max = fabs (min);
1419                         }
1420
1421                         prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1422                         ++cache_index;
1423                 }
1424                 if(last_pymax < 0) {
1425                         /* take the index of one sample right of what we render */
1426                         int index = cache_index + (end - begin);
1427                         
1428                         max = MIN(waveview->cache->data[index].max, 1.0);
1429                         min = MAX(waveview->cache->data[index].min, -1.0);
1430
1431                         if (fabs (min) > fabs (max)) {
1432                                 max = fabs (min);
1433                         }
1434
1435                         last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1436                 }
1437
1438                 /* 
1439                  * initialize NEXT* variables for the first run, duplicated in the loop for speed
1440                  */
1441                 max = waveview->cache->data[cache_index].max;
1442                 min = waveview->cache->data[cache_index].min;
1443                 
1444                 if (max >= 1.0) {
1445                         max = 1.0;
1446                         next_clip_max = 1;
1447                 }
1448                 
1449                 if (min <= -1.0) {
1450                         min = -1.0;
1451                         next_clip_min = 1;
1452                 }
1453                 
1454                 if (fabs (min) > fabs (max)) {
1455                         max = fabs (min);
1456                 } 
1457                 
1458                 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1459
1460                 /*
1461                  * And now the loop
1462                  */
1463                 for(x = begin; x < end; ++x) {
1464                         int clip_max = next_clip_max;
1465                         int clip_min = next_clip_min;
1466                         int fill_max;
1467
1468                         pymax = next_pymax;
1469
1470                         /* compute next */
1471                         if(x == end - 1) {
1472                                 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1473                                 next_pymax = last_pymax;
1474                         }
1475                         else {
1476                                 ++cache_index;
1477
1478                                 max = waveview->cache->data[cache_index].max;
1479                                 min = waveview->cache->data[cache_index].min;
1480                                 
1481                                 if (max >= 1.0) {
1482                                         max = 1.0;
1483                                         next_clip_max = 1;
1484                                 }
1485                                 
1486                                 if (min <= -1.0) {
1487                                         min = -1.0;
1488                                         next_clip_min = 1;
1489                                 }
1490                                 
1491                                 if (fabs (min) > fabs (max)) {
1492                                         max = fabs (min);
1493                                 } 
1494                                 
1495                                 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1496                         }
1497                         
1498                         /* render */
1499                         if (pymax == pymin) {
1500                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1501                         } else {
1502                                 if((prev_pymax < pymax && next_pymax < pymax) ||
1503                                    (prev_pymax == pymax && next_pymax == pymax)) {
1504                                         fill_max = pymax + 1;
1505                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1506                                 }
1507                                 else {
1508                                         fill_max = MAX(prev_pymax, next_pymax);
1509                                         if(pymax == fill_max) {
1510                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1511                                                 ++fill_max;
1512                                         }
1513                                         else {
1514                                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1515                                         }
1516                                 }
1517
1518                                 if(fill_max < pymin) {
1519                                         PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
1520                                 }
1521                                 else if(fill_max == pymin) {
1522                                         PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
1523                                 }
1524                         }
1525
1526                         if (clip_max) {
1527                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1528                         }
1529                         
1530                         if (clip_min) {
1531                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1532                         }
1533
1534                         prev_pymax = pymax;
1535                 }
1536         }
1537         else {
1538                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1539
1540                 for (x = begin; x < end; x++) {
1541                         
1542                         double max, min;
1543                         int clip_max, clip_min;
1544                         
1545                         clip_max = 0;
1546                         clip_min = 0;
1547                         
1548                         max = waveview->cache->data[cache_index].max;
1549                         min = waveview->cache->data[cache_index].min;
1550                         
1551                         if (max >= 1.0) {
1552                                 max = 1.0;
1553                                 clip_max = 1;
1554                         }
1555                         
1556                         if (min <= -1.0) {
1557                                 min = -1.0;
1558                                 clip_min = 1;
1559                         }
1560                         
1561                         if (rectify) {
1562                                 
1563                                 if (fabs (min) > fabs (max)) {
1564                                         max = fabs (min);
1565                                 } 
1566                                 
1567                                 max = max * waveview->height;
1568                                 
1569                                 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1570                                 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1571                                 
1572                         } else {
1573                                 
1574                                 max = max * half_height;
1575                                 min = min * half_height;
1576                                 
1577                                 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1578                                 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1579                         }
1580                         
1581                         /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1582                            or, if samples_per_unit == 1, then a dot at each location.
1583                         */
1584                         
1585                         if (pymax == pymin) {
1586                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1587                         } else {
1588                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1589                         }
1590                         
1591                         /* show clipped waveforms with small red lines */
1592                         
1593                         if (clip_max) {
1594                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1595                         }
1596                         
1597                         if (clip_min) {
1598                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1599                         }
1600
1601                         /* presto, we're done */
1602                 
1603                         cache_index++;
1604                 }
1605         }
1606
1607         if (!waveview->rectified && waveview->zero_line) {
1608                 // Paint zeroline.
1609                 //PAINT_HORIZA(buf, waveview->zero_r, waveview->zero_g, waveview->zero_b, waveview->zero_a, begin, endi-1, origin );
1610                 
1611                 unsigned char zero_r, zero_g, zero_b, zero_a;
1612                 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a );
1613                 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1614                 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, zend, zeroline_y);
1615         }
1616 #undef origin
1617
1618 }
1619
1620 static void
1621 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1622                             GdkDrawable *drawable,
1623                             int x, int y,
1624                             int width, int height)
1625 {
1626         GnomeCanvasWaveView *waveview;
1627         cairo_t* cr;
1628         gulong s1, s2;
1629         int cache_index;
1630         gboolean rectify;
1631         double origin;
1632         double clip_length;
1633         double xoff;
1634         double yoff = 0.0;
1635         double ulx;
1636         double uly;
1637         double lrx;
1638         double lry;
1639
1640         waveview = GNOME_CANVAS_WAVEVIEW (item);
1641
1642         /* compute intersection of Drawable area and waveview,
1643            in canvas coordinate space
1644         */
1645
1646         if (x > waveview->bbox_ulx) {
1647                 ulx = x;
1648         } else {
1649                 ulx = waveview->bbox_ulx;
1650         }
1651
1652         if (y > waveview->bbox_uly) {
1653                 uly = y;
1654         } else {
1655                 uly = waveview->bbox_uly;
1656         }
1657
1658         if (x + width > waveview->bbox_lrx) {
1659                 lrx = waveview->bbox_lrx;
1660         } else {
1661                 lrx = x + width;
1662         }
1663
1664         if (y + height > waveview->bbox_lry) {
1665                 lry = waveview->bbox_lry;
1666         } else {
1667                 lry = y + height;
1668         }
1669
1670         /* figure out which samples we need for the resulting intersection */
1671
1672         s1 = floor ((ulx - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1673
1674         if (lrx == waveview->bbox_lrx) {
1675                 /* This avoids minor rounding errors when we have the
1676                    entire region visible.
1677                 */
1678                 s2 = waveview->samples;
1679         } else {
1680                 s2 = s1 + floor ((lrx - ulx) * waveview->samples_per_unit);
1681         }
1682
1683         /* translate back to buffer coordinate space */
1684
1685         ulx -= x;
1686         uly -= y;
1687         lrx -= x;
1688         lry -= y;
1689
1690         /* don't rectify at single-sample zoom */
1691         if(waveview->rectified && waveview->samples_per_unit > 1.0) {
1692                 rectify = TRUE;
1693         } else {
1694                 rectify = FALSE;
1695         }
1696
1697         clip_length = MIN(5,(waveview->height/4));
1698
1699         cr = gdk_cairo_create (drawable);
1700         cairo_set_line_width (cr, 0.5);
1701
1702         origin = waveview->bbox_uly - y + waveview->half_height;
1703
1704         cairo_rectangle (cr, ulx, uly, lrx - ulx, lry - uly);
1705         cairo_clip (cr);
1706
1707         if (waveview->cache_updater && waveview->reload_cache_in_render) {
1708                 waveview->cache->start = 0;
1709                 waveview->cache->end = 0;
1710                 waveview->reload_cache_in_render = FALSE;
1711         }
1712
1713         cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1714
1715 #if 0
1716         printf ("%p r (%d,%d)(%d,%d)[%d x %d] bbox (%d,%d)(%d,%d)[%d x %d]"
1717                 " draw (%.1f,%.1f)(%.1f,%.1f)[%.1f x %.1f] s= %lu..%lu\n",
1718                 waveview,
1719                 x, y, 
1720                 x + width,
1721                 y + height,
1722                 width,
1723                 height,
1724                 waveview->bbox_ulx,
1725                 waveview->bbox_uly,
1726                 waveview->bbox_lrx,
1727                 waveview->bbox_lry,
1728                 waveview->bbox_lrx - waveview->bbox_ulx,
1729                 waveview->bbox_lry - waveview->bbox_uly,
1730                 ulx, uly,
1731                 lrx, lry,
1732                 lrx - ulx,
1733                 lry - uly,
1734                 s1, s2);
1735 #endif
1736
1737         /* draw the top half */
1738         
1739         for (xoff = ulx; xoff < lrx; xoff++) {
1740                 double max, min;
1741
1742                 max = waveview->cache->data[cache_index].max;
1743                 min = waveview->cache->data[cache_index].min;
1744
1745                 if (min <= -1.0) {
1746                         min = -1.0;
1747                 }
1748                 
1749                 if (max >= 1.0) {
1750                         max = 1.0;
1751                 }
1752                 
1753                 if (rectify) {
1754                         if (fabs (min) > fabs (max)) {
1755                                 max = fabs (min);
1756                         } 
1757                 } 
1758                 
1759                 yoff = origin - (waveview->half_height * max) + 0.5;
1760                 
1761                 if (xoff == ulx) {
1762                         /* first point */
1763                         cairo_move_to (cr, xoff+0.5, yoff);
1764                 } else {
1765                         cairo_line_to (cr, xoff+0.5, yoff);
1766                 }
1767
1768                 cache_index++;
1769         }
1770
1771         /* from the final top point, move out of the clip zone */
1772         
1773         cairo_line_to (cr, xoff + 10, yoff);
1774         
1775         /* now draw the bottom half */
1776
1777         for (--xoff, --cache_index; xoff >= ulx; --xoff) {
1778                 double min;
1779
1780                 min = waveview->cache->data[cache_index].min;
1781
1782                 if (min <= -1.0) {
1783                         min = -1.0;
1784                 }
1785
1786                 yoff = origin - (waveview->half_height * min) + 0.5;
1787
1788                 cairo_line_to (cr, xoff+0.5, yoff);
1789                 cache_index--;
1790         }
1791
1792         /* from the final lower point, move out of the clip zone */
1793
1794         cairo_line_to (cr, xoff - 10, yoff);
1795         
1796         /* close path to fill */
1797
1798         cairo_close_path (cr);
1799
1800         /* fill and stroke */
1801
1802         cairo_set_source_rgba (cr, 
1803                                (waveview->fill_r/255.0), 
1804                                (waveview->fill_g/255.0), 
1805                                (waveview->fill_b/255.0), 
1806                                (waveview->fill_a/255.0));
1807         cairo_fill_preserve (cr);
1808         cairo_set_source_rgba (cr, 
1809                                (waveview->wave_r/255.0), 
1810                                (waveview->wave_g/255.0), 
1811                                (waveview->wave_b/255.0), 
1812                                (waveview->wave_a/255.0));
1813         cairo_stroke (cr);
1814
1815         cairo_destroy (cr);
1816 }
1817
1818 #if 0
1819                 if (clip_max || clip_min) {
1820                         cairo_set_source_rgba (cr, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a);
1821                 }
1822
1823                 if (clip_max) {
1824                         cairo_move_to (cr, xoff, yoff1);
1825                         cairo_line_to (cr, xoff, yoff1 + clip_length);
1826                         cairo_stroke (cr);
1827                 }
1828                 
1829                 if (clip_min) {
1830                         cairo_move_to (cr, xoff, yoff2);
1831                         cairo_line_to (cr, xoff, yoff2 - clip_length);
1832                         cairo_stroke (cr);
1833                 }
1834                 
1835 #endif
1836
1837 static void
1838 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1839 {
1840         GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1841
1842         *x1 = waveview->x;
1843         *y1 = waveview->y;
1844
1845         *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1846         *y2 = *y1 + waveview->height;
1847
1848 #if 0
1849         x = 0; y = 0;
1850         gnome_canvas_item_i2w (item, &x, &y);
1851         gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1852         x = *x2;
1853         y = *y2;
1854         gnome_canvas_item_i2w (item, &x, &y);
1855         gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1856         printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1857 #endif          
1858
1859 }
1860
1861 static double
1862 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1863 {
1864         /* XXX for now, point is never inside the wave 
1865         GnomeCanvasWaveView *waveview;
1866         double x1, y1, x2, y2;
1867         double dx, dy;
1868         */
1869
1870         return DBL_MAX;
1871
1872 #if 0
1873         waveview = GNOME_CANVAS_WAVEVIEW (item);
1874
1875         *actual_item = item;
1876
1877         /* Find the bounds for the rectangle plus its outline width */
1878
1879         gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1880
1881         /* Is point inside rectangle */
1882         
1883         if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1884                 return 0.0;
1885         }
1886
1887         /* Point is outside rectangle */
1888
1889         if (x < x1)
1890                 dx = x1 - x;
1891         else if (x > x2)
1892                 dx = x - x2;
1893         else
1894                 dx = 0.0;
1895
1896         if (y < y1)
1897                 dy = y1 - y;
1898         else if (y > y2)
1899                 dy = y - y2;
1900         else
1901                 dy = 0.0;
1902
1903         return sqrt (dx * dx + dy * dy);
1904 #endif
1905 }
1906