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