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