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 gint32 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
396 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_sample, gulong end_sample)
398 gulong required_cache_entries;
399 gulong rf1, rf2,rf3, required_frames;
400 gulong new_cache_start, new_cache_end;
406 GnomeCanvasWaveViewCache *cache;
408 #ifdef CACHE_MEMMOVE_OPTIMIZATION
409 gulong present_frames;
410 gulong present_entries;
413 cache = waveview->cache;
415 start_sample = start_sample + waveview->region_start;
416 end_sample = end_sample + waveview->region_start;
418 // printf("waveview->region_start == %lu\n",waveview->region_start);
420 printf ("\n\n=> 0x%x cache @ 0x%x range: %lu - %lu request: %lu - %lu (%lu frames)\n",
422 cache->start, cache->end,
423 start_sample, end_sample, end_sample - start_sample);
426 if (cache->start <= start_sample && cache->end >= end_sample) {
428 // printf ("0x%x: cache hit for %lu-%lu (cache holds: %lu-%lu\n",
429 // waveview, start_sample, end_sample, cache->start, cache->end);
434 /* make sure the cache is at least twice as wide as the screen width, and put the start sample
435 in the middle, ensuring that we cover the end_sample.
438 /* Note the assumption that we have a 1:1 units:pixel ratio for the canvas. Its everywhere ... */
440 half_width = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit)/2.0 + 0.5);
442 if (start_sample < half_width) {
445 new_cache_start = start_sample - half_width;
448 /* figure out how many frames we want */
450 rf1 = end_sample - start_sample + 1;
451 rf2 = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit * 2.0f));
452 required_frames = MAX(rf1,rf2);
454 /* but make sure it doesn't extend beyond the end of the source material */
456 rf3 = (gulong) (waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit)) + 1;
457 if (rf3 < new_cache_start) {
460 rf3 -= new_cache_start;
464 fprintf (stderr, "AVAILABLE FRAMES = %lu of %lu, start = %lu, sstart = %lu, cstart = %lu\n",
465 rf3, waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit),
466 waveview->region_start, start_sample, new_cache_start);
469 required_frames = MIN(required_frames,rf3);
471 new_cache_end = new_cache_start + required_frames - 1;
473 required_cache_entries = (gulong) floor (required_frames / waveview->samples_per_unit );
476 fprintf (stderr, "new cache = %lu - %lu\n", new_cache_start, new_cache_end);
477 fprintf(stderr,"required_cach_entries = %lu, samples_per_unit = %f req frames = %lu\n",
478 required_cache_entries,waveview->samples_per_unit, required_frames);
481 if (required_cache_entries > cache->allocated) {
482 cache->data = g_realloc (cache->data, sizeof (GnomeCanvasWaveViewCacheEntry) * required_cache_entries);
483 cache->allocated = required_cache_entries;
488 ostart = new_cache_start;
490 #ifdef CACHE_MEMMOVE_OPTIMIZATION
492 /* data is not entirely in the cache, so go fetch it, making sure to fill the cache */
494 /* some of the required cache entries are in the cache, but in the wrong
495 locations. use memmove to fix this.
498 if (cache->start < new_cache_start && new_cache_start < cache->end) {
500 /* case one: the common area is at the end of the existing cache. move it
501 to the beginning of the cache, and set up to refill whatever remains.
504 wv->cache_start wv->cache_end
505 |-------------------------------------------------------| cache
506 |--------------------------------| requested
507 <------------------->
509 new_cache_start new_cache_end
513 present_frames = cache->end - new_cache_start;
514 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
517 fprintf (stderr, "existing material at end of current cache, move to start of new cache\n"
518 "\tcopy from %lu to start\n", cache->data_size - present_entries);
521 memmove (&cache->data[0],
522 &cache->data[cache->data_size - present_entries],
523 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
526 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
527 present_frames, required_frames, present_entries, new_cache_start + present_entries,
528 cache->data + present_entries);
531 copied = present_entries;
532 offset = present_entries;
533 new_cache_start += present_frames;
534 required_frames -= present_frames;
536 } else if (new_cache_end > cache->start && new_cache_end < cache->end) {
538 /* case two: the common area lives at the beginning of the existing cache.
540 wv->cache_start wv->cache_end
541 |-----------------------------------------------------|
542 |--------------------------------|
546 new_cache_start new_cache_end
549 present_frames = new_cache_end - cache->start;
550 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
552 memmove (&cache->data[cache->data_size - present_entries],
554 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
557 fprintf (stderr, "existing material at start of current cache, move to start of end cache\n");
561 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
562 present_entries, required_frames, present_entries, new_cache_start + present_entries,
563 cache->data + present_entries);
566 copied = present_entries;
568 required_frames -= present_frames;
581 #endif /* CACHE_MEMMOVE_OPTIMIZATION */
583 // fprintf(stderr,"length == %lu\n",waveview->length_function (waveview->data_src));
584 // required_frames = MIN (waveview->length_function (waveview->data_src) - new_cache_start, required_frames);
586 npeaks = (gulong) floor (required_frames / waveview->samples_per_unit);
587 required_frames = npeaks * waveview->samples_per_unit;
592 printf ("requesting %lu/%f to cover %lu-%lu at %f spu (request was %lu-%lu) into cache + %lu\n",
593 required_frames, required_frames/waveview->samples_per_unit, new_cache_start, new_cache_end,
594 waveview->samples_per_unit, start_sample, end_sample, offset);
598 // printf ("cache holds %lu entries, requesting %lu to cover %lu-%lu (request was %lu-%lu)\n",
599 // cache->data_size, npeaks, new_cache_start, new_cache_end,
600 // start_sample, end_sample);
603 if (required_frames) {
604 waveview->peak_function (waveview->data_src, npeaks, new_cache_start, required_frames, cache->data + offset, waveview->channel,waveview->samples_per_unit);
606 /* take into account any copied peaks */
613 if (npeaks < cache->allocated) {
615 fprintf (stderr, "zero fill cache for %lu at %lu\n", cache->allocated - npeaks, npeaks);
617 memset (&cache->data[npeaks], 0, sizeof (GnomeCanvasWaveViewCacheEntry) * (cache->allocated - npeaks));
618 cache->data_size = npeaks;
620 cache->data_size = cache->allocated;
623 if (waveview->gain_curve_function) {
626 gain = (float*) malloc (sizeof (float) * cache->data_size);
628 waveview->gain_curve_function (waveview->gain_src, new_cache_start, new_cache_end, gain, cache->data_size);
630 for (n = 0; n < cache->data_size; ++n) {
631 cache->data[n].min *= gain[n];
632 cache->data[n].max *= gain[n];
639 /* do optional log scaling. this implementation is not particularly efficient */
641 if (waveview->logscaled) {
643 GnomeCanvasWaveViewCacheEntry* buf = cache->data;
645 for (n = 0; n < cache->data_size; ++n) {
647 if (buf[n].max > 0.0f) {
648 buf[n].max = alt_log_meter(coefficient_to_dB(buf[n].max));
649 } else if (buf[n].max < 0.0f) {
650 buf[n].max = -alt_log_meter(coefficient_to_dB(-buf[n].max));
653 if (buf[n].min > 0.0f) {
654 buf[n].min = alt_log_meter(coefficient_to_dB(buf[n].min));
655 } else if (buf[n].min < 0.0f) {
656 buf[n].min = -alt_log_meter(coefficient_to_dB(-buf[n].min));
661 cache->start = ostart;
662 cache->end = new_cache_end;
666 fprintf (stderr, "return cache index = %d\n",
667 (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5));
669 return (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5);
674 gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *waveview, void *data_src)
677 if (waveview->cache_updater) {
678 if (waveview->data_src == data_src) {
679 waveview->reload_cache_in_render = TRUE;
683 waveview->cache->start = 0;
684 waveview->cache->end = 0;
687 waveview->data_src = data_src;
691 gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *waveview, guint32 chan)
693 if (waveview->channel == chan) {
697 waveview->channel = chan;
701 gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
704 double x1, x2, y1, y2;
707 int Ix1, Ix2, Iy1, Iy2;
710 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
717 gnome_canvas_item_i2w_affine (item, i2w);
718 art_affine_point (&w1, &i1, i2w);
719 art_affine_point (&w2, &i2, i2w);
721 Ix1 = (int) rint(w1.x);
722 Ix2 = (int) rint(w2.x);
723 Iy1 = (int) rint(w1.y);
724 Iy2 = (int) rint(w2.y);
726 gnome_canvas_update_bbox (item, Ix1, Iy1, Ix2, Iy2);
734 gnome_canvas_waveview_set_property (GObject *object,
740 GnomeCanvasItem *item;
741 GnomeCanvasWaveView *waveview;
743 int calc_bounds = FALSE;
745 g_return_if_fail (object != NULL);
746 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
748 item = GNOME_CANVAS_ITEM (object);
749 waveview = GNOME_CANVAS_WAVEVIEW (object);
753 gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
758 gnome_canvas_waveview_set_channel (waveview, g_value_get_uint(value));
762 case PROP_LENGTH_FUNCTION:
763 waveview->length_function = g_value_get_pointer(value);
766 case PROP_SOURCEFILE_LENGTH_FUNCTION:
767 waveview->sourcefile_length_function = g_value_get_pointer(value);
771 case PROP_PEAK_FUNCTION:
772 waveview->peak_function = g_value_get_pointer(value);
776 case PROP_GAIN_FUNCTION:
777 waveview->gain_curve_function = g_value_get_pointer(value);
782 waveview->gain_src = g_value_get_pointer(value);
783 if (waveview->cache_updater) {
784 waveview->cache->start = 0;
785 waveview->cache->end = 0;
792 waveview->cache = g_value_get_pointer(value);
797 case PROP_CACHE_UPDATER:
798 waveview->cache_updater = g_value_get_boolean(value);
802 case PROP_SAMPLES_PER_UNIT:
803 if ((waveview->samples_per_unit = g_value_get_double(value)) < 1.0) {
804 waveview->samples_per_unit = 1.0;
806 if (waveview->cache_updater) {
807 waveview->cache->start = 0;
808 waveview->cache->end = 0;
814 case PROP_AMPLITUDE_ABOVE_AXIS:
815 waveview->amplitude_above_axis = g_value_get_double(value);
820 if (waveview->x != g_value_get_double (value)) {
821 waveview->x = g_value_get_double (value);
827 if (waveview->y != g_value_get_double (value)) {
828 waveview->y = g_value_get_double (value);
834 if (waveview->height != fabs (g_value_get_double (value))) {
835 waveview->height = fabs (g_value_get_double (value));
840 case PROP_WAVE_COLOR:
841 if (waveview->wave_color != g_value_get_uint(value)) {
842 waveview->wave_color = g_value_get_uint(value);
847 case PROP_CLIP_COLOR:
848 if (waveview->clip_color != g_value_get_uint(value)) {
849 waveview->clip_color = g_value_get_uint(value);
854 case PROP_ZERO_COLOR:
855 if (waveview->zero_color != g_value_get_uint(value)) {
856 waveview->zero_color = g_value_get_uint(value);
861 case PROP_FILL_COLOR:
862 if (waveview->fill_color != g_value_get_uint(value)) {
863 waveview->fill_color = g_value_get_uint(value);
869 if (waveview->filled != g_value_get_boolean(value)) {
870 waveview->filled = g_value_get_boolean(value);
876 if (waveview->rectified != g_value_get_boolean(value)) {
877 waveview->rectified = g_value_get_boolean(value);
883 if (waveview->zero_line != g_value_get_boolean(value)) {
884 waveview->zero_line = g_value_get_boolean(value);
890 if (waveview->logscaled != g_value_get_boolean(value)) {
891 waveview->logscaled = g_value_get_boolean(value);
892 if (waveview->cache_updater) {
893 waveview->cache->start = 0;
894 waveview->cache->end = 0;
900 case PROP_REGION_START:
901 waveview->region_start = g_value_get_uint(value);
912 gnome_canvas_waveview_reset_bounds (item);
916 gnome_canvas_item_request_update (item);
922 gnome_canvas_waveview_get_property (GObject *object,
929 g_return_if_fail (object != NULL);
930 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
932 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
936 g_value_set_pointer(value, waveview->data_src);
940 g_value_set_uint(value, waveview->channel);
943 case PROP_LENGTH_FUNCTION:
944 g_value_set_pointer(value, waveview->length_function);
947 case PROP_SOURCEFILE_LENGTH_FUNCTION:
948 g_value_set_pointer(value, waveview->sourcefile_length_function);
951 case PROP_PEAK_FUNCTION:
952 g_value_set_pointer(value, waveview->peak_function);
955 case PROP_GAIN_FUNCTION:
956 g_value_set_pointer(value, waveview->gain_curve_function);
960 g_value_set_pointer(value, waveview->gain_src);
964 g_value_set_pointer(value, waveview->cache);
967 case PROP_CACHE_UPDATER:
968 g_value_set_boolean(value, waveview->cache_updater);
971 case PROP_SAMPLES_PER_UNIT:
972 g_value_set_double(value, waveview->samples_per_unit);
975 case PROP_AMPLITUDE_ABOVE_AXIS:
976 g_value_set_double(value, waveview->amplitude_above_axis);
980 g_value_set_double (value, waveview->x);
984 g_value_set_double (value, waveview->y);
988 g_value_set_double (value, waveview->height);
991 case PROP_WAVE_COLOR:
992 g_value_set_uint (value, waveview->wave_color);
995 case PROP_CLIP_COLOR:
996 g_value_set_uint (value, waveview->clip_color);
999 case PROP_ZERO_COLOR:
1000 g_value_set_uint (value, waveview->zero_color);
1003 case PROP_FILL_COLOR:
1004 g_value_set_uint (value, waveview->fill_color);
1008 g_value_set_boolean (value, waveview->filled);
1011 case PROP_RECTIFIED:
1012 g_value_set_boolean (value, waveview->rectified);
1015 case PROP_ZERO_LINE:
1016 g_value_set_boolean (value, waveview->zero_line);
1019 case PROP_LOGSCALED:
1020 g_value_set_boolean (value, waveview->logscaled);
1023 case PROP_REGION_START:
1024 g_value_set_uint (value, waveview->region_start);
1028 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1034 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1036 GnomeCanvasWaveView *waveview;
1039 waveview = GNOME_CANVAS_WAVEVIEW (item);
1041 // check_cache (waveview, "start of update");
1043 if (parent_class->update)
1044 (* parent_class->update) (item, affine, clip_path, flags);
1046 gnome_canvas_waveview_reset_bounds (item);
1048 /* get the canvas coordinates of the view. Do NOT use affines
1049 for this, because they do not round to the integer units used
1050 by the canvas, resulting in subtle pixel-level errors later.
1056 gnome_canvas_item_i2w (item, &x, &y);
1057 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
1059 waveview->samples = waveview->length_function (waveview->data_src);
1061 x = waveview->x + (waveview->samples / waveview->samples_per_unit);
1062 y = waveview->y + waveview->height;
1064 gnome_canvas_item_i2w (item, &x, &y);
1065 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
1067 /* cache the half-height and the end point in canvas units */
1069 waveview->half_height = waveview->height / 2.0;
1071 /* parse the color */
1073 UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
1075 UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
1077 UINT_TO_RGBA (waveview->fill_color, &waveview->fill_r, &waveview->fill_g, &waveview->fill_b,
1080 // check_cache (waveview, "end of update");
1084 gnome_canvas_waveview_render (GnomeCanvasItem *item,
1085 GnomeCanvasBuf *buf)
1087 GnomeCanvasWaveView *waveview;
1089 int clip_length = 0;
1097 waveview = GNOME_CANVAS_WAVEVIEW (item);
1099 // check_cache (waveview, "start of render");
1101 if (parent_class->render) {
1102 (*parent_class->render) (item, buf);
1106 gnome_canvas_buf_ensure_buf (buf);
1110 begin = MAX(waveview->bbox_ulx, buf->rect.x0);
1112 if (begin == waveview->bbox_ulx) {
1118 if (waveview->bbox_lrx >= 0) {
1119 end = MIN(waveview->bbox_lrx,buf->rect.x1);
1124 if (end == waveview->bbox_lrx) {
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) {
1231 prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1232 prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1235 if(last_pymax != last_pymin) {
1236 /* take the index of one sample right of what we render */
1237 int index = cache_index + (end - begin);
1239 last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1240 last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1244 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1246 max = waveview->cache->data[cache_index].max;
1247 min = waveview->cache->data[cache_index].min;
1262 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1263 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1268 for(x = begin; x < end; ++x) {
1269 int clip_max = next_clip_max;
1270 int clip_min = next_clip_min;
1271 int fill_max, fill_min;
1278 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1279 next_pymax = last_pymax;
1280 next_pymin = last_pymin;
1285 max = waveview->cache->data[cache_index].max;
1286 min = waveview->cache->data[cache_index].min;
1304 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1305 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1309 if (pymax == pymin) {
1310 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1312 if((prev_pymax < pymax && next_pymax < pymax) ||
1313 (prev_pymax == pymax && next_pymax == pymax)) {
1314 fill_max = pymax + 1;
1315 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1318 fill_max = MAX(prev_pymax, next_pymax);
1319 if(pymax == fill_max) {
1320 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1324 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1328 if((prev_pymin > pymin && next_pymin > pymin) ||
1329 (prev_pymin == pymin && next_pymin == pymin)) {
1330 fill_min = pymin - 1;
1331 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1334 fill_min = MIN(prev_pymin, next_pymin);
1335 if(pymin == fill_min) {
1336 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1339 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin);
1343 if(fill_max < fill_min) {
1344 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min);
1346 else if(fill_max == fill_min) {
1347 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1352 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1356 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1363 else if(waveview->filled && rectify) {
1364 int prev_pymax = -1;
1365 int last_pymax = -1;
1368 int next_clip_max = 0;
1369 int next_clip_min = 0;
1371 // for rectified, this stays constant throughout the loop
1372 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1374 if(s1 < waveview->samples_per_unit) {
1375 /* we haven't got a prev vars to compare with, so outline the whole line here */
1379 s1 -= waveview->samples_per_unit;
1382 if(end == waveview->bbox_lrx) {
1383 /* we don't have the NEXT vars for the last sample */
1387 s2 += waveview->samples_per_unit;
1390 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1393 * Compute the variables outside the rendering rect
1395 if(prev_pymax < 0) {
1396 max = MIN(waveview->cache->data[cache_index].max, 1.0);
1397 min = MAX(waveview->cache->data[cache_index].min, -1.0);
1399 if (fabs (min) > fabs (max)) {
1403 prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1406 if(last_pymax < 0) {
1407 /* take the index of one sample right of what we render */
1408 int index = cache_index + (end - begin);
1410 max = MIN(waveview->cache->data[index].max, 1.0);
1411 min = MAX(waveview->cache->data[index].min, -1.0);
1413 if (fabs (min) > fabs (max)) {
1417 last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1421 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1423 max = waveview->cache->data[cache_index].max;
1424 min = waveview->cache->data[cache_index].min;
1436 if (fabs (min) > fabs (max)) {
1440 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1445 for(x = begin; x < end; ++x) {
1446 int clip_max = next_clip_max;
1447 int clip_min = next_clip_min;
1454 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1455 next_pymax = last_pymax;
1460 max = waveview->cache->data[cache_index].max;
1461 min = waveview->cache->data[cache_index].min;
1473 if (fabs (min) > fabs (max)) {
1477 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1481 if (pymax == pymin) {
1482 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1484 if((prev_pymax < pymax && next_pymax < pymax) ||
1485 (prev_pymax == pymax && next_pymax == pymax)) {
1486 fill_max = pymax + 1;
1487 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1490 fill_max = MAX(prev_pymax, next_pymax);
1491 if(pymax == fill_max) {
1492 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1496 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1500 if(fill_max < pymin) {
1501 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
1503 else if(fill_max == pymin) {
1504 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
1509 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1513 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1520 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1522 for (x = begin; x < end; x++) {
1525 int clip_max, clip_min;
1530 max = waveview->cache->data[cache_index].max;
1531 min = waveview->cache->data[cache_index].min;
1545 if (fabs (min) > fabs (max)) {
1549 max = max * waveview->height;
1551 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1552 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1556 max = max * half_height;
1557 min = min * half_height;
1559 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1560 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1563 /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1564 or, if samples_per_unit == 1, then a dot at each location.
1567 if (pymax == pymin) {
1568 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1570 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1573 /* show clipped waveforms with small red lines */
1576 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1580 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1583 /* presto, we're done */
1589 if (!waveview->rectified && waveview->zero_line) {
1591 //PAINT_HORIZA(buf, waveview->zero_r, waveview->zero_g, waveview->zero_b, waveview->zero_a, begin, endi-1, origin );
1593 unsigned char zero_r, zero_g, zero_b, zero_a;
1594 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a );
1595 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1596 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, end, zeroline_y);
1603 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1604 GdkDrawable *drawable,
1606 int width, int height)
1608 GnomeCanvasWaveView *waveview;
1612 double zbegin, zend;
1624 waveview = GNOME_CANVAS_WAVEVIEW (item);
1626 /* compute intersection of Drawable area and waveview,
1627 in canvas coordinate space
1630 if (x > waveview->bbox_ulx) {
1634 ulx = waveview->bbox_ulx;
1638 if (y > waveview->bbox_uly) {
1641 uly = waveview->bbox_uly;
1644 if (x + width > waveview->bbox_lrx) {
1645 lrx = waveview->bbox_lrx;
1652 if (y + height > waveview->bbox_lry) {
1653 lry = waveview->bbox_lry;
1658 /* figure out which samples we need for the resulting intersection */
1660 s1 = floor ((ulx - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1662 if (lrx == waveview->bbox_lrx) {
1663 /* This avoids minor rounding errors when we have the
1664 entire region visible.
1666 s2 = waveview->samples;
1668 s2 = s1 + floor ((lrx - ulx) * waveview->samples_per_unit);
1671 /* translate back to buffer coordinate space */
1680 /* don't rectify at single-sample zoom */
1681 if(waveview->rectified && waveview->samples_per_unit > 1.0) {
1687 clip_length = MIN(5,(waveview->height/4));
1689 cr = gdk_cairo_create (drawable);
1690 cairo_set_line_width (cr, 0.5);
1692 origin = waveview->bbox_uly - y + waveview->half_height;
1694 cairo_rectangle (cr, ulx, uly, lrx - ulx, lry - uly);
1697 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1698 waveview->cache->start = 0;
1699 waveview->cache->end = 0;
1700 waveview->reload_cache_in_render = FALSE;
1703 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1706 printf ("%p r (%d,%d)(%d,%d)[%d x %d] bbox (%d,%d)(%d,%d)[%d x %d]"
1707 " draw (%.1f,%.1f)(%.1f,%.1f)[%.1f x %.1f] s= %lu..%lu\n",
1718 waveview->bbox_lrx - waveview->bbox_ulx,
1719 waveview->bbox_lry - waveview->bbox_uly,
1727 /* draw the top half */
1729 for (xoff = ulx; xoff < lrx; xoff++) {
1732 max = waveview->cache->data[cache_index].max;
1733 min = waveview->cache->data[cache_index].min;
1744 if (fabs (min) > fabs (max)) {
1749 yoff = origin - (waveview->half_height * max) + 0.5;
1752 cairo_move_to (cr, xoff+0.5, yoff);
1754 cairo_line_to (cr, xoff+0.5, yoff);
1760 /* from the final top point, move out of the clip zone */
1762 cairo_line_to (cr, xoff + 10, yoff);
1764 /* now draw the bottom half */
1766 for (--xoff, --cache_index; xoff >= ulx; --xoff) {
1769 min = waveview->cache->data[cache_index].min;
1775 yoff = origin - (waveview->half_height * min) + 0.5;
1777 cairo_line_to (cr, xoff+0.5, yoff);
1781 /* from the final lower point, move out of the clip zone */
1783 cairo_line_to (cr, xoff - 10, yoff);
1785 /* close path to fill */
1787 cairo_close_path (cr);
1789 /* fill and stroke */
1791 cairo_set_source_rgba (cr,
1792 (waveview->fill_r/255.0),
1793 (waveview->fill_g/255.0),
1794 (waveview->fill_b/255.0),
1795 (waveview->fill_a/255.0));
1796 cairo_fill_preserve (cr);
1797 cairo_set_source_rgba (cr,
1798 (waveview->wave_r/255.0),
1799 (waveview->wave_g/255.0),
1800 (waveview->wave_b/255.0),
1801 (waveview->wave_a/255.0));
1808 if (clip_max || clip_min) {
1809 cairo_set_source_rgba (cr, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a);
1813 cairo_move_to (cr, xoff, yoff1);
1814 cairo_line_to (cr, xoff, yoff1 + clip_length);
1819 cairo_move_to (cr, xoff, yoff2);
1820 cairo_line_to (cr, xoff, yoff2 - clip_length);
1827 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1829 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1834 *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1835 *y2 = *y1 + waveview->height;
1839 gnome_canvas_item_i2w (item, &x, &y);
1840 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1843 gnome_canvas_item_i2w (item, &x, &y);
1844 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1845 printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1851 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1853 /* XXX for now, point is never inside the wave
1854 GnomeCanvasWaveView *waveview;
1855 double x1, y1, x2, y2;
1862 waveview = GNOME_CANVAS_WAVEVIEW (item);
1864 *actual_item = item;
1866 /* Find the bounds for the rectangle plus its outline width */
1868 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1870 /* Is point inside rectangle */
1872 if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1876 /* Point is outside rectangle */
1892 return sqrt (dx * dx + dy * dy);