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