gradient-ized waveforms, c/o Mike Start
[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                 int wave_middle = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1218                 int wave_top = (int) rint ((item->y1) * item->canvas->pixels_per_unit);
1219
1220                 if (s1 < waveview->samples_per_unit) {
1221                         /* we haven't got a prev vars to compare with, so outline the whole line here */
1222                         prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1223                         prev_pymin = prev_pymax;
1224                 }
1225                 else {
1226                         s1 -= waveview->samples_per_unit;
1227                 }
1228
1229                 if(end == waveview->bbox_lrx) {
1230                         /* we don't have the NEXT vars for the last sample */
1231                         last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1232                         last_pymin = last_pymax;
1233                 }
1234                 else {
1235                         s2 += waveview->samples_per_unit;
1236                 }
1237
1238                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1239
1240                 /*
1241                  * Compute the variables outside the rendering rect
1242                  */
1243                 if(prev_pymax != prev_pymin) {
1244
1245                         prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1246                         prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1247                         ++cache_index;
1248                 }
1249                 if(last_pymax != last_pymin) {
1250                         /* take the index of one sample right of what we render */
1251                         guint index = cache_index + (end - begin);
1252
1253                         if (index >= waveview->cache->data_size) {
1254
1255                                 /* the data we want is off the end of the cache, which must mean its beyond
1256                                    the end of the region's source; hence the peak values are 0 */
1257                                 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1258                                 last_pymin = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1259
1260                         } else {
1261
1262                                 last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1263                                 last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1264
1265                         }
1266
1267                 }
1268
1269                 /*
1270                  * initialize NEXT* variables for the first run, duplicated in the loop for speed
1271                  */
1272                 max = waveview->cache->data[cache_index].max;
1273                 min = waveview->cache->data[cache_index].min;
1274
1275                 if (max >= 1.0) {
1276                         max = 1.0;
1277                         next_clip_max = 1;
1278                 }
1279
1280                 if (min <= -1.0) {
1281                         min = -1.0;
1282                         next_clip_min = 1;
1283                 }
1284
1285                 max *= half_height;
1286                 min *= half_height;
1287
1288                 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1289                 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1290
1291                 /*
1292                  * And now the loop
1293                  */
1294                 for(x = begin; x < end; ++x) {
1295                         int clip_max = next_clip_max;
1296                         int clip_min = next_clip_min;
1297                         int fill_max, fill_min;
1298
1299                         pymax = next_pymax;
1300                         pymin = next_pymin;
1301
1302                         /* compute next */
1303                         if(x == end - 1) {
1304                                 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1305                                 next_pymax = last_pymax;
1306                                 next_pymin = last_pymin;
1307                         }
1308                         else {
1309                                 ++cache_index;
1310
1311                                 if (cache_index < waveview->cache->data_size) {
1312                                         max = waveview->cache->data[cache_index].max;
1313                                         min = waveview->cache->data[cache_index].min;
1314                                 } else {
1315                                         max = min = 0;
1316                                 }
1317
1318                                 next_clip_max = 0;
1319                                 next_clip_min = 0;
1320
1321                                 if (max >= 1.0) {
1322                                         max = 1.0;
1323                                         next_clip_max = 1;
1324                                 }
1325
1326                                 if (min <= -1.0) {
1327                                         min = -1.0;
1328                                         next_clip_min = 1;
1329                                 }
1330
1331                                 max *= half_height;
1332                                 min *= half_height;
1333
1334                                 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1335                                 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1336                         }
1337
1338                         /* render */
1339                         if (pymax == pymin) {
1340                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1341                         } else {
1342                                 if((prev_pymax < pymax && next_pymax < pymax) ||
1343                                    (prev_pymax == pymax && next_pymax == pymax)) {
1344                                         fill_max = pymax + 1;
1345                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1346                                 }
1347                                 else {
1348                                         fill_max = MAX(prev_pymax, next_pymax);
1349                                         if(pymax == fill_max) {
1350                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1351                                                 ++fill_max;
1352                                         }
1353                                         else {
1354                                                 PAINT_VERTA_GR(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max, wave_middle, wave_top);                                 }
1355                                 }
1356
1357                                 if((prev_pymin > pymin && next_pymin > pymin) ||
1358                                    (prev_pymin == pymin && next_pymin == pymin)) {
1359                                         fill_min = pymin - 1;
1360                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1361                                 }
1362                                 else {
1363                                         fill_min = MIN(prev_pymin, next_pymin);
1364                                         if(pymin == fill_min) {
1365                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1366                                         }
1367                                         else {
1368                                                 PAINT_VERTA_GR(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin, wave_middle, wave_top);
1369                                         }
1370                                 }
1371
1372                                 if(fill_max < fill_min) {
1373                                         PAINT_VERTA_GR(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min, wave_middle, wave_top);
1374                                 }
1375                                 else if(fill_max == fill_min) {
1376                                         PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1377                                 }
1378                         }
1379
1380                         if (clip_max) {
1381                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax + clip_length);
1382                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x + 1, pymax, pymax + (clip_length -1));
1383                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x - 1, pymax, pymax + (clip_length - 1));
1384
1385                         }
1386
1387                         if (clip_min) {
1388                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a , x, pymin - clip_length, pymin);
1389                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x + 1, pymin - (clip_length - 1), pymin);
1390                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x - 1, pymin - (clip_length - 1), pymin);
1391                         }
1392
1393                         prev_pymax = pymax;
1394                         prev_pymin = pymin;
1395                 }
1396
1397         } else if (waveview->filled && rectify) {
1398
1399                 int prev_pymax = -1;
1400                 int last_pymax = -1;
1401                 int next_pymax;
1402                 double max, min;
1403                 int next_clip_max = 0;
1404                 int next_clip_min = 0;
1405
1406                 int wave_middle = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1407                 int wave_top = (int) rint ((item->y1) * item->canvas->pixels_per_unit);
1408
1409                 // for rectified, this stays constant throughout the loop
1410                 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1411
1412                 if(s1 < waveview->samples_per_unit) {
1413                         /* we haven't got a prev vars to compare with, so outline the whole line here */
1414                         prev_pymax = pymin;
1415                 }
1416                 else {
1417                         s1 -= waveview->samples_per_unit;
1418                 }
1419
1420                 if(end == waveview->bbox_lrx) {
1421                         /* we don't have the NEXT vars for the last sample */
1422                         last_pymax = pymin;
1423                 }
1424                 else {
1425                         s2 += waveview->samples_per_unit;
1426                 }
1427
1428                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1429
1430                 /*
1431                  * Compute the variables outside the rendering rect
1432                  */
1433                 if(prev_pymax < 0) {
1434                         max = MIN(waveview->cache->data[cache_index].max, 1.0);
1435                         min = MAX(waveview->cache->data[cache_index].min, -1.0);
1436
1437                         if (fabs (min) > fabs (max)) {
1438                                 max = fabs (min);
1439                         }
1440
1441                         prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1442                         ++cache_index;
1443                 }
1444                 if(last_pymax < 0) {
1445                         /* take the index of one sample right of what we render */
1446                         int index = cache_index + (end - begin);
1447
1448                         max = MIN(waveview->cache->data[index].max, 1.0);
1449                         min = MAX(waveview->cache->data[index].min, -1.0);
1450
1451                         if (fabs (min) > fabs (max)) {
1452                                 max = fabs (min);
1453                         }
1454
1455                         last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1456                 }
1457
1458                 /*
1459                  * initialize NEXT* variables for the first run, duplicated in the loop for speed
1460                  */
1461                 max = waveview->cache->data[cache_index].max;
1462                 min = waveview->cache->data[cache_index].min;
1463
1464                 if (max >= 1.0) {
1465                         max = 1.0;
1466                         next_clip_max = 1;
1467                 }
1468
1469                 if (min <= -1.0) {
1470                         min = -1.0;
1471                         next_clip_min = 1;
1472                 }
1473
1474                 if (fabs (min) > fabs (max)) {
1475                         max = fabs (min);
1476                 }
1477
1478                 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1479
1480                 /*
1481                  * And now the loop
1482                  */
1483                 for(x = begin; x < end; ++x) {
1484                         int clip_max = next_clip_max;
1485                         int clip_min = next_clip_min;
1486                         int fill_max;
1487
1488                         pymax = next_pymax;
1489
1490                         /* compute next */
1491                         if(x == end - 1) {
1492                                 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1493                                 next_pymax = last_pymax;
1494                         }
1495                         else {
1496                                 ++cache_index;
1497
1498                                 max = waveview->cache->data[cache_index].max;
1499                                 min = waveview->cache->data[cache_index].min;
1500
1501                                 if (max >= 1.0) {
1502                                         max = 1.0;
1503                                         next_clip_max = 1;
1504                                 }
1505
1506                                 if (min <= -1.0) {
1507                                         min = -1.0;
1508                                         next_clip_min = 1;
1509                                 }
1510
1511                                 if (fabs (min) > fabs (max)) {
1512                                         max = fabs (min);
1513                                 }
1514
1515                                 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1516                         }
1517
1518                         /* render */
1519                         if (pymax == pymin) {
1520                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1521                         } else {
1522                                 if((prev_pymax < pymax && next_pymax < pymax) ||
1523                                    (prev_pymax == pymax && next_pymax == pymax)) {
1524                                         fill_max = pymax + 1;
1525                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1526                                 }
1527                                 else {
1528                                         fill_max = MAX(prev_pymax, next_pymax);
1529                                         if(pymax == fill_max) {
1530                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1531                                                 ++fill_max;
1532                                         }
1533                                         else {
1534                                                 PAINT_VERTA_GR(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max, wave_middle, wave_top);
1535                                         }
1536                                 }
1537
1538                                 if(fill_max < pymin) {
1539                                         PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
1540                                 }
1541                                 else if(fill_max == pymin) {
1542                                         PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
1543                                 }
1544                         }
1545
1546                         if (clip_max) {
1547                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax + clip_length);
1548                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x + 1, pymax, pymax + (clip_length -1));
1549                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x - 1, pymax, pymax + (clip_length - 1));
1550                         }
1551
1552                         if (clip_min) { 
1553                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a , x, pymin - clip_length, pymin);
1554                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x + 1, pymin - (clip_length - 1), pymin);
1555                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a >> 1, x - 1, pymin - (clip_length - 1), pymin);
1556                         }
1557
1558                         prev_pymax = pymax;
1559                 }
1560         }
1561         else {
1562                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1563
1564                 for (x = begin; x < end; x++) {
1565
1566                         double max, min;
1567                         int clip_max, clip_min;
1568
1569                         clip_max = 0;
1570                         clip_min = 0;
1571
1572                         max = waveview->cache->data[cache_index].max;
1573                         min = waveview->cache->data[cache_index].min;
1574
1575                         if (max >= 1.0) {
1576                                 max = 1.0;
1577                                 clip_max = 1;
1578                         }
1579
1580                         if (min <= -1.0) {
1581                                 min = -1.0;
1582                                 clip_min = 1;
1583                         }
1584
1585                         if (rectify) {
1586
1587                                 if (fabs (min) > fabs (max)) {
1588                                         max = fabs (min);
1589                                 }
1590
1591                                 max = max * waveview->height;
1592
1593                                 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1594                                 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1595
1596                         } else {
1597
1598                                 max = max * half_height;
1599                                 min = min * half_height;
1600
1601                                 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1602                                 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1603                         }
1604
1605                         /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1606                            or, if samples_per_unit == 1, then a dot at each location.
1607                         */
1608
1609                         if (pymax == pymin) {
1610                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1611                         } else {
1612                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1613                         }
1614
1615                         /* show clipped waveforms with small red lines */
1616
1617                         if (clip_max) {
1618                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1619                         }
1620
1621                         if (clip_min) {
1622                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1623                         }
1624
1625                         /* presto, we're done */
1626
1627                         cache_index++;
1628                 }
1629         }
1630
1631         if (!waveview->rectified && waveview->zero_line && waveview->height >= 100) {
1632                 // Paint zeroline.
1633
1634                 unsigned char zero_r, zero_g, zero_b, zero_a;
1635                 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a);
1636                 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1637                 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, zend, zeroline_y);
1638         }
1639 #undef origin
1640
1641 }
1642
1643 static void
1644 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1645                             GdkDrawable *drawable,
1646                             int x, int y,
1647                             int width, int height)
1648 {
1649         GnomeCanvasWaveView *waveview;
1650         cairo_t* cr;
1651         gulong s1, s2;
1652         int cache_index;
1653         gboolean rectify;
1654         double origin;
1655         double xoff;
1656         double yoff = 0.0;
1657         double ulx;
1658         double uly;
1659         double lrx;
1660         double lry;
1661
1662         waveview = GNOME_CANVAS_WAVEVIEW (item);
1663
1664         /* compute intersection of Drawable area and waveview,
1665            in canvas coordinate space
1666         */
1667
1668         if (x > waveview->bbox_ulx) {
1669                 ulx = x;
1670         } else {
1671                 ulx = waveview->bbox_ulx;
1672         }
1673
1674         if (y > waveview->bbox_uly) {
1675                 uly = y;
1676         } else {
1677                 uly = waveview->bbox_uly;
1678         }
1679
1680         if (x + width > waveview->bbox_lrx) {
1681                 lrx = waveview->bbox_lrx;
1682         } else {
1683                 lrx = x + width;
1684         }
1685
1686         if (y + height > waveview->bbox_lry) {
1687                 lry = waveview->bbox_lry;
1688         } else {
1689                 lry = y + height;
1690         }
1691
1692         /* figure out which samples we need for the resulting intersection */
1693
1694         s1 = floor ((ulx - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1695
1696         if (lrx == waveview->bbox_lrx) {
1697                 /* This avoids minor rounding errors when we have the
1698                    entire region visible.
1699                 */
1700                 s2 = waveview->samples;
1701         } else {
1702                 s2 = s1 + floor ((lrx - ulx) * waveview->samples_per_unit);
1703         }
1704
1705         /* translate back to buffer coordinate space */
1706
1707         ulx -= x;
1708         uly -= y;
1709         lrx -= x;
1710         lry -= y;
1711
1712         /* don't rectify at single-sample zoom */
1713         if(waveview->rectified && waveview->samples_per_unit > 1.0) {
1714                 rectify = TRUE;
1715         } else {
1716                 rectify = FALSE;
1717         }
1718
1719         cr = gdk_cairo_create (drawable);
1720         cairo_set_line_width (cr, 0.5);
1721
1722         origin = waveview->bbox_uly - y + waveview->half_height;
1723
1724         cairo_rectangle (cr, ulx, uly, lrx - ulx, lry - uly);
1725         cairo_clip (cr);
1726
1727         if (waveview->cache_updater && waveview->reload_cache_in_render) {
1728                 waveview->cache->start = 0;
1729                 waveview->cache->end = 0;
1730                 waveview->reload_cache_in_render = FALSE;
1731         }
1732
1733         cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1734
1735 #if 0
1736         printf ("%p r (%d,%d)(%d,%d)[%d x %d] bbox (%d,%d)(%d,%d)[%d x %d]"
1737                 " draw (%.1f,%.1f)(%.1f,%.1f)[%.1f x %.1f] s= %lu..%lu\n",
1738                 waveview,
1739                 x, y,
1740                 x + width,
1741                 y + height,
1742                 width,
1743                 height,
1744                 waveview->bbox_ulx,
1745                 waveview->bbox_uly,
1746                 waveview->bbox_lrx,
1747                 waveview->bbox_lry,
1748                 waveview->bbox_lrx - waveview->bbox_ulx,
1749                 waveview->bbox_lry - waveview->bbox_uly,
1750                 ulx, uly,
1751                 lrx, lry,
1752                 lrx - ulx,
1753                 lry - uly,
1754                 s1, s2);
1755 #endif
1756
1757         /* draw the top half */
1758
1759         for (xoff = ulx; xoff < lrx; xoff++) {
1760                 double max, min;
1761
1762                 max = waveview->cache->data[cache_index].max;
1763                 min = waveview->cache->data[cache_index].min;
1764
1765                 if (min <= -1.0) {
1766                         min = -1.0;
1767                 }
1768
1769                 if (max >= 1.0) {
1770                         max = 1.0;
1771                 }
1772
1773                 if (rectify) {
1774                         if (fabs (min) > fabs (max)) {
1775                                 max = fabs (min);
1776                         }
1777                 }
1778
1779                 yoff = origin - (waveview->half_height * max) + 0.5;
1780
1781                 if (xoff == ulx) {
1782                         /* first point */
1783                         cairo_move_to (cr, xoff+0.5, yoff);
1784                 } else {
1785                         cairo_line_to (cr, xoff+0.5, yoff);
1786                 }
1787
1788                 cache_index++;
1789         }
1790
1791         /* from the final top point, move out of the clip zone */
1792
1793         cairo_line_to (cr, xoff + 10, yoff);
1794
1795         /* now draw the bottom half */
1796
1797         for (--xoff, --cache_index; xoff >= ulx; --xoff) {
1798                 double min;
1799
1800                 min = waveview->cache->data[cache_index].min;
1801
1802                 if (min <= -1.0) {
1803                         min = -1.0;
1804                 }
1805
1806                 yoff = origin - (waveview->half_height * min) + 0.5;
1807
1808                 cairo_line_to (cr, xoff+0.5, yoff);
1809                 cache_index--;
1810         }
1811
1812         /* from the final lower point, move out of the clip zone */
1813
1814         cairo_line_to (cr, xoff - 10, yoff);
1815
1816         /* close path to fill */
1817
1818         cairo_close_path (cr);
1819
1820         /* fill and stroke */
1821
1822         cairo_set_source_rgba (cr,
1823                                (waveview->fill_r/255.0),
1824                                (waveview->fill_g/255.0),
1825                                (waveview->fill_b/255.0),
1826                                (waveview->fill_a/255.0));
1827         cairo_fill_preserve (cr);
1828         cairo_set_source_rgba (cr,
1829                                (waveview->wave_r/255.0),
1830                                (waveview->wave_g/255.0),
1831                                (waveview->wave_b/255.0),
1832                                (waveview->wave_a/255.0));
1833         cairo_stroke (cr);
1834
1835         cairo_destroy (cr);
1836 }
1837
1838 #if 0
1839                 if (clip_max || clip_min) {
1840                         cairo_set_source_rgba (cr, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a);
1841                 }
1842
1843                 if (clip_max) {
1844                         cairo_move_to (cr, xoff, yoff1);
1845                         cairo_line_to (cr, xoff, yoff1 + clip_length);
1846                         cairo_stroke (cr);
1847                 }
1848
1849                 if (clip_min) {
1850                         cairo_move_to (cr, xoff, yoff2);
1851                         cairo_line_to (cr, xoff, yoff2 - clip_length);
1852                         cairo_stroke (cr);
1853                 }
1854
1855 #endif
1856
1857 static void
1858 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1859 {
1860         GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1861
1862         *x1 = waveview->x;
1863         *y1 = waveview->y;
1864
1865         *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1866         *y2 = *y1 + waveview->height;
1867
1868 #if 0
1869         x = 0; y = 0;
1870         gnome_canvas_item_i2w (item, &x, &y);
1871         gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1872         x = *x2;
1873         y = *y2;
1874         gnome_canvas_item_i2w (item, &x, &y);
1875         gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1876         printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1877 #endif
1878
1879 }
1880
1881 static double
1882 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1883 {
1884         (void) item;
1885         (void) x;
1886         (void) y;
1887         (void) cx;
1888         (void) cy;
1889         (void) actual_item;
1890
1891         /* XXX for now, point is never inside the wave
1892         GnomeCanvasWaveView *waveview;
1893         double x1, y1, x2, y2;
1894         double dx, dy;
1895         */
1896
1897         return DBL_MAX;
1898
1899 #if 0
1900         waveview = GNOME_CANVAS_WAVEVIEW (item);
1901
1902         *actual_item = item;
1903
1904         /* Find the bounds for the rectangle plus its outline width */
1905
1906         gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1907
1908         /* Is point inside rectangle */
1909
1910         if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1911                 return 0.0;
1912         }
1913
1914         /* Point is outside rectangle */
1915
1916         if (x < x1)
1917                 dx = x1 - x;
1918         else if (x > x2)
1919                 dx = x - x2;
1920         else
1921                 dx = 0.0;
1922
1923         if (y < y1)
1924                 dy = y1 - y;
1925         else if (y > y2)
1926                 dy = y - y2;
1927         else
1928                 dy = 0.0;
1929
1930         return sqrt (dx * dx + dy * dy);
1931 #endif
1932 }
1933