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