2 Copyright (C) 2000-2002 Paul Davis
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.
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.
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.
23 #include <libgnomecanvas/libgnomecanvas.h>
28 #include "ardour/dB.h"
31 #include "canvas-waveview.h"
32 #include "rgb_macros.h"
35 extern void c_stacktrace();
42 PROP_SOURCEFILE_LENGTH_FUNCTION,
48 PROP_SAMPLES_PER_UNIT,
49 PROP_AMPLITUDE_ABOVE_AXIS,
64 static void gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class);
66 static void gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview);
68 static void gnome_canvas_waveview_destroy (GtkObject *object);
70 static void gnome_canvas_waveview_set_property (GObject *object,
74 static void gnome_canvas_waveview_get_property (GObject *object,
79 static void gnome_canvas_waveview_update (GnomeCanvasItem *item,
84 static void gnome_canvas_waveview_bounds (GnomeCanvasItem *item,
90 static double gnome_canvas_waveview_point (GnomeCanvasItem *item,
95 GnomeCanvasItem **actual_item);
97 static void gnome_canvas_waveview_render (GnomeCanvasItem *item,
100 static void gnome_canvas_waveview_draw (GnomeCanvasItem *item,
101 GdkDrawable *drawable,
107 static void gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *,
110 static void gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *,
113 static guint32 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview,
117 static GnomeCanvasItemClass *parent_class;
120 gnome_canvas_waveview_get_type (void)
122 static GType waveview_type;
124 if (!waveview_type) {
125 static const GTypeInfo object_info = {
126 sizeof (GnomeCanvasWaveViewClass),
127 (GBaseInitFunc) NULL,
128 (GBaseFinalizeFunc) NULL,
129 (GClassInitFunc) gnome_canvas_waveview_class_init,
130 (GClassFinalizeFunc) NULL,
131 NULL, /* class_data */
132 sizeof (GnomeCanvasWaveView),
134 (GInstanceInitFunc) gnome_canvas_waveview_init,
135 NULL /* value_table */
138 waveview_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasWaveView",
142 return waveview_type;
146 gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class)
148 GObjectClass *gobject_class;
149 GtkObjectClass *object_class;
150 GnomeCanvasItemClass *item_class;
152 gobject_class = (GObjectClass *) class;
153 object_class = (GtkObjectClass *) class;
154 item_class = (GnomeCanvasItemClass *) class;
156 parent_class = g_type_class_peek_parent (class);
158 gobject_class->set_property = gnome_canvas_waveview_set_property;
159 gobject_class->get_property = gnome_canvas_waveview_get_property;
161 g_object_class_install_property
164 g_param_spec_pointer ("data_src", NULL, NULL,
165 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
167 g_object_class_install_property
170 g_param_spec_uint ("channel", NULL, NULL,
172 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
174 g_object_class_install_property
176 PROP_LENGTH_FUNCTION,
177 g_param_spec_pointer ("length_function", NULL, NULL,
178 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
180 g_object_class_install_property
182 PROP_SOURCEFILE_LENGTH_FUNCTION,
183 g_param_spec_pointer ("sourcefile_length_function", NULL, NULL,
184 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
186 g_object_class_install_property
189 g_param_spec_pointer ("peak_function", NULL, NULL,
190 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
192 g_object_class_install_property
195 g_param_spec_pointer ("gain_function", NULL, NULL,
196 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
198 g_object_class_install_property
201 g_param_spec_pointer ("gain_src", NULL, NULL,
202 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
204 g_object_class_install_property
207 g_param_spec_pointer ("cache", NULL, NULL,
208 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
210 g_object_class_install_property
213 g_param_spec_boolean ("cache_updater", NULL, NULL,
215 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
217 g_object_class_install_property
219 PROP_SAMPLES_PER_UNIT,
220 g_param_spec_double ("samples_per_unit", NULL, NULL,
221 0.0, G_MAXDOUBLE, 0.0,
222 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
224 g_object_class_install_property
226 PROP_AMPLITUDE_ABOVE_AXIS,
227 g_param_spec_double ("amplitude_above_axis", NULL, NULL,
228 0.0, G_MAXDOUBLE, 0.0,
229 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
231 g_object_class_install_property
234 g_param_spec_double ("x", NULL, NULL,
235 0.0, G_MAXDOUBLE, 0.0,
236 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
238 g_object_class_install_property
241 g_param_spec_double ("y", NULL, NULL,
242 0.0, G_MAXDOUBLE, 0.0,
243 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
245 g_object_class_install_property
248 g_param_spec_double ("height", NULL, NULL,
249 0.0, G_MAXDOUBLE, 0.0,
250 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
252 g_object_class_install_property
255 g_param_spec_uint ("wave_color", NULL, NULL,
257 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
259 g_object_class_install_property
262 g_param_spec_uint ("clip_color", NULL, NULL,
264 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
266 g_object_class_install_property
269 g_param_spec_uint ("zero_color", NULL, NULL,
271 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
273 g_object_class_install_property
276 g_param_spec_uint ("fill_color", NULL, NULL,
278 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
280 g_object_class_install_property
283 g_param_spec_boolean ("filled", NULL, NULL,
285 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
287 g_object_class_install_property
290 g_param_spec_boolean ("rectified", NULL, NULL,
292 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
294 g_object_class_install_property
297 g_param_spec_boolean ("zero_line", NULL, NULL,
299 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
301 g_object_class_install_property
304 g_param_spec_boolean ("logscaled", NULL, NULL,
306 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
308 g_object_class_install_property
311 g_param_spec_uint ("region_start", NULL, NULL,
313 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
315 object_class->destroy = gnome_canvas_waveview_destroy;
317 item_class->update = gnome_canvas_waveview_update;
318 item_class->bounds = gnome_canvas_waveview_bounds;
319 item_class->point = gnome_canvas_waveview_point;
320 item_class->render = gnome_canvas_waveview_render;
321 item_class->draw = gnome_canvas_waveview_draw;
324 GnomeCanvasWaveViewCache*
325 gnome_canvas_waveview_cache_new ()
327 GnomeCanvasWaveViewCache *c;
329 c = g_malloc (sizeof (GnomeCanvasWaveViewCache));
332 c->data = g_malloc (sizeof (GnomeCanvasWaveViewCacheEntry) * c->allocated);
341 gnome_canvas_waveview_cache_destroy (GnomeCanvasWaveViewCache* cache)
343 g_free (cache->data);
348 gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview)
353 waveview->cache_updater = FALSE;
354 waveview->data_src = NULL;
355 waveview->channel = 0;
356 waveview->peak_function = NULL;
357 waveview->length_function = NULL;
358 waveview->sourcefile_length_function = NULL;
359 waveview->gain_curve_function = NULL;
360 waveview->gain_src = NULL;
361 waveview->rectified = FALSE;
362 waveview->logscaled = FALSE;
363 waveview->filled = TRUE;
364 waveview->zero_line = FALSE;
365 waveview->region_start = 0;
366 waveview->samples_per_unit = 1.0;
367 waveview->amplitude_above_axis = 1.0;
368 waveview->height = 100.0;
369 waveview->screen_width = gdk_screen_width ();
370 waveview->reload_cache_in_render = FALSE;
372 waveview->wave_color = 0;
373 waveview->clip_color = 0;
374 waveview->zero_color = 0;
375 waveview->fill_color = 0;
379 gnome_canvas_waveview_destroy (GtkObject *object)
381 GnomeCanvasWaveView *waveview;
383 g_return_if_fail (object != NULL);
384 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
386 waveview = GNOME_CANVAS_WAVEVIEW (object);
388 if (GTK_OBJECT_CLASS (parent_class)->destroy)
389 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
392 #define DEBUG_CACHE 0
393 #undef CACHE_MEMMOVE_OPTIMIZATION
395 /** @return cache index of start_sample within the cache */
397 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_sample, gulong end_sample)
399 gulong required_cache_entries;
400 gulong rf1, rf2,rf3, required_frames;
401 gulong new_cache_start, new_cache_end;
407 GnomeCanvasWaveViewCache *cache;
409 #ifdef CACHE_MEMMOVE_OPTIMIZATION
410 gulong present_frames;
411 gulong present_entries;
414 cache = waveview->cache;
416 start_sample = start_sample + waveview->region_start;
417 end_sample = end_sample + waveview->region_start;
419 // printf("waveview->region_start == %lu\n",waveview->region_start);
421 printf ("\n\n=> 0x%x cache @ 0x%x range: %lu - %lu request: %lu - %lu (%lu frames)\n",
423 cache->start, cache->end,
424 start_sample, end_sample, end_sample - start_sample);
427 if (cache->start <= start_sample && cache->end >= end_sample) {
429 // printf ("0x%x: cache hit for %lu-%lu (cache holds: %lu-%lu\n",
430 // waveview, start_sample, end_sample, cache->start, cache->end);
435 /* make sure the cache is at least twice as wide as the screen width, and put the start sample
436 in the middle, ensuring that we cover the end_sample.
439 /* Note the assumption that we have a 1:1 units:pixel ratio for the canvas. Its everywhere ... */
441 half_width = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit)/2.0 + 0.5);
443 if (start_sample < half_width) {
446 new_cache_start = start_sample - half_width;
449 /* figure out how many frames we want */
451 rf1 = end_sample - start_sample + 1;
452 rf2 = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit * 2.0f));
453 required_frames = MAX(rf1,rf2);
455 /* but make sure it doesn't extend beyond the end of the source material */
457 rf3 = (gulong) (waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit)) + 1;
458 if (rf3 < new_cache_start) {
461 rf3 -= new_cache_start;
465 fprintf (stderr, "AVAILABLE FRAMES = %lu of %lu, start = %lu, sstart = %lu, cstart = %lu\n",
466 rf3, waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit),
467 waveview->region_start, start_sample, new_cache_start);
470 required_frames = MIN(required_frames,rf3);
472 new_cache_end = new_cache_start + required_frames - 1;
474 required_cache_entries = (gulong) floor (required_frames / waveview->samples_per_unit );
477 fprintf (stderr, "new cache = %lu - %lu\n", new_cache_start, new_cache_end);
478 fprintf(stderr,"required_cach_entries = %lu, samples_per_unit = %f req frames = %lu\n",
479 required_cache_entries,waveview->samples_per_unit, required_frames);
482 if (required_cache_entries > cache->allocated) {
483 cache->data = g_realloc (cache->data, sizeof (GnomeCanvasWaveViewCacheEntry) * required_cache_entries);
484 cache->allocated = required_cache_entries;
489 ostart = new_cache_start;
491 #ifdef CACHE_MEMMOVE_OPTIMIZATION
493 /* data is not entirely in the cache, so go fetch it, making sure to fill the cache */
495 /* some of the required cache entries are in the cache, but in the wrong
496 locations. use memmove to fix this.
499 if (cache->start < new_cache_start && new_cache_start < cache->end) {
501 /* case one: the common area is at the end of the existing cache. move it
502 to the beginning of the cache, and set up to refill whatever remains.
505 wv->cache_start wv->cache_end
506 |-------------------------------------------------------| cache
507 |--------------------------------| requested
508 <------------------->
510 new_cache_start new_cache_end
514 present_frames = cache->end - new_cache_start;
515 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
518 fprintf (stderr, "existing material at end of current cache, move to start of new cache\n"
519 "\tcopy from %lu to start\n", cache->data_size - present_entries);
522 memmove (&cache->data[0],
523 &cache->data[cache->data_size - present_entries],
524 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
527 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
528 present_frames, required_frames, present_entries, new_cache_start + present_entries,
529 cache->data + present_entries);
532 copied = present_entries;
533 offset = present_entries;
534 new_cache_start += present_frames;
535 required_frames -= present_frames;
537 } else if (new_cache_end > cache->start && new_cache_end < cache->end) {
539 /* case two: the common area lives at the beginning of the existing cache.
541 wv->cache_start wv->cache_end
542 |-----------------------------------------------------|
543 |--------------------------------|
547 new_cache_start new_cache_end
550 present_frames = new_cache_end - cache->start;
551 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
553 memmove (&cache->data[cache->data_size - present_entries],
555 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
558 fprintf (stderr, "existing material at start of current cache, move to start of end cache\n");
562 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
563 present_entries, required_frames, present_entries, new_cache_start + present_entries,
564 cache->data + present_entries);
567 copied = present_entries;
569 required_frames -= present_frames;
582 #endif /* CACHE_MEMMOVE_OPTIMIZATION */
584 // fprintf(stderr,"length == %lu\n",waveview->length_function (waveview->data_src));
585 // required_frames = MIN (waveview->length_function (waveview->data_src) - new_cache_start, required_frames);
587 npeaks = (gulong) floor (required_frames / waveview->samples_per_unit);
588 required_frames = npeaks * waveview->samples_per_unit;
593 printf ("requesting %lu/%f to cover %lu-%lu at %f spu (request was %lu-%lu) into cache + %lu\n",
594 required_frames, required_frames/waveview->samples_per_unit, new_cache_start, new_cache_end,
595 waveview->samples_per_unit, start_sample, end_sample, offset);
599 // printf ("cache holds %lu entries, requesting %lu to cover %lu-%lu (request was %lu-%lu)\n",
600 // cache->data_size, npeaks, new_cache_start, new_cache_end,
601 // start_sample, end_sample);
604 if (required_frames) {
605 waveview->peak_function (waveview->data_src, npeaks, new_cache_start, required_frames, cache->data + offset, waveview->channel,waveview->samples_per_unit);
607 /* take into account any copied peaks */
614 if (npeaks < cache->allocated) {
616 fprintf (stderr, "zero fill cache for %lu at %lu\n", cache->allocated - npeaks, npeaks);
618 memset (&cache->data[npeaks], 0, sizeof (GnomeCanvasWaveViewCacheEntry) * (cache->allocated - npeaks));
619 cache->data_size = npeaks;
621 cache->data_size = cache->allocated;
624 if (waveview->gain_curve_function) {
627 gain = (float*) malloc (sizeof (float) * cache->data_size);
629 waveview->gain_curve_function (waveview->gain_src, new_cache_start, new_cache_end, gain, cache->data_size);
631 for (n = 0; n < cache->data_size; ++n) {
632 cache->data[n].min *= gain[n];
633 cache->data[n].max *= gain[n];
640 /* do optional log scaling. this implementation is not particularly efficient */
642 if (waveview->logscaled) {
644 GnomeCanvasWaveViewCacheEntry* buf = cache->data;
646 for (n = 0; n < cache->data_size; ++n) {
648 if (buf[n].max > 0.0f) {
649 buf[n].max = alt_log_meter(fast_coefficient_to_dB(buf[n].max));
650 } else if (buf[n].max < 0.0f) {
651 buf[n].max = -alt_log_meter(fast_coefficient_to_dB(-buf[n].max));
654 if (buf[n].min > 0.0f) {
655 buf[n].min = alt_log_meter(fast_coefficient_to_dB(buf[n].min));
656 } else if (buf[n].min < 0.0f) {
657 buf[n].min = -alt_log_meter(fast_coefficient_to_dB(-buf[n].min));
662 cache->start = ostart;
663 cache->end = new_cache_end;
667 fprintf (stderr, "return cache index = %d\n",
668 (guint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5));
670 return (guint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5);
675 gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *waveview, void *data_src)
678 if (waveview->cache_updater) {
679 if (waveview->data_src == data_src) {
680 waveview->reload_cache_in_render = TRUE;
684 waveview->cache->start = 0;
685 waveview->cache->end = 0;
688 waveview->data_src = data_src;
692 gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *waveview, guint32 chan)
694 if (waveview->channel == chan) {
698 waveview->channel = chan;
702 gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
705 double x1, x2, y1, y2;
708 int Ix1, Ix2, Iy1, Iy2;
711 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
718 gnome_canvas_item_i2w_affine (item, i2w);
719 art_affine_point (&w1, &i1, i2w);
720 art_affine_point (&w2, &i2, i2w);
722 Ix1 = (int) rint(w1.x);
723 Ix2 = (int) rint(w2.x);
724 Iy1 = (int) rint(w1.y);
725 Iy2 = (int) rint(w2.y);
727 gnome_canvas_update_bbox (item, Ix1, Iy1, Ix2, Iy2);
735 gnome_canvas_waveview_set_property (GObject *object,
743 GnomeCanvasItem *item;
744 GnomeCanvasWaveView *waveview;
746 int calc_bounds = FALSE;
748 g_return_if_fail (object != NULL);
749 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
751 item = GNOME_CANVAS_ITEM (object);
752 waveview = GNOME_CANVAS_WAVEVIEW (object);
756 gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
761 gnome_canvas_waveview_set_channel (waveview, g_value_get_uint(value));
765 case PROP_LENGTH_FUNCTION:
766 waveview->length_function = (gulong (*)(void*)) g_value_get_pointer(value);
770 case PROP_SOURCEFILE_LENGTH_FUNCTION:
771 waveview->sourcefile_length_function = (gulong (*)(void*,double)) g_value_get_pointer(value);
775 case PROP_PEAK_FUNCTION:
776 waveview->peak_function =
777 (void (*)(void*,gulong,gulong,gulong,gpointer,guint32,double))
778 g_value_get_pointer(value);
782 case PROP_GAIN_FUNCTION:
783 waveview->gain_curve_function =
784 (void (*)(void *arg, double start, double end, float* vector, gint64 veclen))
785 g_value_get_pointer(value);
790 waveview->gain_src = g_value_get_pointer(value);
791 if (waveview->cache_updater) {
792 waveview->cache->start = 0;
793 waveview->cache->end = 0;
800 waveview->cache = g_value_get_pointer(value);
805 case PROP_CACHE_UPDATER:
806 waveview->cache_updater = g_value_get_boolean(value);
810 case PROP_SAMPLES_PER_UNIT:
811 if ((waveview->samples_per_unit = g_value_get_double(value)) < 1.0) {
812 waveview->samples_per_unit = 1.0;
814 if (waveview->cache_updater) {
815 waveview->cache->start = 0;
816 waveview->cache->end = 0;
822 case PROP_AMPLITUDE_ABOVE_AXIS:
823 waveview->amplitude_above_axis = g_value_get_double(value);
828 if (waveview->x != g_value_get_double (value)) {
829 waveview->x = g_value_get_double (value);
835 if (waveview->y != g_value_get_double (value)) {
836 waveview->y = g_value_get_double (value);
842 if (waveview->height != fabs (g_value_get_double (value))) {
843 waveview->height = fabs (g_value_get_double (value));
848 case PROP_WAVE_COLOR:
849 if (waveview->wave_color != g_value_get_uint(value)) {
850 waveview->wave_color = g_value_get_uint(value);
855 case PROP_CLIP_COLOR:
856 if (waveview->clip_color != g_value_get_uint(value)) {
857 waveview->clip_color = g_value_get_uint(value);
862 case PROP_ZERO_COLOR:
863 if (waveview->zero_color != g_value_get_uint(value)) {
864 waveview->zero_color = g_value_get_uint(value);
869 case PROP_FILL_COLOR:
870 if (waveview->fill_color != g_value_get_uint(value)) {
871 waveview->fill_color = g_value_get_uint(value);
877 if (waveview->filled != g_value_get_boolean(value)) {
878 waveview->filled = g_value_get_boolean(value);
884 if (waveview->rectified != g_value_get_boolean(value)) {
885 waveview->rectified = g_value_get_boolean(value);
891 if (waveview->zero_line != g_value_get_boolean(value)) {
892 waveview->zero_line = g_value_get_boolean(value);
898 if (waveview->logscaled != g_value_get_boolean(value)) {
899 waveview->logscaled = g_value_get_boolean(value);
900 if (waveview->cache_updater) {
901 waveview->cache->start = 0;
902 waveview->cache->end = 0;
908 case PROP_REGION_START:
909 waveview->region_start = g_value_get_uint(value);
920 gnome_canvas_waveview_reset_bounds (item);
924 gnome_canvas_item_request_update (item);
930 gnome_canvas_waveview_get_property (
938 g_return_if_fail (object != NULL);
939 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
941 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
945 g_value_set_pointer(value, waveview->data_src);
949 g_value_set_uint(value, waveview->channel);
952 case PROP_LENGTH_FUNCTION:
953 g_value_set_pointer(value, (void*) waveview->length_function);
956 case PROP_SOURCEFILE_LENGTH_FUNCTION:
957 g_value_set_pointer(value, (void*) waveview->sourcefile_length_function);
960 case PROP_PEAK_FUNCTION:
961 g_value_set_pointer(value, (void*) waveview->peak_function);
964 case PROP_GAIN_FUNCTION:
965 g_value_set_pointer(value, (void*) waveview->gain_curve_function);
969 g_value_set_pointer(value, waveview->gain_src);
973 g_value_set_pointer(value, waveview->cache);
976 case PROP_CACHE_UPDATER:
977 g_value_set_boolean(value, waveview->cache_updater);
980 case PROP_SAMPLES_PER_UNIT:
981 g_value_set_double(value, waveview->samples_per_unit);
984 case PROP_AMPLITUDE_ABOVE_AXIS:
985 g_value_set_double(value, waveview->amplitude_above_axis);
989 g_value_set_double (value, waveview->x);
993 g_value_set_double (value, waveview->y);
997 g_value_set_double (value, waveview->height);
1000 case PROP_WAVE_COLOR:
1001 g_value_set_uint (value, waveview->wave_color);
1004 case PROP_CLIP_COLOR:
1005 g_value_set_uint (value, waveview->clip_color);
1008 case PROP_ZERO_COLOR:
1009 g_value_set_uint (value, waveview->zero_color);
1012 case PROP_FILL_COLOR:
1013 g_value_set_uint (value, waveview->fill_color);
1017 g_value_set_boolean (value, waveview->filled);
1020 case PROP_RECTIFIED:
1021 g_value_set_boolean (value, waveview->rectified);
1024 case PROP_ZERO_LINE:
1025 g_value_set_boolean (value, waveview->zero_line);
1028 case PROP_LOGSCALED:
1029 g_value_set_boolean (value, waveview->logscaled);
1032 case PROP_REGION_START:
1033 g_value_set_uint (value, waveview->region_start);
1037 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1043 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1045 GnomeCanvasWaveView *waveview;
1048 waveview = GNOME_CANVAS_WAVEVIEW (item);
1050 // check_cache (waveview, "start of update");
1052 if (parent_class->update)
1053 (* parent_class->update) (item, affine, clip_path, flags);
1055 gnome_canvas_waveview_reset_bounds (item);
1057 /* get the canvas coordinates of the view. Do NOT use affines
1058 for this, because they do not round to the integer units used
1059 by the canvas, resulting in subtle pixel-level errors later.
1065 gnome_canvas_item_i2w (item, &x, &y);
1066 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
1068 waveview->samples = waveview->length_function (waveview->data_src);
1070 x = waveview->x + (waveview->samples / waveview->samples_per_unit);
1071 y = waveview->y + waveview->height;
1073 gnome_canvas_item_i2w (item, &x, &y);
1074 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
1076 /* cache the half-height and the end point in canvas units */
1078 waveview->half_height = waveview->height / 2.0;
1080 /* parse the color */
1082 UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
1084 UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
1086 UINT_TO_RGBA (waveview->fill_color, &waveview->fill_r, &waveview->fill_g, &waveview->fill_b,
1089 // check_cache (waveview, "end of update");
1093 gnome_canvas_waveview_render (GnomeCanvasItem *item,
1094 GnomeCanvasBuf *buf)
1096 GnomeCanvasWaveView *waveview;
1098 int clip_length = 0;
1105 waveview = GNOME_CANVAS_WAVEVIEW (item);
1107 // check_cache (waveview, "start of render");
1109 if (parent_class->render) {
1110 (*parent_class->render) (item, buf);
1114 gnome_canvas_buf_ensure_buf (buf);
1118 /* a "unit" means a pixel */
1120 /* begin: render start x (units) */
1121 int const begin = MAX (waveview->bbox_ulx, buf->rect.x0);
1123 /* zbegin: start x for zero line (units) */
1124 int const zbegin = (begin == waveview->bbox_ulx) ? (begin + 1) : begin;
1126 /* end: render end x (units) */
1127 int const end = (waveview->bbox_lrx >= 0) ? MIN (waveview->bbox_lrx,buf->rect.x1) : buf->rect.x1;
1129 /* zend: end x for zero-line (units) */
1130 int const zend = (end == waveview->bbox_lrx) ? (end - 1) : end;
1140 s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit);
1142 // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
1144 if (end == waveview->bbox_lrx) {
1145 /* This avoids minor rounding errors when we have the
1146 entire region visible.
1148 s2 = waveview->samples;
1150 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
1154 printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
1155 " b/e %d..%d s= %lu..%lu @ %f\n",
1166 waveview->samples_per_unit);
1169 /* now ensure that the cache is full and properly
1173 // check_cache (waveview, "pre-ensure");
1175 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1176 waveview->cache->start = 0;
1177 waveview->cache->end = 0;
1178 waveview->reload_cache_in_render = FALSE;
1181 // check_cache (waveview, "post-ensure");
1183 /* don't rectify at single-sample zoom */
1184 if (waveview->rectified && waveview->samples_per_unit > 1) {
1191 clip_length = MIN(5,(waveview->height/4));
1194 Now draw each line, clipping it appropriately. The clipping
1195 is done by the macros PAINT_FOO().
1198 half_height = waveview->half_height;
1200 /* this makes it slightly easier to comprehend whats going on */
1201 #define origin half_height
1203 if (waveview->filled && !rectify) {
1208 int next_pymin, next_pymax;
1210 int next_clip_max = 0;
1211 int next_clip_min = 0;
1213 if (s1 < waveview->samples_per_unit) {
1214 /* we haven't got a prev vars to compare with, so outline the whole line here */
1215 prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1216 prev_pymin = prev_pymax;
1219 s1 -= waveview->samples_per_unit;
1222 if(end == waveview->bbox_lrx) {
1223 /* we don't have the NEXT vars for the last sample */
1224 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1225 last_pymin = last_pymax;
1228 s2 += waveview->samples_per_unit;
1231 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1234 * Compute the variables outside the rendering rect
1236 if(prev_pymax != prev_pymin) {
1238 prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1239 prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1242 if(last_pymax != last_pymin) {
1243 /* take the index of one sample right of what we render */
1244 guint index = cache_index + (end - begin);
1246 if (index >= waveview->cache->data_size) {
1248 /* the data we want is off the end of the cache, which must mean its beyond
1249 the end of the region's source; hence the peak values are 0 */
1250 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1251 last_pymin = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1255 last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1256 last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1263 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1265 max = waveview->cache->data[cache_index].max;
1266 min = waveview->cache->data[cache_index].min;
1281 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1282 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1287 for(x = begin; x < end; ++x) {
1288 int clip_max = next_clip_max;
1289 int clip_min = next_clip_min;
1290 int fill_max, fill_min;
1297 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1298 next_pymax = last_pymax;
1299 next_pymin = last_pymin;
1304 if (cache_index < waveview->cache->data_size) {
1305 max = waveview->cache->data[cache_index].max;
1306 min = waveview->cache->data[cache_index].min;
1327 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1328 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1332 if (pymax == pymin) {
1333 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1335 if((prev_pymax < pymax && next_pymax < pymax) ||
1336 (prev_pymax == pymax && next_pymax == pymax)) {
1337 fill_max = pymax + 1;
1338 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1341 fill_max = MAX(prev_pymax, next_pymax);
1342 if(pymax == fill_max) {
1343 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1347 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1351 if((prev_pymin > pymin && next_pymin > pymin) ||
1352 (prev_pymin == pymin && next_pymin == pymin)) {
1353 fill_min = pymin - 1;
1354 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1357 fill_min = MIN(prev_pymin, next_pymin);
1358 if(pymin == fill_min) {
1359 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1362 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin);
1366 if(fill_max < fill_min) {
1367 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min);
1369 else if(fill_max == fill_min) {
1370 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1375 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1379 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1386 } else if (waveview->filled && rectify) {
1388 int prev_pymax = -1;
1389 int last_pymax = -1;
1392 int next_clip_max = 0;
1393 int next_clip_min = 0;
1395 // for rectified, this stays constant throughout the loop
1396 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1398 if(s1 < waveview->samples_per_unit) {
1399 /* we haven't got a prev vars to compare with, so outline the whole line here */
1403 s1 -= waveview->samples_per_unit;
1406 if(end == waveview->bbox_lrx) {
1407 /* we don't have the NEXT vars for the last sample */
1411 s2 += waveview->samples_per_unit;
1414 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1417 * Compute the variables outside the rendering rect
1419 if(prev_pymax < 0) {
1420 max = MIN(waveview->cache->data[cache_index].max, 1.0);
1421 min = MAX(waveview->cache->data[cache_index].min, -1.0);
1423 if (fabs (min) > fabs (max)) {
1427 prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1430 if(last_pymax < 0) {
1431 /* take the index of one sample right of what we render */
1432 int index = cache_index + (end - begin);
1434 max = MIN(waveview->cache->data[index].max, 1.0);
1435 min = MAX(waveview->cache->data[index].min, -1.0);
1437 if (fabs (min) > fabs (max)) {
1441 last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1445 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1447 max = waveview->cache->data[cache_index].max;
1448 min = waveview->cache->data[cache_index].min;
1460 if (fabs (min) > fabs (max)) {
1464 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1469 for(x = begin; x < end; ++x) {
1470 int clip_max = next_clip_max;
1471 int clip_min = next_clip_min;
1478 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1479 next_pymax = last_pymax;
1484 max = waveview->cache->data[cache_index].max;
1485 min = waveview->cache->data[cache_index].min;
1497 if (fabs (min) > fabs (max)) {
1501 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1505 if (pymax == pymin) {
1506 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1508 if((prev_pymax < pymax && next_pymax < pymax) ||
1509 (prev_pymax == pymax && next_pymax == pymax)) {
1510 fill_max = pymax + 1;
1511 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1514 fill_max = MAX(prev_pymax, next_pymax);
1515 if(pymax == fill_max) {
1516 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1520 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1524 if(fill_max < pymin) {
1525 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
1527 else if(fill_max == pymin) {
1528 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
1533 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1537 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1544 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1546 for (x = begin; x < end; x++) {
1549 int clip_max, clip_min;
1554 max = waveview->cache->data[cache_index].max;
1555 min = waveview->cache->data[cache_index].min;
1569 if (fabs (min) > fabs (max)) {
1573 max = max * waveview->height;
1575 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1576 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1580 max = max * half_height;
1581 min = min * half_height;
1583 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1584 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1587 /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1588 or, if samples_per_unit == 1, then a dot at each location.
1591 if (pymax == pymin) {
1592 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1594 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1597 /* show clipped waveforms with small red lines */
1600 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1604 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1607 /* presto, we're done */
1613 if (!waveview->rectified && waveview->zero_line) {
1615 //PAINT_HORIZA(buf, waveview->zero_r, waveview->zero_g, waveview->zero_b, waveview->zero_a, begin, endi-1, origin );
1617 unsigned char zero_r, zero_g, zero_b, zero_a;
1618 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a );
1619 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1620 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, zend, zeroline_y);
1627 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1628 GdkDrawable *drawable,
1630 int width, int height)
1632 GnomeCanvasWaveView *waveview;
1646 waveview = GNOME_CANVAS_WAVEVIEW (item);
1648 /* compute intersection of Drawable area and waveview,
1649 in canvas coordinate space
1652 if (x > waveview->bbox_ulx) {
1655 ulx = waveview->bbox_ulx;
1658 if (y > waveview->bbox_uly) {
1661 uly = waveview->bbox_uly;
1664 if (x + width > waveview->bbox_lrx) {
1665 lrx = waveview->bbox_lrx;
1670 if (y + height > waveview->bbox_lry) {
1671 lry = waveview->bbox_lry;
1676 /* figure out which samples we need for the resulting intersection */
1678 s1 = floor ((ulx - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1680 if (lrx == waveview->bbox_lrx) {
1681 /* This avoids minor rounding errors when we have the
1682 entire region visible.
1684 s2 = waveview->samples;
1686 s2 = s1 + floor ((lrx - ulx) * waveview->samples_per_unit);
1689 /* translate back to buffer coordinate space */
1696 /* don't rectify at single-sample zoom */
1697 if(waveview->rectified && waveview->samples_per_unit > 1.0) {
1703 clip_length = MIN(5,(waveview->height/4));
1705 cr = gdk_cairo_create (drawable);
1706 cairo_set_line_width (cr, 0.5);
1708 origin = waveview->bbox_uly - y + waveview->half_height;
1710 cairo_rectangle (cr, ulx, uly, lrx - ulx, lry - uly);
1713 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1714 waveview->cache->start = 0;
1715 waveview->cache->end = 0;
1716 waveview->reload_cache_in_render = FALSE;
1719 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1722 printf ("%p r (%d,%d)(%d,%d)[%d x %d] bbox (%d,%d)(%d,%d)[%d x %d]"
1723 " draw (%.1f,%.1f)(%.1f,%.1f)[%.1f x %.1f] s= %lu..%lu\n",
1734 waveview->bbox_lrx - waveview->bbox_ulx,
1735 waveview->bbox_lry - waveview->bbox_uly,
1743 /* draw the top half */
1745 for (xoff = ulx; xoff < lrx; xoff++) {
1748 max = waveview->cache->data[cache_index].max;
1749 min = waveview->cache->data[cache_index].min;
1760 if (fabs (min) > fabs (max)) {
1765 yoff = origin - (waveview->half_height * max) + 0.5;
1769 cairo_move_to (cr, xoff+0.5, yoff);
1771 cairo_line_to (cr, xoff+0.5, yoff);
1777 /* from the final top point, move out of the clip zone */
1779 cairo_line_to (cr, xoff + 10, yoff);
1781 /* now draw the bottom half */
1783 for (--xoff, --cache_index; xoff >= ulx; --xoff) {
1786 min = waveview->cache->data[cache_index].min;
1792 yoff = origin - (waveview->half_height * min) + 0.5;
1794 cairo_line_to (cr, xoff+0.5, yoff);
1798 /* from the final lower point, move out of the clip zone */
1800 cairo_line_to (cr, xoff - 10, yoff);
1802 /* close path to fill */
1804 cairo_close_path (cr);
1806 /* fill and stroke */
1808 cairo_set_source_rgba (cr,
1809 (waveview->fill_r/255.0),
1810 (waveview->fill_g/255.0),
1811 (waveview->fill_b/255.0),
1812 (waveview->fill_a/255.0));
1813 cairo_fill_preserve (cr);
1814 cairo_set_source_rgba (cr,
1815 (waveview->wave_r/255.0),
1816 (waveview->wave_g/255.0),
1817 (waveview->wave_b/255.0),
1818 (waveview->wave_a/255.0));
1825 if (clip_max || clip_min) {
1826 cairo_set_source_rgba (cr, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a);
1830 cairo_move_to (cr, xoff, yoff1);
1831 cairo_line_to (cr, xoff, yoff1 + clip_length);
1836 cairo_move_to (cr, xoff, yoff2);
1837 cairo_line_to (cr, xoff, yoff2 - clip_length);
1844 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1846 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1851 *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1852 *y2 = *y1 + waveview->height;
1856 gnome_canvas_item_i2w (item, &x, &y);
1857 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1860 gnome_canvas_item_i2w (item, &x, &y);
1861 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1862 printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1868 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1877 /* XXX for now, point is never inside the wave
1878 GnomeCanvasWaveView *waveview;
1879 double x1, y1, x2, y2;
1886 waveview = GNOME_CANVAS_WAVEVIEW (item);
1888 *actual_item = item;
1890 /* Find the bounds for the rectangle plus its outline width */
1892 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1894 /* Is point inside rectangle */
1896 if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1900 /* Point is outside rectangle */
1916 return sqrt (dx * dx + dy * dy);