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