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