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>
27 #include <ardour/dB.h>
30 #include "canvas-waveview.h"
31 #include "rgb_macros.h"
34 extern void c_stacktrace();
41 PROP_SOURCEFILE_LENGTH_FUNCTION,
47 PROP_SAMPLES_PER_UNIT,
48 PROP_AMPLITUDE_ABOVE_AXIS,
63 static void gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class);
65 static void gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview);
67 static void gnome_canvas_waveview_destroy (GtkObject *object);
69 static void gnome_canvas_waveview_set_property (GObject *object,
73 static void gnome_canvas_waveview_get_property (GObject *object,
78 static void gnome_canvas_waveview_update (GnomeCanvasItem *item,
83 static void gnome_canvas_waveview_bounds (GnomeCanvasItem *item,
89 static double gnome_canvas_waveview_point (GnomeCanvasItem *item,
94 GnomeCanvasItem **actual_item);
96 static void gnome_canvas_waveview_render (GnomeCanvasItem *item,
99 static void gnome_canvas_waveview_draw (GnomeCanvasItem *item,
100 GdkDrawable *drawable,
106 static void gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *,
109 static void gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *,
112 static gint32 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview,
116 static GnomeCanvasItemClass *parent_class;
119 gnome_canvas_waveview_get_type (void)
121 static GType waveview_type;
123 if (!waveview_type) {
124 static const GTypeInfo object_info = {
125 sizeof (GnomeCanvasWaveViewClass),
126 (GBaseInitFunc) NULL,
127 (GBaseFinalizeFunc) NULL,
128 (GClassInitFunc) gnome_canvas_waveview_class_init,
129 (GClassFinalizeFunc) NULL,
130 NULL, /* class_data */
131 sizeof (GnomeCanvasWaveView),
133 (GInstanceInitFunc) gnome_canvas_waveview_init,
134 NULL /* value_table */
137 waveview_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasWaveView",
141 return waveview_type;
145 gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class)
147 GObjectClass *gobject_class;
148 GtkObjectClass *object_class;
149 GnomeCanvasItemClass *item_class;
151 gobject_class = (GObjectClass *) class;
152 object_class = (GtkObjectClass *) class;
153 item_class = (GnomeCanvasItemClass *) class;
155 parent_class = g_type_class_peek_parent (class);
157 gobject_class->set_property = gnome_canvas_waveview_set_property;
158 gobject_class->get_property = gnome_canvas_waveview_get_property;
160 g_object_class_install_property
163 g_param_spec_pointer ("data_src", NULL, NULL,
164 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
166 g_object_class_install_property
169 g_param_spec_uint ("channel", NULL, NULL,
171 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
173 g_object_class_install_property
175 PROP_LENGTH_FUNCTION,
176 g_param_spec_pointer ("length_function", NULL, NULL,
177 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
179 g_object_class_install_property
181 PROP_SOURCEFILE_LENGTH_FUNCTION,
182 g_param_spec_pointer ("sourcefile_length_function", NULL, NULL,
183 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
185 g_object_class_install_property
188 g_param_spec_pointer ("peak_function", NULL, NULL,
189 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
191 g_object_class_install_property
194 g_param_spec_pointer ("gain_function", NULL, NULL,
195 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
197 g_object_class_install_property
200 g_param_spec_pointer ("gain_src", NULL, NULL,
201 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
203 g_object_class_install_property
206 g_param_spec_pointer ("cache", NULL, NULL,
207 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
209 g_object_class_install_property
212 g_param_spec_boolean ("cache_updater", NULL, NULL,
214 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
216 g_object_class_install_property
218 PROP_SAMPLES_PER_UNIT,
219 g_param_spec_double ("samples_per_unit", NULL, NULL,
220 0.0, G_MAXDOUBLE, 0.0,
221 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
223 g_object_class_install_property
225 PROP_AMPLITUDE_ABOVE_AXIS,
226 g_param_spec_double ("amplitude_above_axis", NULL, NULL,
227 0.0, G_MAXDOUBLE, 0.0,
228 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
230 g_object_class_install_property
233 g_param_spec_double ("x", NULL, NULL,
234 0.0, G_MAXDOUBLE, 0.0,
235 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
237 g_object_class_install_property
240 g_param_spec_double ("y", NULL, NULL,
241 0.0, G_MAXDOUBLE, 0.0,
242 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
244 g_object_class_install_property
247 g_param_spec_double ("height", NULL, NULL,
248 0.0, G_MAXDOUBLE, 0.0,
249 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
251 g_object_class_install_property
254 g_param_spec_uint ("wave_color", NULL, NULL,
256 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
258 g_object_class_install_property
261 g_param_spec_uint ("clip_color", NULL, NULL,
263 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
265 g_object_class_install_property
268 g_param_spec_uint ("zero_color", NULL, NULL,
270 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
272 g_object_class_install_property
275 g_param_spec_uint ("fill_color", NULL, NULL,
277 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
279 g_object_class_install_property
282 g_param_spec_boolean ("filled", NULL, NULL,
284 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
286 g_object_class_install_property
289 g_param_spec_boolean ("rectified", NULL, NULL,
291 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
293 g_object_class_install_property
296 g_param_spec_boolean ("zero_line", NULL, NULL,
298 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
300 g_object_class_install_property
303 g_param_spec_boolean ("logscaled", NULL, NULL,
305 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
307 g_object_class_install_property
310 g_param_spec_uint ("region_start", NULL, NULL,
312 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
314 object_class->destroy = gnome_canvas_waveview_destroy;
316 item_class->update = gnome_canvas_waveview_update;
317 item_class->bounds = gnome_canvas_waveview_bounds;
318 item_class->point = gnome_canvas_waveview_point;
319 item_class->render = gnome_canvas_waveview_render;
320 item_class->draw = gnome_canvas_waveview_draw;
323 GnomeCanvasWaveViewCache*
324 gnome_canvas_waveview_cache_new ()
326 GnomeCanvasWaveViewCache *c;
328 c = g_malloc (sizeof (GnomeCanvasWaveViewCache));
331 c->data = g_malloc (sizeof (GnomeCanvasWaveViewCacheEntry) * c->allocated);
340 gnome_canvas_waveview_cache_destroy (GnomeCanvasWaveViewCache* cache)
342 g_free (cache->data);
347 gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview)
352 waveview->cache_updater = FALSE;
353 waveview->data_src = NULL;
354 waveview->channel = 0;
355 waveview->peak_function = NULL;
356 waveview->length_function = NULL;
357 waveview->sourcefile_length_function = NULL;
358 waveview->gain_curve_function = NULL;
359 waveview->gain_src = NULL;
360 waveview->rectified = FALSE;
361 waveview->logscaled = FALSE;
362 waveview->filled = TRUE;
363 waveview->zero_line = FALSE;
364 waveview->region_start = 0;
365 waveview->samples_per_unit = 1.0;
366 waveview->amplitude_above_axis = 1.0;
367 waveview->height = 100.0;
368 waveview->screen_width = gdk_screen_width ();
369 waveview->reload_cache_in_render = FALSE;
371 waveview->wave_color = 0;
372 waveview->clip_color = 0;
373 waveview->zero_color = 0;
374 waveview->fill_color = 0;
378 gnome_canvas_waveview_destroy (GtkObject *object)
380 GnomeCanvasWaveView *waveview;
382 g_return_if_fail (object != NULL);
383 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
385 waveview = GNOME_CANVAS_WAVEVIEW (object);
387 if (GTK_OBJECT_CLASS (parent_class)->destroy)
388 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
391 #define DEBUG_CACHE 0
392 #undef CACHE_MEMMOVE_OPTIMIZATION
395 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_sample, gulong end_sample)
397 gulong required_cache_entries;
398 gulong rf1, rf2,rf3, required_frames;
399 gulong new_cache_start, new_cache_end;
405 GnomeCanvasWaveViewCache *cache;
407 #ifdef CACHE_MEMMOVE_OPTIMIZATION
408 gulong present_frames;
409 gulong present_entries;
412 cache = waveview->cache;
414 start_sample = start_sample + waveview->region_start;
415 end_sample = end_sample + waveview->region_start;
417 // printf("waveview->region_start == %lu\n",waveview->region_start);
419 printf ("\n\n=> 0x%x cache @ 0x%x range: %lu - %lu request: %lu - %lu (%lu frames)\n",
421 cache->start, cache->end,
422 start_sample, end_sample, end_sample - start_sample);
425 if (cache->start <= start_sample && cache->end >= end_sample) {
427 // printf ("0x%x: cache hit for %lu-%lu (cache holds: %lu-%lu\n",
428 // waveview, start_sample, end_sample, cache->start, cache->end);
433 /* make sure the cache is at least twice as wide as the screen width, and put the start sample
434 in the middle, ensuring that we cover the end_sample.
437 /* Note the assumption that we have a 1:1 units:pixel ratio for the canvas. Its everywhere ... */
439 half_width = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit)/2.0 + 0.5);
441 if (start_sample < half_width) {
444 new_cache_start = start_sample - half_width;
447 /* figure out how many frames we want */
449 rf1 = end_sample - start_sample + 1;
450 rf2 = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit * 2.0f));
451 required_frames = MAX(rf1,rf2);
453 /* but make sure it doesn't extend beyond the end of the source material */
455 rf3 = (gulong) (waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit)) + 1;
456 if (rf3 < new_cache_start) {
459 rf3 -= new_cache_start;
463 fprintf (stderr, "AVAILABLE FRAMES = %lu of %lu, start = %lu, sstart = %lu, cstart = %lu\n",
464 rf3, waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit),
465 waveview->region_start, start_sample, new_cache_start);
468 required_frames = MIN(required_frames,rf3);
470 new_cache_end = new_cache_start + required_frames - 1;
472 required_cache_entries = (gulong) floor (required_frames / waveview->samples_per_unit );
475 fprintf (stderr, "new cache = %lu - %lu\n", new_cache_start, new_cache_end);
476 fprintf(stderr,"required_cach_entries = %lu, samples_per_unit = %f req frames = %lu\n",
477 required_cache_entries,waveview->samples_per_unit, required_frames);
480 if (required_cache_entries > cache->allocated) {
481 cache->data = g_realloc (cache->data, sizeof (GnomeCanvasWaveViewCacheEntry) * required_cache_entries);
482 cache->allocated = required_cache_entries;
487 ostart = new_cache_start;
489 #ifdef CACHE_MEMMOVE_OPTIMIZATION
491 /* data is not entirely in the cache, so go fetch it, making sure to fill the cache */
493 /* some of the required cache entries are in the cache, but in the wrong
494 locations. use memmove to fix this.
497 if (cache->start < new_cache_start && new_cache_start < cache->end) {
499 /* case one: the common area is at the end of the existing cache. move it
500 to the beginning of the cache, and set up to refill whatever remains.
503 wv->cache_start wv->cache_end
504 |-------------------------------------------------------| cache
505 |--------------------------------| requested
506 <------------------->
508 new_cache_start new_cache_end
512 present_frames = cache->end - new_cache_start;
513 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
516 fprintf (stderr, "existing material at end of current cache, move to start of new cache\n"
517 "\tcopy from %lu to start\n", cache->data_size - present_entries);
520 memmove (&cache->data[0],
521 &cache->data[cache->data_size - present_entries],
522 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
525 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
526 present_frames, required_frames, present_entries, new_cache_start + present_entries,
527 cache->data + present_entries);
530 copied = present_entries;
531 offset = present_entries;
532 new_cache_start += present_frames;
533 required_frames -= present_frames;
535 } else if (new_cache_end > cache->start && new_cache_end < cache->end) {
537 /* case two: the common area lives at the beginning of the existing cache.
539 wv->cache_start wv->cache_end
540 |-----------------------------------------------------|
541 |--------------------------------|
545 new_cache_start new_cache_end
548 present_frames = new_cache_end - cache->start;
549 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
551 memmove (&cache->data[cache->data_size - present_entries],
553 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
556 fprintf (stderr, "existing material at start of current cache, move to start of end cache\n");
560 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
561 present_entries, required_frames, present_entries, new_cache_start + present_entries,
562 cache->data + present_entries);
565 copied = present_entries;
567 required_frames -= present_frames;
580 #endif /* CACHE_MEMMOVE_OPTIMIZATION */
582 // fprintf(stderr,"length == %lu\n",waveview->length_function (waveview->data_src));
583 // required_frames = MIN (waveview->length_function (waveview->data_src) - new_cache_start, required_frames);
585 npeaks = (gulong) floor (required_frames / waveview->samples_per_unit);
586 required_frames = npeaks * waveview->samples_per_unit;
591 printf ("requesting %lu/%f to cover %lu-%lu at %f spu (request was %lu-%lu) into cache + %lu\n",
592 required_frames, required_frames/waveview->samples_per_unit, new_cache_start, new_cache_end,
593 waveview->samples_per_unit, start_sample, end_sample, offset);
597 // printf ("cache holds %lu entries, requesting %lu to cover %lu-%lu (request was %lu-%lu)\n",
598 // cache->data_size, npeaks, new_cache_start, new_cache_end,
599 // start_sample, end_sample);
602 if (required_frames) {
603 waveview->peak_function (waveview->data_src, npeaks, new_cache_start, required_frames, cache->data + offset, waveview->channel,waveview->samples_per_unit);
605 /* take into account any copied peaks */
612 if (npeaks < cache->allocated) {
614 fprintf (stderr, "zero fill cache for %lu at %lu\n", cache->allocated - npeaks, npeaks);
616 memset (&cache->data[npeaks], 0, sizeof (GnomeCanvasWaveViewCacheEntry) * (cache->allocated - npeaks));
617 cache->data_size = npeaks;
619 cache->data_size = cache->allocated;
622 if (waveview->gain_curve_function) {
625 gain = (float*) malloc (sizeof (float) * cache->data_size);
627 waveview->gain_curve_function (waveview->gain_src, new_cache_start, new_cache_end, gain, cache->data_size);
629 for (n = 0; n < cache->data_size; ++n) {
630 cache->data[n].min *= gain[n];
631 cache->data[n].max *= gain[n];
638 /* do optional log scaling. this implementation is not particularly efficient */
640 if (waveview->logscaled) {
642 GnomeCanvasWaveViewCacheEntry* buf = cache->data;
644 for (n = 0; n < cache->data_size; ++n) {
646 if (buf[n].max > 0.0f) {
647 buf[n].max = alt_log_meter(coefficient_to_dB(buf[n].max));
648 } else if (buf[n].max < 0.0f) {
649 buf[n].max = -alt_log_meter(coefficient_to_dB(-buf[n].max));
652 if (buf[n].min > 0.0f) {
653 buf[n].min = alt_log_meter(coefficient_to_dB(buf[n].min));
654 } else if (buf[n].min < 0.0f) {
655 buf[n].min = -alt_log_meter(coefficient_to_dB(-buf[n].min));
660 cache->start = ostart;
661 cache->end = new_cache_end;
665 fprintf (stderr, "return cache index = %d\n",
666 (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5));
668 return (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5);
673 gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *waveview, void *data_src)
676 if (waveview->cache_updater) {
677 if (waveview->data_src == data_src) {
678 waveview->reload_cache_in_render = TRUE;
682 waveview->cache->start = 0;
683 waveview->cache->end = 0;
686 waveview->data_src = data_src;
690 gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *waveview, guint32 chan)
692 if (waveview->channel == chan) {
696 waveview->channel = chan;
700 gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
703 double x1, x2, y1, y2;
706 int Ix1, Ix2, Iy1, Iy2;
709 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
716 gnome_canvas_item_i2w_affine (item, i2w);
717 art_affine_point (&w1, &i1, i2w);
718 art_affine_point (&w2, &i2, i2w);
720 Ix1 = (int) rint(w1.x);
721 Ix2 = (int) rint(w2.x);
722 Iy1 = (int) rint(w1.y);
723 Iy2 = (int) rint(w2.y);
725 gnome_canvas_update_bbox (item, Ix1, Iy1, Ix2, Iy2);
733 gnome_canvas_waveview_set_property (GObject *object,
739 GnomeCanvasItem *item;
740 GnomeCanvasWaveView *waveview;
742 int calc_bounds = FALSE;
744 g_return_if_fail (object != NULL);
745 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
747 item = GNOME_CANVAS_ITEM (object);
748 waveview = GNOME_CANVAS_WAVEVIEW (object);
752 gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
757 gnome_canvas_waveview_set_channel (waveview, g_value_get_uint(value));
761 case PROP_LENGTH_FUNCTION:
762 waveview->length_function = g_value_get_pointer(value);
765 case PROP_SOURCEFILE_LENGTH_FUNCTION:
766 waveview->sourcefile_length_function = g_value_get_pointer(value);
770 case PROP_PEAK_FUNCTION:
771 waveview->peak_function = g_value_get_pointer(value);
775 case PROP_GAIN_FUNCTION:
776 waveview->gain_curve_function = g_value_get_pointer(value);
781 waveview->gain_src = g_value_get_pointer(value);
782 if (waveview->cache_updater) {
783 waveview->cache->start = 0;
784 waveview->cache->end = 0;
791 waveview->cache = g_value_get_pointer(value);
796 case PROP_CACHE_UPDATER:
797 waveview->cache_updater = g_value_get_boolean(value);
801 case PROP_SAMPLES_PER_UNIT:
802 if ((waveview->samples_per_unit = g_value_get_double(value)) < 1.0) {
803 waveview->samples_per_unit = 1.0;
805 if (waveview->cache_updater) {
806 waveview->cache->start = 0;
807 waveview->cache->end = 0;
813 case PROP_AMPLITUDE_ABOVE_AXIS:
814 waveview->amplitude_above_axis = g_value_get_double(value);
819 if (waveview->x != g_value_get_double (value)) {
820 waveview->x = g_value_get_double (value);
826 if (waveview->y != g_value_get_double (value)) {
827 waveview->y = g_value_get_double (value);
833 if (waveview->height != fabs (g_value_get_double (value))) {
834 waveview->height = fabs (g_value_get_double (value));
839 case PROP_WAVE_COLOR:
840 if (waveview->wave_color != g_value_get_uint(value)) {
841 waveview->wave_color = g_value_get_uint(value);
846 case PROP_CLIP_COLOR:
847 if (waveview->clip_color != g_value_get_uint(value)) {
848 waveview->clip_color = g_value_get_uint(value);
853 case PROP_ZERO_COLOR:
854 if (waveview->zero_color != g_value_get_uint(value)) {
855 waveview->zero_color = g_value_get_uint(value);
860 case PROP_FILL_COLOR:
861 if (waveview->fill_color != g_value_get_uint(value)) {
862 waveview->fill_color = g_value_get_uint(value);
868 if (waveview->filled != g_value_get_boolean(value)) {
869 waveview->filled = g_value_get_boolean(value);
875 if (waveview->rectified != g_value_get_boolean(value)) {
876 waveview->rectified = g_value_get_boolean(value);
882 if (waveview->zero_line != g_value_get_boolean(value)) {
883 waveview->zero_line = g_value_get_boolean(value);
889 if (waveview->logscaled != g_value_get_boolean(value)) {
890 waveview->logscaled = g_value_get_boolean(value);
891 if (waveview->cache_updater) {
892 waveview->cache->start = 0;
893 waveview->cache->end = 0;
899 case PROP_REGION_START:
900 waveview->region_start = g_value_get_uint(value);
911 gnome_canvas_waveview_reset_bounds (item);
915 gnome_canvas_item_request_update (item);
921 gnome_canvas_waveview_get_property (GObject *object,
928 g_return_if_fail (object != NULL);
929 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
931 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
935 g_value_set_pointer(value, waveview->data_src);
939 g_value_set_uint(value, waveview->channel);
942 case PROP_LENGTH_FUNCTION:
943 g_value_set_pointer(value, waveview->length_function);
946 case PROP_SOURCEFILE_LENGTH_FUNCTION:
947 g_value_set_pointer(value, waveview->sourcefile_length_function);
950 case PROP_PEAK_FUNCTION:
951 g_value_set_pointer(value, waveview->peak_function);
954 case PROP_GAIN_FUNCTION:
955 g_value_set_pointer(value, waveview->gain_curve_function);
959 g_value_set_pointer(value, waveview->gain_src);
963 g_value_set_pointer(value, waveview->cache);
966 case PROP_CACHE_UPDATER:
967 g_value_set_boolean(value, waveview->cache_updater);
970 case PROP_SAMPLES_PER_UNIT:
971 g_value_set_double(value, waveview->samples_per_unit);
974 case PROP_AMPLITUDE_ABOVE_AXIS:
975 g_value_set_double(value, waveview->amplitude_above_axis);
979 g_value_set_double (value, waveview->x);
983 g_value_set_double (value, waveview->y);
987 g_value_set_double (value, waveview->height);
990 case PROP_WAVE_COLOR:
991 g_value_set_uint (value, waveview->wave_color);
994 case PROP_CLIP_COLOR:
995 g_value_set_uint (value, waveview->clip_color);
998 case PROP_ZERO_COLOR:
999 g_value_set_uint (value, waveview->zero_color);
1002 case PROP_FILL_COLOR:
1003 g_value_set_uint (value, waveview->fill_color);
1007 g_value_set_boolean (value, waveview->filled);
1010 case PROP_RECTIFIED:
1011 g_value_set_boolean (value, waveview->rectified);
1014 case PROP_ZERO_LINE:
1015 g_value_set_boolean (value, waveview->zero_line);
1018 case PROP_LOGSCALED:
1019 g_value_set_boolean (value, waveview->logscaled);
1022 case PROP_REGION_START:
1023 g_value_set_uint (value, waveview->region_start);
1027 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1033 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1035 GnomeCanvasWaveView *waveview;
1038 waveview = GNOME_CANVAS_WAVEVIEW (item);
1040 // check_cache (waveview, "start of update");
1042 if (parent_class->update)
1043 (* parent_class->update) (item, affine, clip_path, flags);
1045 gnome_canvas_waveview_reset_bounds (item);
1047 /* get the canvas coordinates of the view. Do NOT use affines
1048 for this, because they do not round to the integer units used
1049 by the canvas, resulting in subtle pixel-level errors later.
1055 gnome_canvas_item_i2w (item, &x, &y);
1056 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
1058 waveview->samples = waveview->length_function (waveview->data_src);
1060 x = waveview->x + (waveview->samples / waveview->samples_per_unit);
1061 y = waveview->y + waveview->height;
1063 gnome_canvas_item_i2w (item, &x, &y);
1064 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
1066 /* cache the half-height and the end point in canvas units */
1068 waveview->half_height = waveview->height / 2.0;
1070 /* parse the color */
1072 UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
1074 UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
1076 UINT_TO_RGBA (waveview->fill_color, &waveview->fill_r, &waveview->fill_g, &waveview->fill_b,
1079 // check_cache (waveview, "end of update");
1083 gnome_canvas_waveview_render (GnomeCanvasItem *item,
1084 GnomeCanvasBuf *buf)
1086 GnomeCanvasWaveView *waveview;
1088 int clip_length = 0;
1096 waveview = GNOME_CANVAS_WAVEVIEW (item);
1098 // check_cache (waveview, "start of render");
1100 if (parent_class->render) {
1101 (*parent_class->render) (item, buf);
1105 gnome_canvas_buf_ensure_buf (buf);
1109 begin = MAX(waveview->bbox_ulx, buf->rect.x0);
1111 if (begin == waveview->bbox_ulx) {
1117 if (waveview->bbox_lrx >= 0) {
1118 end = MIN(waveview->bbox_lrx,buf->rect.x1);
1123 if (end == waveview->bbox_lrx) {
1133 s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1135 // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
1137 if (end == waveview->bbox_lrx) {
1138 /* This avoids minor rounding errors when we have the
1139 entire region visible.
1141 s2 = waveview->samples;
1143 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
1147 printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
1148 " b/e %d..%d s= %lu..%lu @ %f\n",
1159 waveview->samples_per_unit);
1162 /* now ensure that the cache is full and properly
1166 // check_cache (waveview, "pre-ensure");
1168 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1169 waveview->cache->start = 0;
1170 waveview->cache->end = 0;
1171 waveview->reload_cache_in_render = FALSE;
1174 // check_cache (waveview, "post-ensure");
1176 /* don't rectify at single-sample zoom */
1177 if(waveview->rectified && waveview->samples_per_unit > 1) {
1184 clip_length = MIN(5,(waveview->height/4));
1187 Now draw each line, clipping it appropriately. The clipping
1188 is done by the macros PAINT_FOO().
1191 half_height = waveview->half_height;
1193 /* this makes it slightly easier to comprehend whats going on */
1194 #define origin half_height
1196 if(waveview->filled && !rectify) {
1201 int next_pymin, next_pymax;
1203 int next_clip_max = 0;
1204 int next_clip_min = 0;
1206 if(s1 < waveview->samples_per_unit) {
1207 /* we haven't got a prev vars to compare with, so outline the whole line here */
1208 prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1209 prev_pymin = prev_pymax;
1212 s1 -= waveview->samples_per_unit;
1215 if(end == waveview->bbox_lrx) {
1216 /* we don't have the NEXT vars for the last sample */
1217 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1218 last_pymin = last_pymax;
1221 s2 += waveview->samples_per_unit;
1224 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1227 * Compute the variables outside the rendering rect
1229 if(prev_pymax != prev_pymin) {
1230 prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1231 prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1234 if(last_pymax != last_pymin) {
1235 /* take the index of one sample right of what we render */
1236 int index = cache_index + (end - begin);
1238 last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1239 last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1243 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1245 max = waveview->cache->data[cache_index].max;
1246 min = waveview->cache->data[cache_index].min;
1261 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1262 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1267 for(x = begin; x < end; ++x) {
1268 int clip_max = next_clip_max;
1269 int clip_min = next_clip_min;
1270 int fill_max, fill_min;
1277 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1278 next_pymax = last_pymax;
1279 next_pymin = last_pymin;
1284 max = waveview->cache->data[cache_index].max;
1285 min = waveview->cache->data[cache_index].min;
1303 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1304 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1308 if (pymax == pymin) {
1309 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1311 if((prev_pymax < pymax && next_pymax < pymax) ||
1312 (prev_pymax == pymax && next_pymax == pymax)) {
1313 fill_max = pymax + 1;
1314 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1317 fill_max = MAX(prev_pymax, next_pymax);
1318 if(pymax == fill_max) {
1319 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1323 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1327 if((prev_pymin > pymin && next_pymin > pymin) ||
1328 (prev_pymin == pymin && next_pymin == pymin)) {
1329 fill_min = pymin - 1;
1330 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1333 fill_min = MIN(prev_pymin, next_pymin);
1334 if(pymin == fill_min) {
1335 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1338 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin);
1342 if(fill_max < fill_min) {
1343 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min);
1345 else if(fill_max == fill_min) {
1346 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1351 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1355 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1362 else if(waveview->filled && rectify) {
1363 int prev_pymax = -1;
1364 int last_pymax = -1;
1367 int next_clip_max = 0;
1368 int next_clip_min = 0;
1370 // for rectified, this stays constant throughout the loop
1371 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1373 if(s1 < waveview->samples_per_unit) {
1374 /* we haven't got a prev vars to compare with, so outline the whole line here */
1378 s1 -= waveview->samples_per_unit;
1381 if(end == waveview->bbox_lrx) {
1382 /* we don't have the NEXT vars for the last sample */
1386 s2 += waveview->samples_per_unit;
1389 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1392 * Compute the variables outside the rendering rect
1394 if(prev_pymax < 0) {
1395 max = MIN(waveview->cache->data[cache_index].max, 1.0);
1396 min = MAX(waveview->cache->data[cache_index].min, -1.0);
1398 if (fabs (min) > fabs (max)) {
1402 prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1405 if(last_pymax < 0) {
1406 /* take the index of one sample right of what we render */
1407 int index = cache_index + (end - begin);
1409 max = MIN(waveview->cache->data[index].max, 1.0);
1410 min = MAX(waveview->cache->data[index].min, -1.0);
1412 if (fabs (min) > fabs (max)) {
1416 last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1420 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1422 max = waveview->cache->data[cache_index].max;
1423 min = waveview->cache->data[cache_index].min;
1435 if (fabs (min) > fabs (max)) {
1439 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1444 for(x = begin; x < end; ++x) {
1445 int clip_max = next_clip_max;
1446 int clip_min = next_clip_min;
1453 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1454 next_pymax = last_pymax;
1459 max = waveview->cache->data[cache_index].max;
1460 min = waveview->cache->data[cache_index].min;
1472 if (fabs (min) > fabs (max)) {
1476 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1480 if (pymax == pymin) {
1481 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1483 if((prev_pymax < pymax && next_pymax < pymax) ||
1484 (prev_pymax == pymax && next_pymax == pymax)) {
1485 fill_max = pymax + 1;
1486 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1489 fill_max = MAX(prev_pymax, next_pymax);
1490 if(pymax == fill_max) {
1491 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1495 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1499 if(fill_max < pymin) {
1500 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
1502 else if(fill_max == pymin) {
1503 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
1508 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1512 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1519 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1521 for (x = begin; x < end; x++) {
1524 int clip_max, clip_min;
1529 max = waveview->cache->data[cache_index].max;
1530 min = waveview->cache->data[cache_index].min;
1544 if (fabs (min) > fabs (max)) {
1548 max = max * waveview->height;
1550 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1551 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1555 max = max * half_height;
1556 min = min * half_height;
1558 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1559 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1562 /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1563 or, if samples_per_unit == 1, then a dot at each location.
1566 if (pymax == pymin) {
1567 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1569 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1572 /* show clipped waveforms with small red lines */
1575 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1579 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1582 /* presto, we're done */
1588 if (!waveview->rectified && waveview->zero_line) {
1590 //PAINT_HORIZA(buf, waveview->zero_r, waveview->zero_g, waveview->zero_b, waveview->zero_a, begin, endi-1, origin );
1592 unsigned char zero_r, zero_g, zero_b, zero_a;
1593 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a );
1594 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1595 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, end, zeroline_y);
1602 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1603 GdkDrawable *drawable,
1605 int width, int height)
1607 GnomeCanvasWaveView *waveview;
1609 waveview = GNOME_CANVAS_WAVEVIEW (item);
1611 if (parent_class->draw) {
1612 (* parent_class->draw) (item, drawable, x, y, width, height);
1615 fprintf (stderr, "please don't use the CanvasWaveView item in a non-aa Canvas\n");
1620 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1622 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1627 *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1628 *y2 = *y1 + waveview->height;
1632 gnome_canvas_item_i2w (item, &x, &y);
1633 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1636 gnome_canvas_item_i2w (item, &x, &y);
1637 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1638 printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1644 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1646 /* XXX for now, point is never inside the wave
1647 GnomeCanvasWaveView *waveview;
1648 double x1, y1, x2, y2;
1655 waveview = GNOME_CANVAS_WAVEVIEW (item);
1657 *actual_item = item;
1659 /* Find the bounds for the rectangle plus its outline width */
1661 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1663 /* Is point inside rectangle */
1665 if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1669 /* Point is outside rectangle */
1685 return sqrt (dx * dx + dy * dy);