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