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