f47529973ec828e34d9a9f5d615be83199bb0ada
[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 <string.h>
25  #include <limits.h>
26
27  #include <ardour/dB.h>
28
29  #include "canvas-waveview.h"
30  #include "rgb_macros.h"
31
32  enum {
33          PROP_0,
34          PROP_DATA_SRC,
35          PROP_CHANNEL,
36          PROP_LENGTH_FUNCTION,
37          PROP_SOURCEFILE_LENGTH_FUNCTION,
38          PROP_PEAK_FUNCTION,
39          PROP_GAIN_FUNCTION,
40          PROP_GAIN_SRC,
41          PROP_CACHE,
42          PROP_CACHE_UPDATER,
43          PROP_SAMPLES_PER_UNIT,
44          PROP_AMPLITUDE_ABOVE_AXIS,
45          PROP_X,
46          PROP_Y,
47          PROP_HEIGHT,
48          PROP_WAVE_COLOR,
49          PROP_RECTIFIED,
50          PROP_REGION_START
51  };
52
53  static void gnome_canvas_waveview_class_init     (GnomeCanvasWaveViewClass *class);
54
55  static void gnome_canvas_waveview_init           (GnomeCanvasWaveView      *waveview);
56
57  static void gnome_canvas_waveview_destroy        (GtkObject            *object);
58
59  static void gnome_canvas_waveview_set_property   (GObject        *object,
60                                                    guint           prop_id,
61                                                    const GValue   *value,
62                                                    GParamSpec     *pspec);
63  static void gnome_canvas_waveview_get_property   (GObject        *object,
64                                                    guint           prop_id,
65                                                    GValue         *value,
66                                                    GParamSpec     *pspec);
67
68  static void   gnome_canvas_waveview_update       (GnomeCanvasItem *item,
69                                                    double          *affine,
70                                                    ArtSVP          *clip_path,
71                                                    int              flags);
72
73  static void   gnome_canvas_waveview_bounds       (GnomeCanvasItem *item,
74                                                    double          *x1,
75                                                    double          *y1,
76                                                    double          *x2,
77                                                    double          *y2);
78
79  static double gnome_canvas_waveview_point        (GnomeCanvasItem  *item,
80                                                    double            x,
81                                                    double            y,
82                                                    int               cx,
83                                                    int               cy,
84                                                    GnomeCanvasItem **actual_item);
85
86  static void gnome_canvas_waveview_render         (GnomeCanvasItem *item,
87                                                    GnomeCanvasBuf  *buf);
88
89  static void gnome_canvas_waveview_draw           (GnomeCanvasItem *item,
90                                                    GdkDrawable     *drawable,
91                                                    int              x,
92                                                    int              y,
93                                                    int              w,
94                                                    int              h);
95
96  static void gnome_canvas_waveview_set_data_src   (GnomeCanvasWaveView *,
97                                                    void *);
98
99  static void gnome_canvas_waveview_set_channel    (GnomeCanvasWaveView *,
100                                                    guint32);
101
102  static gint32 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview,
103                                                    gulong               start_sample,
104                                                    gulong               end_sample);
105
106  static GnomeCanvasItemClass *parent_class;
107
108  GType
109  gnome_canvas_waveview_get_type (void)
110  {
111          static GType waveview_type;
112
113          if (!waveview_type) {
114                  static const GTypeInfo object_info = {
115                          sizeof (GnomeCanvasWaveViewClass),
116                          (GBaseInitFunc) NULL,
117                          (GBaseFinalizeFunc) NULL,
118                          (GClassInitFunc) gnome_canvas_waveview_class_init,
119                          (GClassFinalizeFunc) NULL,
120                          NULL,                  /* class_data */
121                          sizeof (GnomeCanvasWaveView),
122                          0,                     /* n_preallocs */
123                          (GInstanceInitFunc) gnome_canvas_waveview_init,
124                          NULL                   /* value_table */
125                  };
126
127                  waveview_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasWaveView",
128                                                          &object_info, 0);
129          }
130
131          return waveview_type;
132  }
133
134  static void
135  gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class)
136  {
137          GObjectClass *gobject_class;
138          GtkObjectClass *object_class;
139          GnomeCanvasItemClass *item_class;
140
141          gobject_class = (GObjectClass *) class;
142          object_class = (GtkObjectClass *) class;
143          item_class = (GnomeCanvasItemClass *) class;
144
145          parent_class = g_type_class_peek_parent (class);
146
147          gobject_class->set_property = gnome_canvas_waveview_set_property;
148          gobject_class->get_property = gnome_canvas_waveview_get_property;
149
150          g_object_class_install_property
151                  (gobject_class,
152                   PROP_DATA_SRC,
153                   g_param_spec_pointer ("data_src", NULL, NULL,
154                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
155          
156          g_object_class_install_property
157                  (gobject_class,
158                   PROP_CHANNEL,
159                   g_param_spec_uint ("channel", NULL, NULL,
160                                      0, G_MAXUINT, 0,
161                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
162          
163          g_object_class_install_property
164                  (gobject_class,
165                   PROP_LENGTH_FUNCTION,
166                   g_param_spec_pointer ("length_function", NULL, NULL,
167                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
168          
169          g_object_class_install_property
170                 (gobject_class,
171                  PROP_SOURCEFILE_LENGTH_FUNCTION,
172                  g_param_spec_pointer ("sourcefile_length_function", NULL, NULL,
173                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
174          
175          g_object_class_install_property
176                  (gobject_class,
177                   PROP_PEAK_FUNCTION,
178                   g_param_spec_pointer ("peak_function", NULL, NULL,
179                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
180          
181          g_object_class_install_property
182                  (gobject_class,
183                   PROP_GAIN_FUNCTION,
184                   g_param_spec_pointer ("gain_function", NULL, NULL,
185                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
186          
187          g_object_class_install_property
188                  (gobject_class,
189                  PROP_GAIN_SRC,
190                  g_param_spec_pointer ("gain_src", NULL, NULL,
191                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
192         
193          g_object_class_install_property
194                  (gobject_class,
195                   PROP_CACHE,
196                   g_param_spec_pointer ("cache", NULL, NULL,
197                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
198          
199          g_object_class_install_property
200                  (gobject_class,
201                   PROP_CACHE_UPDATER,
202                  g_param_spec_boolean ("cache_updater", NULL, NULL,
203                                        FALSE,
204                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
205          
206          g_object_class_install_property
207                  (gobject_class,
208                   PROP_SAMPLES_PER_UNIT,
209                   g_param_spec_double ("samples_per_unit", NULL, NULL,
210                                        0.0, G_MAXDOUBLE, 0.0,
211                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
212          
213          g_object_class_install_property
214                  (gobject_class,
215                   PROP_AMPLITUDE_ABOVE_AXIS,
216                   g_param_spec_double ("amplitude_above_axis", NULL, NULL,
217                                        0.0, G_MAXDOUBLE, 0.0,
218                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
219          
220          g_object_class_install_property
221                  (gobject_class,
222                   PROP_X,
223                   g_param_spec_double ("x", NULL, NULL,
224                                        0.0, G_MAXDOUBLE, 0.0,
225                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
226          
227          g_object_class_install_property
228                  (gobject_class,
229                   PROP_Y,
230                   g_param_spec_double ("y", NULL, NULL,
231                                        0.0, G_MAXDOUBLE, 0.0,
232                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
233          
234          g_object_class_install_property
235                  (gobject_class,
236                   PROP_HEIGHT,
237                   g_param_spec_double ("height", NULL, NULL,
238                                        0.0, G_MAXDOUBLE, 0.0,
239                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
240          
241          g_object_class_install_property
242                  (gobject_class,
243                   PROP_WAVE_COLOR,
244                   g_param_spec_uint ("wave_color", NULL, NULL,
245                                      0, G_MAXUINT, 0,
246                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
247          
248          g_object_class_install_property
249                  (gobject_class,
250                   PROP_RECTIFIED,
251                   g_param_spec_boolean ("rectified", NULL, NULL,
252                                         FALSE,
253                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
254          
255          g_object_class_install_property
256                  (gobject_class,
257                   PROP_REGION_START,
258                   g_param_spec_uint ("region_start", NULL, NULL,
259                                      0, G_MAXUINT, 0,
260                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
261          
262          object_class->destroy = gnome_canvas_waveview_destroy;
263          
264          item_class->update = gnome_canvas_waveview_update;
265          item_class->bounds = gnome_canvas_waveview_bounds;
266          item_class->point = gnome_canvas_waveview_point;
267          item_class->render = gnome_canvas_waveview_render;
268          item_class->draw = gnome_canvas_waveview_draw;
269  }
270
271 GnomeCanvasWaveViewCache*
272 gnome_canvas_waveview_cache_new ()
273 {
274         GnomeCanvasWaveViewCache *c;
275
276         c = g_malloc (sizeof (GnomeCanvasWaveViewCache));
277
278         c->allocated = 2048;
279         c->data = g_malloc (sizeof (GnomeCanvasWaveViewCacheEntry) * c->allocated);
280         c->data_size = 0;
281         c->start = 0;
282         c->end = 0;
283
284         return c;
285 }
286
287 void
288 gnome_canvas_waveview_cache_destroy (GnomeCanvasWaveViewCache* cache)
289 {
290         g_free (cache->data);
291         g_free (cache);
292 }
293
294 static void
295 gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview)
296 {
297         waveview->x = 0.0;
298         waveview->y = 0.0;
299         waveview->cache = 0;
300         waveview->cache_updater = FALSE;
301         waveview->data_src = NULL;
302         waveview->channel = 0;
303         waveview->peak_function = NULL;
304         waveview->length_function = NULL;
305         waveview->sourcefile_length_function = NULL;
306         waveview->gain_curve_function = NULL;
307         waveview->gain_src = NULL;
308         waveview->rectified = FALSE;
309         waveview->region_start = 0;
310         waveview->samples_per_unit = 1.0;
311         waveview->amplitude_above_axis = 1.0;
312         waveview->height = 100.0;
313         waveview->screen_width = gdk_screen_width ();
314         waveview->reload_cache_in_render = FALSE;
315
316         waveview->wave_color = RGBA_TO_UINT(44,35,126,255);
317
318         // GTK2FIX
319         // GNOME_CANVAS_ITEM(waveview)->object.flags |= GNOME_CANVAS_ITEM_NO_AUTO_REDRAW;
320 }
321
322 static void
323 gnome_canvas_waveview_destroy (GtkObject *object)
324 {
325         GnomeCanvasWaveView *waveview;
326
327         g_return_if_fail (object != NULL);
328         g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
329
330         waveview = GNOME_CANVAS_WAVEVIEW (object);
331
332         gnome_canvas_waveview_cache_destroy (waveview->cache);
333
334         if (GTK_OBJECT_CLASS (parent_class)->destroy)
335                 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
336 }
337
338 #define DEBUG_CACHE 0
339
340 static gint32
341 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_sample, gulong end_sample)
342 {
343         gulong required_cache_entries;
344         gulong rf1, rf2,rf3, required_frames;
345         gulong new_cache_start, new_cache_end;
346         gulong half_width;
347         gulong npeaks;
348         gulong offset;
349         gulong ostart;
350         gulong copied;
351         GnomeCanvasWaveViewCache *cache;
352         float* gain;
353
354         cache = waveview->cache;
355
356         start_sample = start_sample + waveview->region_start;
357         end_sample = end_sample + waveview->region_start;
358 #if DEBUG_CACHE
359         // printf("waveview->region_start == %lu\n",waveview->region_start);
360         printf ("=> 0x%x cache @ 0x%x range: %lu - %lu request: %lu - %lu (%lu frames)\n", 
361                 waveview, cache,
362                 cache->start, cache->end,
363                 start_sample, end_sample, end_sample - start_sample);
364 #endif
365                 
366         if (cache->start <= start_sample && cache->end >= end_sample) {
367 #if DEBUG_CACHE
368                 // printf ("0x%x: cache hit for %lu-%lu (cache holds: %lu-%lu\n",
369                 // waveview, start_sample, end_sample, cache->start, cache->end);
370 #endif
371                 goto out;
372         }
373
374         /* make sure the cache is at least twice as wide as the screen width, and put the start sample
375            in the middle, ensuring that we cover the end_sample. 
376         */
377
378         /* Note the assumption that we have a 1:1 units:pixel ratio for the canvas. Its everywhere ... */
379         
380         half_width = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit)/2.0 + 0.5);
381         
382         if (start_sample < half_width) {
383                 new_cache_start = 0;
384         } else {
385                 new_cache_start = start_sample - half_width;
386         }
387
388         /* figure out how many frames we want */
389
390         rf1 = end_sample - start_sample + 1;
391         rf2 = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit * 2.0f));
392         required_frames = MAX(rf1,rf2);
393
394         /* but make sure it doesn't extend beyond the end of the source material */
395
396         rf3 = (gulong) (waveview->sourcefile_length_function (waveview->data_src)) + 1;
397         rf3 -= new_cache_start;
398
399 #if DEBUG_CACHE
400         fprintf (stderr, "\n\nAVAILABLE FRAMES = %lu of %lu, start = %lu, sstart = %lu, cstart = %lu\n", 
401                  rf3, waveview->sourcefile_length_function (waveview->data_src),
402                  waveview->region_start, start_sample, new_cache_start);
403 #endif
404
405         required_frames = MIN(required_frames,rf3);
406
407         new_cache_end = new_cache_start + required_frames - 1;
408
409         required_cache_entries = (gulong) floor (required_frames / waveview->samples_per_unit );
410
411 #if DEBUG_CACHE
412         fprintf (stderr, "new cache = %lu - %lu\n", new_cache_start, new_cache_end);
413         fprintf(stderr,"required_cach_entries = %lu, samples_per_unit = %f\n",
414                 required_cache_entries,waveview->samples_per_unit);
415 #endif
416
417         if (required_cache_entries > cache->allocated) {
418                 cache->data = g_realloc (cache->data, sizeof (GnomeCanvasWaveViewCacheEntry) * required_cache_entries);
419                 cache->allocated = required_cache_entries;
420                 // cache->start = 0;
421                 // cache->end = 0;
422         }
423
424         ostart = new_cache_start;
425
426 #undef CACHE_MEMMOVE_OPTIMIZATION
427 #ifdef CACHE_MEMMOVE_OPTIMIZATION
428         
429         /* data is not entirely in the cache, so go fetch it, making sure to fill the cache */
430
431         /* some of the required cache entries are in the cache, but in the wrong
432            locations. use memmove to fix this.
433         */
434
435         if (cache->start < new_cache_start && new_cache_start < cache->end) {
436                 
437                 /* case one: the common area is at the end of the existing cache. move it 
438                    to the beginning of the cache, and set up to refill whatever remains.
439                    
440                    
441                            wv->cache_start                                        wv->cache_end
442                            |-------------------------------------------------------| cache
443                                                                |--------------------------------| requested
444                                                                <------------------->
445                                                                      "present"
446                                                             new_cache_start                      new_cache_end       
447                 */
448                                 
449
450                 present_frames = cache->end - new_cache_start;
451                 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
452
453 #if DEBUG_CACHE         
454                 fprintf (stderr, "existing material at end of current cache, move to start of new cache\n"
455                          "\tcopy from %lu to start\n", cache->data_size - present_entries);
456 #endif
457
458                 memmove (&cache->data[0],
459                          &cache->data[cache->data_size - present_entries],
460                          present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
461                 
462 #if DEBUG_CACHE
463                 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
464                          present_frames, required_frames, present_entries, new_cache_start + present_entries,
465                          cache->data + present_entries);
466 #endif
467
468                 copied = present_entries;
469                 offset = present_entries;
470                 new_cache_start += present_frames;
471                 required_frames -= present_frames;
472
473         } else if (new_cache_end > cache->start && new_cache_end < cache->end) {
474
475                 /* case two: the common area lives at the beginning of the existing cache. 
476                    
477                                             wv->cache_start                                      wv->cache_end
478                                              |-----------------------------------------------------|
479                               |--------------------------------|
480                                              <----------------->
481                                                 "present"
482
483                              new_cache_start                      new_cache_end
484                 */
485                 
486                 present_frames = new_cache_end - cache->start;
487                 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
488
489                 memmove (&cache->data[cache->data_size - present_entries],
490                          &cache->data[0],
491                          present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
492                 
493 #if DEBUG_CACHE         
494                 fprintf (stderr, "existing material at start of current cache, move to start of end cache\n");
495 #endif
496
497 #if DEBUG_CACHE
498                 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
499                          present_entries, required_frames, present_entries, new_cache_start + present_entries,
500                          cache->data + present_entries);
501 #endif
502
503                 copied = present_entries;
504                 offset = 0;
505                 required_frames -= present_frames;
506
507                 
508         } else {
509                 copied = 0;
510                 offset = 0;
511
512         }
513
514 #else
515         copied = 0;
516         offset = 0;
517
518 #endif /* CACHE_MEMMOVE_OPTIMIZATION */
519
520 //      fprintf(stderr,"length == %lu\n",waveview->length_function (waveview->data_src));
521 //      required_frames = MIN (waveview->length_function (waveview->data_src) - new_cache_start, required_frames);
522         npeaks = (gulong) floor (required_frames / waveview->samples_per_unit);
523         npeaks = MAX (1, npeaks);
524         required_frames = npeaks * waveview->samples_per_unit;
525
526 #if DEBUG_CACHE
527
528
529         printf ("requesting %lu/%f to cover %lu-%lu at %f spu (request was %lu-%lu) into cache + %lu\n",
530                 required_frames, required_frames/waveview->samples_per_unit, new_cache_start, new_cache_end,
531                 waveview->samples_per_unit, start_sample, end_sample, offset);
532 #endif
533
534 #if DEBUG_CACHE
535 //      printf ("cache holds %lu entries, requesting %lu to cover %lu-%lu (request was %lu-%lu)\n",
536 //              cache->data_size, npeaks, new_cache_start, new_cache_end,
537 //              start_sample, end_sample);
538 #endif
539
540         waveview->peak_function (waveview->data_src, npeaks, new_cache_start, required_frames, cache->data + offset, waveview->channel,waveview->samples_per_unit);
541
542         /* take into account any copied peaks */
543
544         npeaks += copied;
545
546         if (npeaks < cache->allocated) {
547 #if DEBUG_CACHE
548                 fprintf (stderr, "zero fill cache for %lu at %lu\n", cache->allocated - npeaks, npeaks);
549 #endif
550                 memset (&cache->data[npeaks], 0, sizeof (GnomeCanvasWaveViewCacheEntry) * (cache->allocated - npeaks));
551                 cache->data_size = npeaks;
552         } else {
553                 cache->data_size = cache->allocated;
554         }
555
556         if (waveview->gain_curve_function) {
557                 guint32 n;
558
559                 gain = (float*) malloc (sizeof (float) * cache->data_size);
560
561                 waveview->gain_curve_function (waveview->gain_src, new_cache_start, new_cache_end, gain, cache->data_size);
562
563                 for (n = 0; n < cache->data_size; ++n) {
564                         cache->data[n].min *= gain[n];
565                         cache->data[n].max *= gain[n];
566                 }
567
568                 free (gain);
569         
570         }
571         
572         cache->start = ostart;
573         cache->end = new_cache_end;
574
575   out:
576 #if DEBUG_CACHE
577         fprintf (stderr, "return cache index = %d\n", 
578                  (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5));
579 #endif
580         return (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5);
581
582 }
583
584 void
585 gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *waveview, void *data_src)
586 {
587
588         if (waveview->cache_updater) {
589                 if (waveview->data_src == data_src) {
590                         waveview->reload_cache_in_render = TRUE;
591                         return;
592                 }
593         
594                 waveview->cache->start  = 0;
595                 waveview->cache->end = 0;
596         }
597
598         waveview->data_src = data_src;
599 }
600
601 void
602 gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *waveview, guint32 chan)
603 {
604         if (waveview->channel == chan) {
605                 return;
606         }
607         
608         waveview->channel = chan;
609 }
610
611 static void 
612 gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
613
614 {
615         double x1, x2, y1, y2;
616         ArtPoint i1, i2;
617         ArtPoint w1, w2;
618         int Ix1, Ix2, Iy1, Iy2;
619         double i2w[6];
620
621         gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
622
623         i1.x = x1;
624         i1.y = y1;
625         i2.x = x2;
626         i2.y = y2;
627
628         gnome_canvas_item_i2w_affine (item, i2w);
629         art_affine_point (&w1, &i1, i2w);
630         art_affine_point (&w2, &i2, i2w);
631
632         Ix1 = (int) rint(w1.x);
633         Ix2 = (int) rint(w2.x);
634         Iy1 = (int) rint(w1.y);
635         Iy2 = (int) rint(w2.y);
636
637         gnome_canvas_update_bbox (item, Ix1, Iy1, Ix2, Iy2);
638 }
639
640 /* 
641  * CANVAS CALLBACKS 
642  */
643
644 static void
645 gnome_canvas_waveview_set_property (GObject      *object,
646                                     guint         prop_id,
647                                     const GValue *value,
648                                     GParamSpec   *pspec)
649
650 {
651         GnomeCanvasItem *item;
652         GnomeCanvasWaveView *waveview;
653         int redraw = FALSE;
654         int calc_bounds = FALSE;
655
656         g_return_if_fail (object != NULL);
657         g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
658
659         item = GNOME_CANVAS_ITEM (object);
660         waveview = GNOME_CANVAS_WAVEVIEW (object);
661
662         switch (prop_id) {
663         case PROP_DATA_SRC:
664                 gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
665                 redraw = TRUE;
666                 break;
667
668         case PROP_CHANNEL:
669                 gnome_canvas_waveview_set_channel (waveview, g_value_get_uint(value));
670                 redraw = TRUE;
671                 break;
672
673         case PROP_LENGTH_FUNCTION:
674                 waveview->length_function = g_value_get_pointer(value);
675                 redraw = TRUE;
676                 break;
677         case PROP_SOURCEFILE_LENGTH_FUNCTION:
678                 waveview->sourcefile_length_function = g_value_get_pointer(value);
679                 redraw = TRUE;
680                 break;
681
682         case PROP_PEAK_FUNCTION:
683                 waveview->peak_function = g_value_get_pointer(value);
684                 redraw = TRUE;
685                 break;
686
687         case PROP_GAIN_FUNCTION:
688                 waveview->gain_curve_function = g_value_get_pointer(value);
689                 redraw = TRUE;
690                 break;
691
692         case PROP_GAIN_SRC:
693                 waveview->gain_src = g_value_get_pointer(value);
694                 if (waveview->cache_updater) {
695                         waveview->cache->start = 0;
696                         waveview->cache->end = 0;
697                 }
698                 redraw = TRUE;
699                 calc_bounds = TRUE;
700                 break;
701
702         case PROP_CACHE:
703                 waveview->cache = g_value_get_pointer(value);
704                 redraw = TRUE;
705                 break;
706
707
708         case PROP_CACHE_UPDATER:
709                 waveview->cache_updater = g_value_get_boolean(value);
710                 redraw = TRUE;
711                 break;
712
713         case PROP_SAMPLES_PER_UNIT:
714                 if ((waveview->samples_per_unit = g_value_get_double(value)) < 1.0) {
715                         waveview->samples_per_unit = 1.0;
716                 }
717                 if (waveview->cache_updater) {
718                         waveview->cache->start = 0;
719                         waveview->cache->end = 0;
720                 }
721                 redraw = TRUE;
722                 calc_bounds = TRUE;
723                 break;
724
725         case PROP_AMPLITUDE_ABOVE_AXIS:
726                 waveview->amplitude_above_axis = g_value_get_double(value);
727                 redraw = TRUE;
728                 break;
729
730         case PROP_X:
731                 if (waveview->x != g_value_get_double (value)) {
732                         waveview->x = g_value_get_double (value);
733                         calc_bounds = TRUE;
734                 }
735                 break;
736
737         case PROP_Y:
738                 if (waveview->y != g_value_get_double (value)) {
739                         waveview->y = g_value_get_double (value);
740                         calc_bounds = TRUE;
741                 }
742                 break;
743
744         case PROP_HEIGHT:
745                 if (waveview->height != fabs (g_value_get_double (value))) {
746                         waveview->height = fabs (g_value_get_double (value));
747                         redraw = TRUE;
748                 }
749                 break;
750
751         case PROP_WAVE_COLOR:
752                 if (waveview->wave_color != g_value_get_uint(value)) {
753                         waveview->wave_color = g_value_get_uint(value);
754                         redraw = TRUE;
755                 }
756                 break;
757
758         case PROP_RECTIFIED:
759                 if (waveview->rectified != g_value_get_boolean(value)) {
760                         waveview->rectified = g_value_get_boolean(value);
761                         redraw = TRUE;
762                 }
763                 break;
764         case PROP_REGION_START:
765                 waveview->region_start = g_value_get_uint(value);
766                 redraw = TRUE;
767                 calc_bounds = TRUE;
768                 break;
769
770
771         default:
772                 break;
773         }
774
775         if (calc_bounds) {
776                 gnome_canvas_waveview_reset_bounds (item);
777         }
778
779         if (redraw) {
780                 gnome_canvas_item_request_update (item);
781         }
782
783 }
784
785 static void
786 gnome_canvas_waveview_get_property (GObject      *object,
787                                     guint         prop_id,
788                                     GValue       *value,
789                                     GParamSpec   *pspec)
790 {
791         
792    
793         g_return_if_fail (object != NULL);
794         g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
795
796         GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
797
798         switch (prop_id) {
799         case PROP_DATA_SRC:
800                 g_value_set_pointer(value, waveview->data_src);
801                 break;
802
803         case PROP_CHANNEL:
804                 g_value_set_uint(value, waveview->channel);
805                 break;
806
807         case PROP_LENGTH_FUNCTION:
808                 g_value_set_pointer(value, waveview->length_function);
809                 break;
810
811         case PROP_SOURCEFILE_LENGTH_FUNCTION:
812                 g_value_set_pointer(value, waveview->sourcefile_length_function);
813                 break;
814
815         case PROP_PEAK_FUNCTION:
816                 g_value_set_pointer(value, waveview->peak_function);
817                 break;
818
819         case PROP_GAIN_FUNCTION:
820                 g_value_set_pointer(value, waveview->gain_curve_function);
821                 break;
822
823         case PROP_GAIN_SRC:
824                 g_value_set_pointer(value, waveview->gain_src);
825                 break;
826
827         case PROP_CACHE:
828                 g_value_set_pointer(value, waveview->cache);
829                 break;
830
831         case PROP_CACHE_UPDATER:
832                 g_value_set_boolean(value, waveview->cache_updater);
833                 break;
834
835         case PROP_SAMPLES_PER_UNIT:
836                 g_value_set_double(value, waveview->samples_per_unit);
837                 break;
838
839         case PROP_AMPLITUDE_ABOVE_AXIS:
840                 g_value_set_double(value, waveview->amplitude_above_axis);
841                 break;
842
843         case PROP_X:
844                 g_value_set_double (value, waveview->x);
845                 break;
846
847         case PROP_Y:
848                 g_value_set_double (value, waveview->y);
849                 break;
850
851         case PROP_HEIGHT:
852                 g_value_set_double (value, waveview->height);
853                 break;
854
855         case PROP_WAVE_COLOR:
856                 g_value_set_uint (value, waveview->wave_color);
857                 break;
858
859         case PROP_RECTIFIED:
860                 g_value_set_boolean (value, waveview->rectified);
861
862         case PROP_REGION_START:
863                 g_value_set_uint (value, waveview->region_start);
864         default:
865                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
866                 break;
867         }
868 }
869
870 static void
871 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
872 {
873         GnomeCanvasWaveView *waveview;
874         double x, y;
875
876         waveview = GNOME_CANVAS_WAVEVIEW (item);
877
878 //      check_cache (waveview, "start of update");
879
880         if (parent_class->update)
881                 (* parent_class->update) (item, affine, clip_path, flags);
882
883         gnome_canvas_waveview_reset_bounds (item);
884
885         /* get the canvas coordinates of the view. Do NOT use affines
886            for this, because they do not round to the integer units used
887            by the canvas, resulting in subtle pixel-level errors later.
888         */
889
890         x = waveview->x;
891         y = waveview->y;
892
893         gnome_canvas_item_i2w (item, &x, &y);
894         gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
895
896         waveview->samples = waveview->length_function (waveview->data_src);
897
898         x = waveview->x + (waveview->samples / waveview->samples_per_unit);
899         y = waveview->y + waveview->height;
900
901         gnome_canvas_item_i2w (item, &x, &y);
902         gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
903
904         /* cache the half-height and the end point in canvas units */
905
906         waveview->half_height = waveview->height / 2.0;
907
908         /* parse the color */
909
910         UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
911                       &waveview->wave_a);
912
913 //      check_cache (waveview, "end of update");
914 }
915
916 static void
917 gnome_canvas_waveview_render (GnomeCanvasItem *item,
918                             GnomeCanvasBuf *buf)
919 {
920         GnomeCanvasWaveView *waveview;
921         gulong s1, s2;
922         int clip_length = 0;
923         int pymin, pymax;
924         int cache_index;
925         double half_height;
926         int x, end, begin;
927
928         waveview = GNOME_CANVAS_WAVEVIEW (item);
929
930 //      check_cache (waveview, "start of render");
931
932         if (parent_class->render) {
933                 (*parent_class->render) (item, buf);
934         }
935
936         if (buf->is_bg) {
937                 gnome_canvas_buf_ensure_buf (buf);
938                 buf->is_bg = FALSE;
939         }
940
941         begin = MAX(waveview->bbox_ulx,buf->rect.x0);
942
943         if (waveview->bbox_lrx >= 0) {
944                 end = MIN(waveview->bbox_lrx,buf->rect.x1);
945         } else {
946                 end = buf->rect.x1;
947         }
948
949         if (begin == end) {
950                 return;
951         }
952
953         s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit) ;
954
955         // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
956
957         if (end == waveview->bbox_lrx) {
958                 /* This avoids minor rounding errors when we have the
959                    entire region visible.
960                 */
961                 s2 = waveview->samples;
962         } else {
963                 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
964         }
965
966 #if 0
967         printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
968                 " b/e %d..%d s= %lu..%lu\n",
969                 waveview,
970                 buf->rect.x0,
971                 buf->rect.x1,
972                 buf->rect.y0,
973                 buf->rect.y1,
974                 waveview->bbox_ulx,
975                 waveview->bbox_lrx,
976                 waveview->bbox_uly,
977                 waveview->bbox_lry,
978                 begin, end, s1, s2);
979 #endif
980
981         /* now ensure that the cache is full and properly
982            positioned.
983         */
984
985 //      check_cache (waveview, "pre-ensure");
986
987         if (waveview->cache_updater && waveview->reload_cache_in_render) {
988                 waveview->cache->start = 0;
989                 waveview->cache->end = 0;
990                 waveview->reload_cache_in_render = FALSE;
991         }
992
993         cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
994
995 //      check_cache (waveview, "post-ensure");
996
997         /* 
998            Now draw each line, clipping it appropriately. The clipping
999            is done by the macros PAINT_FOO().
1000         */
1001
1002         half_height = waveview->half_height;
1003
1004 /* this makes it slightly easier to comprehend whats going on */
1005
1006 #define origin half_height
1007
1008         for (x = begin; x < end; x++) {
1009
1010                 double max, min;
1011                 int clip_max, clip_min;
1012                 
1013                 clip_max = 0;
1014                 clip_min = 0;
1015
1016                 max = waveview->cache->data[cache_index].max;
1017                 min = waveview->cache->data[cache_index].min;
1018                 
1019                 if (max >= 1.0) {
1020                         max = 1.0;
1021                         clip_max = 1;
1022                 }
1023
1024                 if (min <= -1.0) {
1025                         min = -1.0;
1026                         clip_min = 1;
1027                 }
1028
1029                 /* don't rectify at single-sample zoom */
1030
1031                 if (waveview->rectified && waveview->samples_per_unit > 1) {
1032
1033                         if (fabs (min) > fabs (max)) {
1034                                 max = fabs (min);
1035                         } 
1036
1037                         max = max * waveview->height;
1038
1039                         pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1040                         pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1041
1042                 } else {
1043                         
1044                         max = max * half_height;
1045                         min = min * half_height;
1046                         
1047                         pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1048                         pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1049                 }
1050
1051                 /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1052                    or, if samples_per_unit == 1, then a dot at each location.
1053                 */
1054
1055                 if (pymax == pymin) {
1056                         PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1057                 } else {
1058                         PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1059                 }
1060                 
1061                 /* show clipped waveforms with small red lines */
1062
1063                 if (clip_max || clip_min) {
1064                         clip_length = MIN(5,(waveview->height/4));
1065                 }
1066
1067                 if (clip_max) {
1068                         PAINT_VERT(buf, 255, 0, 0, x, pymax, pymax+clip_length);
1069                 }
1070
1071                 if (clip_min) {
1072                         PAINT_VERT(buf, 255, 0, 0, x, pymin-clip_length, pymin);
1073                 }
1074
1075                 /* presto, we're done */
1076                 
1077                 cache_index++;
1078         }
1079
1080 #undef origin
1081
1082 }
1083
1084 static void
1085 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1086                           GdkDrawable *drawable,
1087                           int x, int y,
1088                           int width, int height)
1089 {
1090         GnomeCanvasWaveView *waveview;
1091
1092         waveview = GNOME_CANVAS_WAVEVIEW (item);
1093
1094         if (parent_class->draw) {
1095                 (* parent_class->draw) (item, drawable, x, y, width, height);
1096         }
1097
1098         fprintf (stderr, "please don't use the CanvasWaveView item in a non-aa Canvas\n");
1099         abort ();
1100 }
1101
1102 static void
1103 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1104 {
1105         GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1106
1107         *x1 = waveview->x;
1108         *y1 = waveview->y;
1109
1110         *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1111         *y2 = *y1 + waveview->height;
1112
1113 #if 0
1114         x = 0; y = 0;
1115         gnome_canvas_item_i2w (item, &x, &y);
1116         gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1117         x = *x2;
1118         y = *y2;
1119         gnome_canvas_item_i2w (item, &x, &y);
1120         gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1121         printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1122 #endif          
1123
1124 }
1125
1126 static double
1127 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1128 {
1129         /* XXX for now, point is never inside the wave 
1130         GnomeCanvasWaveView *waveview;
1131         double x1, y1, x2, y2;
1132         double dx, dy;
1133         */
1134
1135         return DBL_MAX;
1136
1137 #if 0
1138         waveview = GNOME_CANVAS_WAVEVIEW (item);
1139
1140         *actual_item = item;
1141
1142         /* Find the bounds for the rectangle plus its outline width */
1143
1144         gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1145
1146         /* Is point inside rectangle */
1147         
1148         if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1149                 return 0.0;
1150         }
1151
1152         /* Point is outside rectangle */
1153
1154         if (x < x1)
1155                 dx = x1 - x;
1156         else if (x > x2)
1157                 dx = x - x2;
1158         else
1159                 dx = 0.0;
1160
1161         if (y < y1)
1162                 dy = y1 - y;
1163         else if (y > y2)
1164                 dy = y - y2;
1165         else
1166                 dy = 0.0;
1167
1168         return sqrt (dx * dx + dy * dy);
1169 #endif
1170 }
1171