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