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