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