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