a boatload of minor and middle-sized changes to try to speed up undo. imperfect,...
[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 = g_value_get_pointer(value);
767                 redraw = TRUE;
768                 break;
769
770         case PROP_SOURCEFILE_LENGTH_FUNCTION:
771                 waveview->sourcefile_length_function = g_value_get_pointer(value);
772                 redraw = TRUE;
773                 break;
774
775         case PROP_PEAK_FUNCTION:
776                 waveview->peak_function = g_value_get_pointer(value);
777                 redraw = TRUE;
778                 break;
779
780         case PROP_GAIN_FUNCTION:
781                 waveview->gain_curve_function = g_value_get_pointer(value);
782                 redraw = TRUE;
783                 break;
784
785         case PROP_GAIN_SRC:
786                 waveview->gain_src = g_value_get_pointer(value);
787                 if (waveview->cache_updater) {
788                         waveview->cache->start = 0;
789                         waveview->cache->end = 0;
790                 }
791                 redraw = TRUE;
792                 calc_bounds = TRUE;
793                 break;
794
795         case PROP_CACHE:
796                 waveview->cache = g_value_get_pointer(value);
797                 redraw = TRUE;
798                 break;
799
800
801         case PROP_CACHE_UPDATER:
802                 waveview->cache_updater = g_value_get_boolean(value);
803                 redraw = TRUE;
804                 break;
805
806         case PROP_SAMPLES_PER_UNIT:
807                 if ((waveview->samples_per_unit = g_value_get_double(value)) < 1.0) {
808                         waveview->samples_per_unit = 1.0;
809                 }
810                 if (waveview->cache_updater) {
811                         waveview->cache->start = 0;
812                         waveview->cache->end = 0;
813                 }
814                 redraw = TRUE;
815                 calc_bounds = TRUE;
816                 break;
817
818         case PROP_AMPLITUDE_ABOVE_AXIS:
819                 waveview->amplitude_above_axis = g_value_get_double(value);
820                 redraw = TRUE;
821                 break;
822
823         case PROP_X:
824                 if (waveview->x != g_value_get_double (value)) {
825                         waveview->x = g_value_get_double (value);
826                         calc_bounds = TRUE;
827                 }
828                 break;
829
830         case PROP_Y:
831                 if (waveview->y != g_value_get_double (value)) {
832                         waveview->y = g_value_get_double (value);
833                         calc_bounds = TRUE;
834                 }
835                 break;
836
837         case PROP_HEIGHT:
838                 if (waveview->height != fabs (g_value_get_double (value))) {
839                         waveview->height = fabs (g_value_get_double (value));
840                         redraw = TRUE;
841                 }
842                 break;
843
844         case PROP_WAVE_COLOR:
845                 if (waveview->wave_color != g_value_get_uint(value)) {
846                         waveview->wave_color = g_value_get_uint(value);
847                         redraw = TRUE;
848                 }
849                 break;
850
851         case PROP_CLIP_COLOR:
852                 if (waveview->clip_color != g_value_get_uint(value)) {
853                         waveview->clip_color = g_value_get_uint(value);
854                         redraw = TRUE;
855                 }
856                 break;
857
858         case PROP_ZERO_COLOR:
859                 if (waveview->zero_color != g_value_get_uint(value)) {
860                         waveview->zero_color = g_value_get_uint(value);
861                         redraw = TRUE;
862                 }
863                 break;
864
865         case PROP_FILL_COLOR:
866                 if (waveview->fill_color != g_value_get_uint(value)) {
867                         waveview->fill_color = g_value_get_uint(value);
868                         redraw = TRUE;
869                 }
870                 break;
871
872         case PROP_FILLED:
873                 if (waveview->filled != g_value_get_boolean(value)) {
874                         waveview->filled = g_value_get_boolean(value);
875                         redraw = TRUE;
876                 }
877                 break;
878
879         case PROP_RECTIFIED:
880                 if (waveview->rectified != g_value_get_boolean(value)) {
881                         waveview->rectified = g_value_get_boolean(value);
882                         redraw = TRUE;
883                 }
884                 break;
885
886         case PROP_ZERO_LINE:
887                 if (waveview->zero_line != g_value_get_boolean(value)) {
888                         waveview->zero_line = g_value_get_boolean(value);
889                         redraw = TRUE;
890                 }
891                 break;
892
893         case PROP_LOGSCALED:
894                 if (waveview->logscaled != g_value_get_boolean(value)) {
895                         waveview->logscaled = g_value_get_boolean(value);
896                         if (waveview->cache_updater) {
897                                 waveview->cache->start = 0;
898                                 waveview->cache->end = 0;
899                         }
900                         redraw = TRUE;
901                         calc_bounds = TRUE;
902                 }
903                 break;
904         case PROP_REGION_START:
905                 waveview->region_start = g_value_get_uint(value);
906                 redraw = TRUE;
907                 calc_bounds = TRUE;
908                 break;
909
910
911         default:
912                 break;
913         }
914
915         if (calc_bounds) {
916                 gnome_canvas_waveview_reset_bounds (item);
917         }
918
919         if (redraw) {
920                 gnome_canvas_item_request_update (item);
921         }
922
923 }
924
925 static void
926 gnome_canvas_waveview_get_property (
927                 GObject      *object,
928                 guint         prop_id,
929                 GValue       *value,
930                 GParamSpec   *pspec)
931 {
932
933
934         g_return_if_fail (object != NULL);
935         g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
936
937         GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
938
939         switch (prop_id) {
940         case PROP_DATA_SRC:
941                 g_value_set_pointer(value, waveview->data_src);
942                 break;
943
944         case PROP_CHANNEL:
945                 g_value_set_uint(value, waveview->channel);
946                 break;
947
948         case PROP_LENGTH_FUNCTION:
949                 g_value_set_pointer(value, waveview->length_function);
950                 break;
951
952         case PROP_SOURCEFILE_LENGTH_FUNCTION:
953                 g_value_set_pointer(value, waveview->sourcefile_length_function);
954                 break;
955
956         case PROP_PEAK_FUNCTION:
957                 g_value_set_pointer(value, waveview->peak_function);
958                 break;
959
960         case PROP_GAIN_FUNCTION:
961                 g_value_set_pointer(value, waveview->gain_curve_function);
962                 break;
963
964         case PROP_GAIN_SRC:
965                 g_value_set_pointer(value, waveview->gain_src);
966                 break;
967
968         case PROP_CACHE:
969                 g_value_set_pointer(value, waveview->cache);
970                 break;
971
972         case PROP_CACHE_UPDATER:
973                 g_value_set_boolean(value, waveview->cache_updater);
974                 break;
975
976         case PROP_SAMPLES_PER_UNIT:
977                 g_value_set_double(value, waveview->samples_per_unit);
978                 break;
979
980         case PROP_AMPLITUDE_ABOVE_AXIS:
981                 g_value_set_double(value, waveview->amplitude_above_axis);
982                 break;
983
984         case PROP_X:
985                 g_value_set_double (value, waveview->x);
986                 break;
987
988         case PROP_Y:
989                 g_value_set_double (value, waveview->y);
990                 break;
991
992         case PROP_HEIGHT:
993                 g_value_set_double (value, waveview->height);
994                 break;
995
996         case PROP_WAVE_COLOR:
997                 g_value_set_uint (value, waveview->wave_color);
998                 break;
999
1000         case PROP_CLIP_COLOR:
1001                 g_value_set_uint (value, waveview->clip_color);
1002                 break;
1003
1004         case PROP_ZERO_COLOR:
1005                 g_value_set_uint (value, waveview->zero_color);
1006                 break;
1007
1008         case PROP_FILL_COLOR:
1009                 g_value_set_uint (value, waveview->fill_color);
1010                 break;
1011
1012         case PROP_FILLED:
1013                 g_value_set_boolean (value, waveview->filled);
1014                 break;
1015
1016         case PROP_RECTIFIED:
1017                 g_value_set_boolean (value, waveview->rectified);
1018                 break;
1019
1020         case PROP_ZERO_LINE:
1021                 g_value_set_boolean (value, waveview->zero_line);
1022                 break;
1023
1024         case PROP_LOGSCALED:
1025                 g_value_set_boolean (value, waveview->logscaled);
1026                 break;
1027
1028         case PROP_REGION_START:
1029                 g_value_set_uint (value, waveview->region_start);
1030                 break;
1031
1032         default:
1033                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1034                 break;
1035         }
1036 }
1037
1038 static void
1039 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1040 {
1041         GnomeCanvasWaveView *waveview;
1042         double x, y;
1043
1044         waveview = GNOME_CANVAS_WAVEVIEW (item);
1045
1046 //      check_cache (waveview, "start of update");
1047
1048         if (parent_class->update)
1049                 (* parent_class->update) (item, affine, clip_path, flags);
1050
1051         gnome_canvas_waveview_reset_bounds (item);
1052
1053         /* get the canvas coordinates of the view. Do NOT use affines
1054            for this, because they do not round to the integer units used
1055            by the canvas, resulting in subtle pixel-level errors later.
1056         */
1057
1058         x = waveview->x;
1059         y = waveview->y;
1060
1061         gnome_canvas_item_i2w (item, &x, &y);
1062         gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
1063
1064         waveview->samples = waveview->length_function (waveview->data_src);
1065
1066         x = waveview->x + (waveview->samples / waveview->samples_per_unit);
1067         y = waveview->y + waveview->height;
1068
1069         gnome_canvas_item_i2w (item, &x, &y);
1070         gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
1071
1072         /* cache the half-height and the end point in canvas units */
1073
1074         waveview->half_height = waveview->height / 2.0;
1075
1076         /* parse the color */
1077
1078         UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
1079                       &waveview->wave_a);
1080         UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
1081                       &waveview->clip_a);
1082         UINT_TO_RGBA (waveview->fill_color, &waveview->fill_r, &waveview->fill_g, &waveview->fill_b,
1083                       &waveview->fill_a);
1084
1085 //      check_cache (waveview, "end of update");
1086 }
1087
1088 static void
1089 gnome_canvas_waveview_render (GnomeCanvasItem *item,
1090                             GnomeCanvasBuf *buf)
1091 {
1092         GnomeCanvasWaveView *waveview;
1093         gulong s1, s2;
1094         int clip_length = 0;
1095         int pymin, pymax;
1096         guint cache_index;
1097         double half_height;
1098         int x;
1099         char rectify;
1100
1101         waveview = GNOME_CANVAS_WAVEVIEW (item);
1102
1103 //      check_cache (waveview, "start of render");
1104
1105         if (parent_class->render) {
1106                 (*parent_class->render) (item, buf);
1107         }
1108
1109         if (buf->is_bg) {
1110                 gnome_canvas_buf_ensure_buf (buf);
1111                 buf->is_bg = FALSE;
1112         }
1113
1114         /* a "unit" means a pixel */
1115
1116         /* begin: render start x (units) */
1117         int const begin = MAX (waveview->bbox_ulx, buf->rect.x0);
1118
1119         /* zbegin: start x for zero line (units) */
1120         int const zbegin = (begin == waveview->bbox_ulx) ? (begin + 1) : begin;
1121
1122         /* end: render end x (units) */
1123         int const end = (waveview->bbox_lrx >= 0) ? MIN (waveview->bbox_lrx,buf->rect.x1) : buf->rect.x1;
1124
1125         /* zend: end x for zero-line (units) */
1126         int const zend = (end == waveview->bbox_lrx) ? (end - 1) : end;
1127
1128         if (begin == end) {
1129                 return;
1130         }
1131
1132         /* s1: start sample
1133            s2: end sample
1134         */
1135
1136         s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit);
1137
1138         // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
1139
1140         if (end == waveview->bbox_lrx) {
1141                 /* This avoids minor rounding errors when we have the
1142                    entire region visible.
1143                 */
1144                 s2 = waveview->samples;
1145         } else {
1146                 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
1147         }
1148
1149 #if 0
1150         printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
1151                 " b/e %d..%d s= %lu..%lu @ %f\n",
1152                 waveview,
1153                 buf->rect.x0,
1154                 buf->rect.x1,
1155                 buf->rect.y0,
1156                 buf->rect.y1,
1157                 waveview->bbox_ulx,
1158                 waveview->bbox_lrx,
1159                 waveview->bbox_uly,
1160                 waveview->bbox_lry,
1161                 begin, end, s1, s2,
1162                 waveview->samples_per_unit);
1163 #endif
1164
1165         /* now ensure that the cache is full and properly
1166            positioned.
1167         */
1168
1169 //      check_cache (waveview, "pre-ensure");
1170
1171         if (waveview->cache_updater && waveview->reload_cache_in_render) {
1172                 waveview->cache->start = 0;
1173                 waveview->cache->end = 0;
1174                 waveview->reload_cache_in_render = FALSE;
1175         }
1176
1177 //      check_cache (waveview, "post-ensure");
1178
1179         /* don't rectify at single-sample zoom */
1180         if (waveview->rectified && waveview->samples_per_unit > 1) {
1181                 rectify = TRUE;
1182         }
1183         else {
1184                 rectify = FALSE;
1185         }
1186
1187         clip_length = MIN(5,(waveview->height/4));
1188
1189         /*
1190            Now draw each line, clipping it appropriately. The clipping
1191            is done by the macros PAINT_FOO().
1192         */
1193
1194         half_height = waveview->half_height;
1195
1196 /* this makes it slightly easier to comprehend whats going on */
1197 #define origin half_height
1198
1199         if (waveview->filled && !rectify) {
1200                 int prev_pymin = 1;
1201                 int prev_pymax = 0;
1202                 int last_pymin = 1;
1203                 int last_pymax = 0;
1204                 int next_pymin, next_pymax;
1205                 double max, min;
1206                 int next_clip_max = 0;
1207                 int next_clip_min = 0;
1208
1209                 if (s1 < waveview->samples_per_unit) {
1210                         /* we haven't got a prev vars to compare with, so outline the whole line here */
1211                         prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1212                         prev_pymin = prev_pymax;
1213                 }
1214                 else {
1215                         s1 -= waveview->samples_per_unit;
1216                 }
1217
1218                 if(end == waveview->bbox_lrx) {
1219                         /* we don't have the NEXT vars for the last sample */
1220                         last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1221                         last_pymin = last_pymax;
1222                 }
1223                 else {
1224                         s2 += waveview->samples_per_unit;
1225                 }
1226
1227                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1228
1229                 /*
1230                  * Compute the variables outside the rendering rect
1231                  */
1232                 if(prev_pymax != prev_pymin) {
1233
1234                         prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1235                         prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1236                         ++cache_index;
1237                 }
1238                 if(last_pymax != last_pymin) {
1239                         /* take the index of one sample right of what we render */
1240                         guint index = cache_index + (end - begin);
1241
1242                         if (index >= waveview->cache->data_size) {
1243
1244                                 /* the data we want is off the end of the cache, which must mean its beyond
1245                                    the end of the region's source; hence the peak values are 0 */
1246                                 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1247                                 last_pymin = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1248
1249                         } else {
1250
1251                                 last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1252                                 last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1253
1254                         }
1255
1256                 }
1257
1258                 /*
1259                  * initialize NEXT* variables for the first run, duplicated in the loop for speed
1260                  */
1261                 max = waveview->cache->data[cache_index].max;
1262                 min = waveview->cache->data[cache_index].min;
1263
1264                 if (max >= 1.0) {
1265                         max = 1.0;
1266                         next_clip_max = 1;
1267                 }
1268
1269                 if (min <= -1.0) {
1270                         min = -1.0;
1271                         next_clip_min = 1;
1272                 }
1273
1274                 max *= half_height;
1275                 min *= half_height;
1276
1277                 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1278                 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1279
1280                 /*
1281                  * And now the loop
1282                  */
1283                 for(x = begin; x < end; ++x) {
1284                         int clip_max = next_clip_max;
1285                         int clip_min = next_clip_min;
1286                         int fill_max, fill_min;
1287
1288                         pymax = next_pymax;
1289                         pymin = next_pymin;
1290
1291                         /* compute next */
1292                         if(x == end - 1) {
1293                                 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1294                                 next_pymax = last_pymax;
1295                                 next_pymin = last_pymin;
1296                         }
1297                         else {
1298                                 ++cache_index;
1299
1300                                 if (cache_index < waveview->cache->data_size) {
1301                                         max = waveview->cache->data[cache_index].max;
1302                                         min = waveview->cache->data[cache_index].min;
1303                                 } else {
1304                                         max = min = 0;
1305                                 }
1306
1307                                 next_clip_max = 0;
1308                                 next_clip_min = 0;
1309
1310                                 if (max >= 1.0) {
1311                                         max = 1.0;
1312                                         next_clip_max = 1;
1313                                 }
1314
1315                                 if (min <= -1.0) {
1316                                         min = -1.0;
1317                                         next_clip_min = 1;
1318                                 }
1319
1320                                 max *= half_height;
1321                                 min *= half_height;
1322
1323                                 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1324                                 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1325                         }
1326
1327                         /* render */
1328                         if (pymax == pymin) {
1329                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1330                         } else {
1331                                 if((prev_pymax < pymax && next_pymax < pymax) ||
1332                                    (prev_pymax == pymax && next_pymax == pymax)) {
1333                                         fill_max = pymax + 1;
1334                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1335                                 }
1336                                 else {
1337                                         fill_max = MAX(prev_pymax, next_pymax);
1338                                         if(pymax == fill_max) {
1339                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1340                                                 ++fill_max;
1341                                         }
1342                                         else {
1343                                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1344                                         }
1345                                 }
1346
1347                                 if((prev_pymin > pymin && next_pymin > pymin) ||
1348                                    (prev_pymin == pymin && next_pymin == pymin)) {
1349                                         fill_min = pymin - 1;
1350                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1351                                 }
1352                                 else {
1353                                         fill_min = MIN(prev_pymin, next_pymin);
1354                                         if(pymin == fill_min) {
1355                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1356                                         }
1357                                         else {
1358                                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin);
1359                                         }
1360                                 }
1361
1362                                 if(fill_max < fill_min) {
1363                                         PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min);
1364                                 }
1365                                 else if(fill_max == fill_min) {
1366                                         PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1367                                 }
1368                         }
1369
1370                         if (clip_max) {
1371                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1372                         }
1373
1374                         if (clip_min) {
1375                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1376                         }
1377
1378                         prev_pymax = pymax;
1379                         prev_pymin = pymin;
1380                 }
1381
1382         } else if (waveview->filled && rectify) {
1383
1384                 int prev_pymax = -1;
1385                 int last_pymax = -1;
1386                 int next_pymax;
1387                 double max, min;
1388                 int next_clip_max = 0;
1389                 int next_clip_min = 0;
1390
1391                 // for rectified, this stays constant throughout the loop
1392                 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1393
1394                 if(s1 < waveview->samples_per_unit) {
1395                         /* we haven't got a prev vars to compare with, so outline the whole line here */
1396                         prev_pymax = pymin;
1397                 }
1398                 else {
1399                         s1 -= waveview->samples_per_unit;
1400                 }
1401
1402                 if(end == waveview->bbox_lrx) {
1403                         /* we don't have the NEXT vars for the last sample */
1404                         last_pymax = pymin;
1405                 }
1406                 else {
1407                         s2 += waveview->samples_per_unit;
1408                 }
1409
1410                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1411
1412                 /*
1413                  * Compute the variables outside the rendering rect
1414                  */
1415                 if(prev_pymax < 0) {
1416                         max = MIN(waveview->cache->data[cache_index].max, 1.0);
1417                         min = MAX(waveview->cache->data[cache_index].min, -1.0);
1418
1419                         if (fabs (min) > fabs (max)) {
1420                                 max = fabs (min);
1421                         }
1422
1423                         prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1424                         ++cache_index;
1425                 }
1426                 if(last_pymax < 0) {
1427                         /* take the index of one sample right of what we render */
1428                         int index = cache_index + (end - begin);
1429
1430                         max = MIN(waveview->cache->data[index].max, 1.0);
1431                         min = MAX(waveview->cache->data[index].min, -1.0);
1432
1433                         if (fabs (min) > fabs (max)) {
1434                                 max = fabs (min);
1435                         }
1436
1437                         last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1438                 }
1439
1440                 /*
1441                  * initialize NEXT* variables for the first run, duplicated in the loop for speed
1442                  */
1443                 max = waveview->cache->data[cache_index].max;
1444                 min = waveview->cache->data[cache_index].min;
1445
1446                 if (max >= 1.0) {
1447                         max = 1.0;
1448                         next_clip_max = 1;
1449                 }
1450
1451                 if (min <= -1.0) {
1452                         min = -1.0;
1453                         next_clip_min = 1;
1454                 }
1455
1456                 if (fabs (min) > fabs (max)) {
1457                         max = fabs (min);
1458                 }
1459
1460                 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1461
1462                 /*
1463                  * And now the loop
1464                  */
1465                 for(x = begin; x < end; ++x) {
1466                         int clip_max = next_clip_max;
1467                         int clip_min = next_clip_min;
1468                         int fill_max;
1469
1470                         pymax = next_pymax;
1471
1472                         /* compute next */
1473                         if(x == end - 1) {
1474                                 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1475                                 next_pymax = last_pymax;
1476                         }
1477                         else {
1478                                 ++cache_index;
1479
1480                                 max = waveview->cache->data[cache_index].max;
1481                                 min = waveview->cache->data[cache_index].min;
1482
1483                                 if (max >= 1.0) {
1484                                         max = 1.0;
1485                                         next_clip_max = 1;
1486                                 }
1487
1488                                 if (min <= -1.0) {
1489                                         min = -1.0;
1490                                         next_clip_min = 1;
1491                                 }
1492
1493                                 if (fabs (min) > fabs (max)) {
1494                                         max = fabs (min);
1495                                 }
1496
1497                                 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1498                         }
1499
1500                         /* render */
1501                         if (pymax == pymin) {
1502                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1503                         } else {
1504                                 if((prev_pymax < pymax && next_pymax < pymax) ||
1505                                    (prev_pymax == pymax && next_pymax == pymax)) {
1506                                         fill_max = pymax + 1;
1507                                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1508                                 }
1509                                 else {
1510                                         fill_max = MAX(prev_pymax, next_pymax);
1511                                         if(pymax == fill_max) {
1512                                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1513                                                 ++fill_max;
1514                                         }
1515                                         else {
1516                                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1517                                         }
1518                                 }
1519
1520                                 if(fill_max < pymin) {
1521                                         PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
1522                                 }
1523                                 else if(fill_max == pymin) {
1524                                         PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
1525                                 }
1526                         }
1527
1528                         if (clip_max) {
1529                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1530                         }
1531
1532                         if (clip_min) {
1533                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1534                         }
1535
1536                         prev_pymax = pymax;
1537                 }
1538         }
1539         else {
1540                 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1541
1542                 for (x = begin; x < end; x++) {
1543
1544                         double max, min;
1545                         int clip_max, clip_min;
1546
1547                         clip_max = 0;
1548                         clip_min = 0;
1549
1550                         max = waveview->cache->data[cache_index].max;
1551                         min = waveview->cache->data[cache_index].min;
1552
1553                         if (max >= 1.0) {
1554                                 max = 1.0;
1555                                 clip_max = 1;
1556                         }
1557
1558                         if (min <= -1.0) {
1559                                 min = -1.0;
1560                                 clip_min = 1;
1561                         }
1562
1563                         if (rectify) {
1564
1565                                 if (fabs (min) > fabs (max)) {
1566                                         max = fabs (min);
1567                                 }
1568
1569                                 max = max * waveview->height;
1570
1571                                 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1572                                 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1573
1574                         } else {
1575
1576                                 max = max * half_height;
1577                                 min = min * half_height;
1578
1579                                 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1580                                 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1581                         }
1582
1583                         /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1584                            or, if samples_per_unit == 1, then a dot at each location.
1585                         */
1586
1587                         if (pymax == pymin) {
1588                                 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1589                         } else {
1590                                 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1591                         }
1592
1593                         /* show clipped waveforms with small red lines */
1594
1595                         if (clip_max) {
1596                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1597                         }
1598
1599                         if (clip_min) {
1600                                 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1601                         }
1602
1603                         /* presto, we're done */
1604
1605                         cache_index++;
1606                 }
1607         }
1608
1609         if (!waveview->rectified && waveview->zero_line) {
1610                 // Paint zeroline.
1611                 //PAINT_HORIZA(buf, waveview->zero_r, waveview->zero_g, waveview->zero_b, waveview->zero_a, begin, endi-1, origin );
1612
1613                 unsigned char zero_r, zero_g, zero_b, zero_a;
1614                 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a );
1615                 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1616                 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, zend, zeroline_y);
1617         }
1618 #undef origin
1619
1620 }
1621
1622 static void
1623 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1624                             GdkDrawable *drawable,
1625                             int x, int y,
1626                             int width, int height)
1627 {
1628         GnomeCanvasWaveView *waveview;
1629         cairo_t* cr;
1630         gulong s1, s2;
1631         int cache_index;
1632         gboolean rectify;
1633         double origin;
1634         double clip_length;
1635         double xoff;
1636         double yoff = 0.0;
1637         double ulx;
1638         double uly;
1639         double lrx;
1640         double lry;
1641
1642         waveview = GNOME_CANVAS_WAVEVIEW (item);
1643
1644         /* compute intersection of Drawable area and waveview,
1645            in canvas coordinate space
1646         */
1647
1648         if (x > waveview->bbox_ulx) {
1649                 ulx = x;
1650         } else {
1651                 ulx = waveview->bbox_ulx;
1652         }
1653
1654         if (y > waveview->bbox_uly) {
1655                 uly = y;
1656         } else {
1657                 uly = waveview->bbox_uly;
1658         }
1659
1660         if (x + width > waveview->bbox_lrx) {
1661                 lrx = waveview->bbox_lrx;
1662         } else {
1663                 lrx = x + width;
1664         }
1665
1666         if (y + height > waveview->bbox_lry) {
1667                 lry = waveview->bbox_lry;
1668         } else {
1669                 lry = y + height;
1670         }
1671
1672         /* figure out which samples we need for the resulting intersection */
1673
1674         s1 = floor ((ulx - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1675
1676         if (lrx == waveview->bbox_lrx) {
1677                 /* This avoids minor rounding errors when we have the
1678                    entire region visible.
1679                 */
1680                 s2 = waveview->samples;
1681         } else {
1682                 s2 = s1 + floor ((lrx - ulx) * waveview->samples_per_unit);
1683         }
1684
1685         /* translate back to buffer coordinate space */
1686
1687         ulx -= x;
1688         uly -= y;
1689         lrx -= x;
1690         lry -= y;
1691
1692         /* don't rectify at single-sample zoom */
1693         if(waveview->rectified && waveview->samples_per_unit > 1.0) {
1694                 rectify = TRUE;
1695         } else {
1696                 rectify = FALSE;
1697         }
1698
1699         clip_length = MIN(5,(waveview->height/4));
1700
1701         cr = gdk_cairo_create (drawable);
1702         cairo_set_line_width (cr, 0.5);
1703
1704         origin = waveview->bbox_uly - y + waveview->half_height;
1705
1706         cairo_rectangle (cr, ulx, uly, lrx - ulx, lry - uly);
1707         cairo_clip (cr);
1708
1709         if (waveview->cache_updater && waveview->reload_cache_in_render) {
1710                 waveview->cache->start = 0;
1711                 waveview->cache->end = 0;
1712                 waveview->reload_cache_in_render = FALSE;
1713         }
1714
1715         cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1716
1717 #if 0
1718         printf ("%p r (%d,%d)(%d,%d)[%d x %d] bbox (%d,%d)(%d,%d)[%d x %d]"
1719                 " draw (%.1f,%.1f)(%.1f,%.1f)[%.1f x %.1f] s= %lu..%lu\n",
1720                 waveview,
1721                 x, y,
1722                 x + width,
1723                 y + height,
1724                 width,
1725                 height,
1726                 waveview->bbox_ulx,
1727                 waveview->bbox_uly,
1728                 waveview->bbox_lrx,
1729                 waveview->bbox_lry,
1730                 waveview->bbox_lrx - waveview->bbox_ulx,
1731                 waveview->bbox_lry - waveview->bbox_uly,
1732                 ulx, uly,
1733                 lrx, lry,
1734                 lrx - ulx,
1735                 lry - uly,
1736                 s1, s2);
1737 #endif
1738
1739         /* draw the top half */
1740
1741         for (xoff = ulx; xoff < lrx; xoff++) {
1742                 double max, min;
1743
1744                 max = waveview->cache->data[cache_index].max;
1745                 min = waveview->cache->data[cache_index].min;
1746
1747                 if (min <= -1.0) {
1748                         min = -1.0;
1749                 }
1750
1751                 if (max >= 1.0) {
1752                         max = 1.0;
1753                 }
1754
1755                 if (rectify) {
1756                         if (fabs (min) > fabs (max)) {
1757                                 max = fabs (min);
1758                         }
1759                 }
1760
1761                 yoff = origin - (waveview->half_height * max) + 0.5;
1762
1763                 if (xoff == ulx) {
1764                         /* first point */
1765                         cairo_move_to (cr, xoff+0.5, yoff);
1766                 } else {
1767                         cairo_line_to (cr, xoff+0.5, yoff);
1768                 }
1769
1770                 cache_index++;
1771         }
1772
1773         /* from the final top point, move out of the clip zone */
1774
1775         cairo_line_to (cr, xoff + 10, yoff);
1776
1777         /* now draw the bottom half */
1778
1779         for (--xoff, --cache_index; xoff >= ulx; --xoff) {
1780                 double min;
1781
1782                 min = waveview->cache->data[cache_index].min;
1783
1784                 if (min <= -1.0) {
1785                         min = -1.0;
1786                 }
1787
1788                 yoff = origin - (waveview->half_height * min) + 0.5;
1789
1790                 cairo_line_to (cr, xoff+0.5, yoff);
1791                 cache_index--;
1792         }
1793
1794         /* from the final lower point, move out of the clip zone */
1795
1796         cairo_line_to (cr, xoff - 10, yoff);
1797
1798         /* close path to fill */
1799
1800         cairo_close_path (cr);
1801
1802         /* fill and stroke */
1803
1804         cairo_set_source_rgba (cr,
1805                                (waveview->fill_r/255.0),
1806                                (waveview->fill_g/255.0),
1807                                (waveview->fill_b/255.0),
1808                                (waveview->fill_a/255.0));
1809         cairo_fill_preserve (cr);
1810         cairo_set_source_rgba (cr,
1811                                (waveview->wave_r/255.0),
1812                                (waveview->wave_g/255.0),
1813                                (waveview->wave_b/255.0),
1814                                (waveview->wave_a/255.0));
1815         cairo_stroke (cr);
1816
1817         cairo_destroy (cr);
1818 }
1819
1820 #if 0
1821                 if (clip_max || clip_min) {
1822                         cairo_set_source_rgba (cr, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a);
1823                 }
1824
1825                 if (clip_max) {
1826                         cairo_move_to (cr, xoff, yoff1);
1827                         cairo_line_to (cr, xoff, yoff1 + clip_length);
1828                         cairo_stroke (cr);
1829                 }
1830
1831                 if (clip_min) {
1832                         cairo_move_to (cr, xoff, yoff2);
1833                         cairo_line_to (cr, xoff, yoff2 - clip_length);
1834                         cairo_stroke (cr);
1835                 }
1836
1837 #endif
1838
1839 static void
1840 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1841 {
1842         GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1843
1844         *x1 = waveview->x;
1845         *y1 = waveview->y;
1846
1847         *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1848         *y2 = *y1 + waveview->height;
1849
1850 #if 0
1851         x = 0; y = 0;
1852         gnome_canvas_item_i2w (item, &x, &y);
1853         gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1854         x = *x2;
1855         y = *y2;
1856         gnome_canvas_item_i2w (item, &x, &y);
1857         gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1858         printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1859 #endif
1860
1861 }
1862
1863 static double
1864 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1865 {
1866         (void) item;
1867         (void) x;
1868         (void) y;
1869         (void) cx;
1870         (void) cy;
1871         (void) actual_item;
1872
1873         /* XXX for now, point is never inside the wave
1874         GnomeCanvasWaveView *waveview;
1875         double x1, y1, x2, y2;
1876         double dx, dy;
1877         */
1878
1879         return DBL_MAX;
1880
1881 #if 0
1882         waveview = GNOME_CANVAS_WAVEVIEW (item);
1883
1884         *actual_item = item;
1885
1886         /* Find the bounds for the rectangle plus its outline width */
1887
1888         gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1889
1890         /* Is point inside rectangle */
1891
1892         if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1893                 return 0.0;
1894         }
1895
1896         /* Point is outside rectangle */
1897
1898         if (x < x1)
1899                 dx = x1 - x;
1900         else if (x > x2)
1901                 dx = x - x2;
1902         else
1903                 dx = 0.0;
1904
1905         if (y < y1)
1906                 dy = y1 - y;
1907         else if (y > y2)
1908                 dy = y - y2;
1909         else
1910                 dy = 0.0;
1911
1912         return sqrt (dx * dx + dy * dy);
1913 #endif
1914 }
1915