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