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(coefficient_to_dB(buf[n].max));
650 } else if (buf[n].max < 0.0f) {
651 buf[n].max = -alt_log_meter(coefficient_to_dB(-buf[n].max));
654 if (buf[n].min > 0.0f) {
655 buf[n].min = alt_log_meter(coefficient_to_dB(buf[n].min));
656 } else if (buf[n].min < 0.0f) {
657 buf[n].min = -alt_log_meter(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,
741 GnomeCanvasItem *item;
742 GnomeCanvasWaveView *waveview;
744 int calc_bounds = FALSE;
746 g_return_if_fail (object != NULL);
747 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
749 item = GNOME_CANVAS_ITEM (object);
750 waveview = GNOME_CANVAS_WAVEVIEW (object);
754 gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
759 gnome_canvas_waveview_set_channel (waveview, g_value_get_uint(value));
763 case PROP_LENGTH_FUNCTION:
764 waveview->length_function = g_value_get_pointer(value);
768 case PROP_SOURCEFILE_LENGTH_FUNCTION:
769 waveview->sourcefile_length_function = g_value_get_pointer(value);
773 case PROP_PEAK_FUNCTION:
774 waveview->peak_function = g_value_get_pointer(value);
778 case PROP_GAIN_FUNCTION:
779 waveview->gain_curve_function = g_value_get_pointer(value);
784 waveview->gain_src = g_value_get_pointer(value);
785 if (waveview->cache_updater) {
786 waveview->cache->start = 0;
787 waveview->cache->end = 0;
794 waveview->cache = g_value_get_pointer(value);
799 case PROP_CACHE_UPDATER:
800 waveview->cache_updater = g_value_get_boolean(value);
804 case PROP_SAMPLES_PER_UNIT:
805 if ((waveview->samples_per_unit = g_value_get_double(value)) < 1.0) {
806 waveview->samples_per_unit = 1.0;
808 if (waveview->cache_updater) {
809 waveview->cache->start = 0;
810 waveview->cache->end = 0;
816 case PROP_AMPLITUDE_ABOVE_AXIS:
817 waveview->amplitude_above_axis = g_value_get_double(value);
822 if (waveview->x != g_value_get_double (value)) {
823 waveview->x = g_value_get_double (value);
829 if (waveview->y != g_value_get_double (value)) {
830 waveview->y = g_value_get_double (value);
836 if (waveview->height != fabs (g_value_get_double (value))) {
837 waveview->height = fabs (g_value_get_double (value));
842 case PROP_WAVE_COLOR:
843 if (waveview->wave_color != g_value_get_uint(value)) {
844 waveview->wave_color = g_value_get_uint(value);
849 case PROP_CLIP_COLOR:
850 if (waveview->clip_color != g_value_get_uint(value)) {
851 waveview->clip_color = g_value_get_uint(value);
856 case PROP_ZERO_COLOR:
857 if (waveview->zero_color != g_value_get_uint(value)) {
858 waveview->zero_color = g_value_get_uint(value);
863 case PROP_FILL_COLOR:
864 if (waveview->fill_color != g_value_get_uint(value)) {
865 waveview->fill_color = g_value_get_uint(value);
871 if (waveview->filled != g_value_get_boolean(value)) {
872 waveview->filled = g_value_get_boolean(value);
878 if (waveview->rectified != g_value_get_boolean(value)) {
879 waveview->rectified = g_value_get_boolean(value);
885 if (waveview->zero_line != g_value_get_boolean(value)) {
886 waveview->zero_line = g_value_get_boolean(value);
892 if (waveview->logscaled != g_value_get_boolean(value)) {
893 waveview->logscaled = g_value_get_boolean(value);
894 if (waveview->cache_updater) {
895 waveview->cache->start = 0;
896 waveview->cache->end = 0;
902 case PROP_REGION_START:
903 waveview->region_start = g_value_get_uint(value);
914 gnome_canvas_waveview_reset_bounds (item);
918 gnome_canvas_item_request_update (item);
924 gnome_canvas_waveview_get_property (
932 g_return_if_fail (object != NULL);
933 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
935 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
939 g_value_set_pointer(value, waveview->data_src);
943 g_value_set_uint(value, waveview->channel);
946 case PROP_LENGTH_FUNCTION:
947 g_value_set_pointer(value, waveview->length_function);
950 case PROP_SOURCEFILE_LENGTH_FUNCTION:
951 g_value_set_pointer(value, waveview->sourcefile_length_function);
954 case PROP_PEAK_FUNCTION:
955 g_value_set_pointer(value, waveview->peak_function);
958 case PROP_GAIN_FUNCTION:
959 g_value_set_pointer(value, waveview->gain_curve_function);
963 g_value_set_pointer(value, waveview->gain_src);
967 g_value_set_pointer(value, waveview->cache);
970 case PROP_CACHE_UPDATER:
971 g_value_set_boolean(value, waveview->cache_updater);
974 case PROP_SAMPLES_PER_UNIT:
975 g_value_set_double(value, waveview->samples_per_unit);
978 case PROP_AMPLITUDE_ABOVE_AXIS:
979 g_value_set_double(value, waveview->amplitude_above_axis);
983 g_value_set_double (value, waveview->x);
987 g_value_set_double (value, waveview->y);
991 g_value_set_double (value, waveview->height);
994 case PROP_WAVE_COLOR:
995 g_value_set_uint (value, waveview->wave_color);
998 case PROP_CLIP_COLOR:
999 g_value_set_uint (value, waveview->clip_color);
1002 case PROP_ZERO_COLOR:
1003 g_value_set_uint (value, waveview->zero_color);
1006 case PROP_FILL_COLOR:
1007 g_value_set_uint (value, waveview->fill_color);
1011 g_value_set_boolean (value, waveview->filled);
1014 case PROP_RECTIFIED:
1015 g_value_set_boolean (value, waveview->rectified);
1018 case PROP_ZERO_LINE:
1019 g_value_set_boolean (value, waveview->zero_line);
1022 case PROP_LOGSCALED:
1023 g_value_set_boolean (value, waveview->logscaled);
1026 case PROP_REGION_START:
1027 g_value_set_uint (value, waveview->region_start);
1031 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1037 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1039 GnomeCanvasWaveView *waveview;
1042 waveview = GNOME_CANVAS_WAVEVIEW (item);
1044 // check_cache (waveview, "start of update");
1046 if (parent_class->update)
1047 (* parent_class->update) (item, affine, clip_path, flags);
1049 gnome_canvas_waveview_reset_bounds (item);
1051 /* get the canvas coordinates of the view. Do NOT use affines
1052 for this, because they do not round to the integer units used
1053 by the canvas, resulting in subtle pixel-level errors later.
1059 gnome_canvas_item_i2w (item, &x, &y);
1060 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
1062 waveview->samples = waveview->length_function (waveview->data_src);
1064 x = waveview->x + (waveview->samples / waveview->samples_per_unit);
1065 y = waveview->y + waveview->height;
1067 gnome_canvas_item_i2w (item, &x, &y);
1068 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
1070 /* cache the half-height and the end point in canvas units */
1072 waveview->half_height = waveview->height / 2.0;
1074 /* parse the color */
1076 UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
1078 UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
1080 UINT_TO_RGBA (waveview->fill_color, &waveview->fill_r, &waveview->fill_g, &waveview->fill_b,
1083 // check_cache (waveview, "end of update");
1087 gnome_canvas_waveview_render (GnomeCanvasItem *item,
1088 GnomeCanvasBuf *buf)
1090 GnomeCanvasWaveView *waveview;
1092 int clip_length = 0;
1099 waveview = GNOME_CANVAS_WAVEVIEW (item);
1101 // check_cache (waveview, "start of render");
1103 if (parent_class->render) {
1104 (*parent_class->render) (item, buf);
1108 gnome_canvas_buf_ensure_buf (buf);
1112 /* a "unit" means a pixel */
1114 /* begin: render start x (units) */
1115 int const begin = MAX (waveview->bbox_ulx, buf->rect.x0);
1117 /* zbegin: start x for zero line (units) */
1118 int const zbegin = (begin == waveview->bbox_ulx) ? (begin + 1) : begin;
1120 /* end: render end x (units) */
1121 int const end = (waveview->bbox_lrx >= 0) ? MIN (waveview->bbox_lrx,buf->rect.x1) : buf->rect.x1;
1123 /* zend: end x for zero-line (units) */
1124 int const zend = (end == waveview->bbox_lrx) ? (end - 1) : end;
1134 s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit);
1136 // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
1138 if (end == waveview->bbox_lrx) {
1139 /* This avoids minor rounding errors when we have the
1140 entire region visible.
1142 s2 = waveview->samples;
1144 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
1148 printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
1149 " b/e %d..%d s= %lu..%lu @ %f\n",
1160 waveview->samples_per_unit);
1163 /* now ensure that the cache is full and properly
1167 // check_cache (waveview, "pre-ensure");
1169 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1170 waveview->cache->start = 0;
1171 waveview->cache->end = 0;
1172 waveview->reload_cache_in_render = FALSE;
1175 // check_cache (waveview, "post-ensure");
1177 /* don't rectify at single-sample zoom */
1178 if (waveview->rectified && waveview->samples_per_unit > 1) {
1185 clip_length = MIN(5,(waveview->height/4));
1188 Now draw each line, clipping it appropriately. The clipping
1189 is done by the macros PAINT_FOO().
1192 half_height = waveview->half_height;
1194 /* this makes it slightly easier to comprehend whats going on */
1195 #define origin half_height
1197 if (waveview->filled && !rectify) {
1202 int next_pymin, next_pymax;
1204 int next_clip_max = 0;
1205 int next_clip_min = 0;
1207 if (s1 < waveview->samples_per_unit) {
1208 /* we haven't got a prev vars to compare with, so outline the whole line here */
1209 prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1210 prev_pymin = prev_pymax;
1213 s1 -= waveview->samples_per_unit;
1216 if(end == waveview->bbox_lrx) {
1217 /* we don't have the NEXT vars for the last sample */
1218 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1219 last_pymin = last_pymax;
1222 s2 += waveview->samples_per_unit;
1225 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1228 * Compute the variables outside the rendering rect
1230 if(prev_pymax != prev_pymin) {
1232 prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1233 prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1236 if(last_pymax != last_pymin) {
1237 /* take the index of one sample right of what we render */
1238 guint index = cache_index + (end - begin);
1240 if (index >= waveview->cache->data_size) {
1242 /* the data we want is off the end of the cache, which must mean its beyond
1243 the end of the region's source; hence the peak values are 0 */
1244 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1245 last_pymin = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1249 last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1250 last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1257 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1259 max = waveview->cache->data[cache_index].max;
1260 min = waveview->cache->data[cache_index].min;
1275 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1276 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1281 for(x = begin; x < end; ++x) {
1282 int clip_max = next_clip_max;
1283 int clip_min = next_clip_min;
1284 int fill_max, fill_min;
1291 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1292 next_pymax = last_pymax;
1293 next_pymin = last_pymin;
1298 if (cache_index < waveview->cache->data_size) {
1299 max = waveview->cache->data[cache_index].max;
1300 min = waveview->cache->data[cache_index].min;
1321 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1322 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1326 if (pymax == pymin) {
1327 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1329 if((prev_pymax < pymax && next_pymax < pymax) ||
1330 (prev_pymax == pymax && next_pymax == pymax)) {
1331 fill_max = pymax + 1;
1332 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1335 fill_max = MAX(prev_pymax, next_pymax);
1336 if(pymax == fill_max) {
1337 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1341 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1345 if((prev_pymin > pymin && next_pymin > pymin) ||
1346 (prev_pymin == pymin && next_pymin == pymin)) {
1347 fill_min = pymin - 1;
1348 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1351 fill_min = MIN(prev_pymin, next_pymin);
1352 if(pymin == fill_min) {
1353 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1356 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin);
1360 if(fill_max < fill_min) {
1361 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min);
1363 else if(fill_max == fill_min) {
1364 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1369 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1373 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1380 } else if (waveview->filled && rectify) {
1382 int prev_pymax = -1;
1383 int last_pymax = -1;
1386 int next_clip_max = 0;
1387 int next_clip_min = 0;
1389 // for rectified, this stays constant throughout the loop
1390 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1392 if(s1 < waveview->samples_per_unit) {
1393 /* we haven't got a prev vars to compare with, so outline the whole line here */
1397 s1 -= waveview->samples_per_unit;
1400 if(end == waveview->bbox_lrx) {
1401 /* we don't have the NEXT vars for the last sample */
1405 s2 += waveview->samples_per_unit;
1408 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1411 * Compute the variables outside the rendering rect
1413 if(prev_pymax < 0) {
1414 max = MIN(waveview->cache->data[cache_index].max, 1.0);
1415 min = MAX(waveview->cache->data[cache_index].min, -1.0);
1417 if (fabs (min) > fabs (max)) {
1421 prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1424 if(last_pymax < 0) {
1425 /* take the index of one sample right of what we render */
1426 int index = cache_index + (end - begin);
1428 max = MIN(waveview->cache->data[index].max, 1.0);
1429 min = MAX(waveview->cache->data[index].min, -1.0);
1431 if (fabs (min) > fabs (max)) {
1435 last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1439 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1441 max = waveview->cache->data[cache_index].max;
1442 min = waveview->cache->data[cache_index].min;
1454 if (fabs (min) > fabs (max)) {
1458 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1463 for(x = begin; x < end; ++x) {
1464 int clip_max = next_clip_max;
1465 int clip_min = next_clip_min;
1472 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1473 next_pymax = last_pymax;
1478 max = waveview->cache->data[cache_index].max;
1479 min = waveview->cache->data[cache_index].min;
1491 if (fabs (min) > fabs (max)) {
1495 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1499 if (pymax == pymin) {
1500 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1502 if((prev_pymax < pymax && next_pymax < pymax) ||
1503 (prev_pymax == pymax && next_pymax == pymax)) {
1504 fill_max = pymax + 1;
1505 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1508 fill_max = MAX(prev_pymax, next_pymax);
1509 if(pymax == fill_max) {
1510 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1514 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1518 if(fill_max < pymin) {
1519 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
1521 else if(fill_max == pymin) {
1522 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
1527 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1531 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1538 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1540 for (x = begin; x < end; x++) {
1543 int clip_max, clip_min;
1548 max = waveview->cache->data[cache_index].max;
1549 min = waveview->cache->data[cache_index].min;
1563 if (fabs (min) > fabs (max)) {
1567 max = max * waveview->height;
1569 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1570 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1574 max = max * half_height;
1575 min = min * half_height;
1577 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1578 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1581 /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1582 or, if samples_per_unit == 1, then a dot at each location.
1585 if (pymax == pymin) {
1586 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1588 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1591 /* show clipped waveforms with small red lines */
1594 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1598 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1601 /* presto, we're done */
1607 if (!waveview->rectified && waveview->zero_line) {
1609 //PAINT_HORIZA(buf, waveview->zero_r, waveview->zero_g, waveview->zero_b, waveview->zero_a, begin, endi-1, origin );
1611 unsigned char zero_r, zero_g, zero_b, zero_a;
1612 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a );
1613 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1614 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, zend, zeroline_y);
1621 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1622 GdkDrawable *drawable,
1624 int width, int height)
1626 GnomeCanvasWaveView *waveview;
1640 waveview = GNOME_CANVAS_WAVEVIEW (item);
1642 /* compute intersection of Drawable area and waveview,
1643 in canvas coordinate space
1646 if (x > waveview->bbox_ulx) {
1649 ulx = waveview->bbox_ulx;
1652 if (y > waveview->bbox_uly) {
1655 uly = waveview->bbox_uly;
1658 if (x + width > waveview->bbox_lrx) {
1659 lrx = waveview->bbox_lrx;
1664 if (y + height > waveview->bbox_lry) {
1665 lry = waveview->bbox_lry;
1670 /* figure out which samples we need for the resulting intersection */
1672 s1 = floor ((ulx - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1674 if (lrx == waveview->bbox_lrx) {
1675 /* This avoids minor rounding errors when we have the
1676 entire region visible.
1678 s2 = waveview->samples;
1680 s2 = s1 + floor ((lrx - ulx) * waveview->samples_per_unit);
1683 /* translate back to buffer coordinate space */
1690 /* don't rectify at single-sample zoom */
1691 if(waveview->rectified && waveview->samples_per_unit > 1.0) {
1697 clip_length = MIN(5,(waveview->height/4));
1699 cr = gdk_cairo_create (drawable);
1700 cairo_set_line_width (cr, 0.5);
1702 origin = waveview->bbox_uly - y + waveview->half_height;
1704 cairo_rectangle (cr, ulx, uly, lrx - ulx, lry - uly);
1707 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1708 waveview->cache->start = 0;
1709 waveview->cache->end = 0;
1710 waveview->reload_cache_in_render = FALSE;
1713 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1716 printf ("%p r (%d,%d)(%d,%d)[%d x %d] bbox (%d,%d)(%d,%d)[%d x %d]"
1717 " draw (%.1f,%.1f)(%.1f,%.1f)[%.1f x %.1f] s= %lu..%lu\n",
1728 waveview->bbox_lrx - waveview->bbox_ulx,
1729 waveview->bbox_lry - waveview->bbox_uly,
1737 /* draw the top half */
1739 for (xoff = ulx; xoff < lrx; xoff++) {
1742 max = waveview->cache->data[cache_index].max;
1743 min = waveview->cache->data[cache_index].min;
1754 if (fabs (min) > fabs (max)) {
1759 yoff = origin - (waveview->half_height * max) + 0.5;
1763 cairo_move_to (cr, xoff+0.5, yoff);
1765 cairo_line_to (cr, xoff+0.5, yoff);
1771 /* from the final top point, move out of the clip zone */
1773 cairo_line_to (cr, xoff + 10, yoff);
1775 /* now draw the bottom half */
1777 for (--xoff, --cache_index; xoff >= ulx; --xoff) {
1780 min = waveview->cache->data[cache_index].min;
1786 yoff = origin - (waveview->half_height * min) + 0.5;
1788 cairo_line_to (cr, xoff+0.5, yoff);
1792 /* from the final lower point, move out of the clip zone */
1794 cairo_line_to (cr, xoff - 10, yoff);
1796 /* close path to fill */
1798 cairo_close_path (cr);
1800 /* fill and stroke */
1802 cairo_set_source_rgba (cr,
1803 (waveview->fill_r/255.0),
1804 (waveview->fill_g/255.0),
1805 (waveview->fill_b/255.0),
1806 (waveview->fill_a/255.0));
1807 cairo_fill_preserve (cr);
1808 cairo_set_source_rgba (cr,
1809 (waveview->wave_r/255.0),
1810 (waveview->wave_g/255.0),
1811 (waveview->wave_b/255.0),
1812 (waveview->wave_a/255.0));
1819 if (clip_max || clip_min) {
1820 cairo_set_source_rgba (cr, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a);
1824 cairo_move_to (cr, xoff, yoff1);
1825 cairo_line_to (cr, xoff, yoff1 + clip_length);
1830 cairo_move_to (cr, xoff, yoff2);
1831 cairo_line_to (cr, xoff, yoff2 - clip_length);
1838 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1840 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1845 *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1846 *y2 = *y1 + waveview->height;
1850 gnome_canvas_item_i2w (item, &x, &y);
1851 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1854 gnome_canvas_item_i2w (item, &x, &y);
1855 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1856 printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1862 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1864 /* XXX for now, point is never inside the wave
1865 GnomeCanvasWaveView *waveview;
1866 double x1, y1, x2, y2;
1873 waveview = GNOME_CANVAS_WAVEVIEW (item);
1875 *actual_item = item;
1877 /* Find the bounds for the rectangle plus its outline width */
1879 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1881 /* Is point inside rectangle */
1883 if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1887 /* Point is outside rectangle */
1903 return sqrt (dx * dx + dy * dy);