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