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 (
930 g_return_if_fail (object != NULL);
931 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
933 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
937 g_value_set_pointer(value, waveview->data_src);
941 g_value_set_uint(value, waveview->channel);
944 case PROP_LENGTH_FUNCTION:
945 g_value_set_pointer(value, waveview->length_function);
948 case PROP_SOURCEFILE_LENGTH_FUNCTION:
949 g_value_set_pointer(value, waveview->sourcefile_length_function);
952 case PROP_PEAK_FUNCTION:
953 g_value_set_pointer(value, waveview->peak_function);
956 case PROP_GAIN_FUNCTION:
957 g_value_set_pointer(value, waveview->gain_curve_function);
961 g_value_set_pointer(value, waveview->gain_src);
965 g_value_set_pointer(value, waveview->cache);
968 case PROP_CACHE_UPDATER:
969 g_value_set_boolean(value, waveview->cache_updater);
972 case PROP_SAMPLES_PER_UNIT:
973 g_value_set_double(value, waveview->samples_per_unit);
976 case PROP_AMPLITUDE_ABOVE_AXIS:
977 g_value_set_double(value, waveview->amplitude_above_axis);
981 g_value_set_double (value, waveview->x);
985 g_value_set_double (value, waveview->y);
989 g_value_set_double (value, waveview->height);
992 case PROP_WAVE_COLOR:
993 g_value_set_uint (value, waveview->wave_color);
996 case PROP_CLIP_COLOR:
997 g_value_set_uint (value, waveview->clip_color);
1000 case PROP_ZERO_COLOR:
1001 g_value_set_uint (value, waveview->zero_color);
1004 case PROP_FILL_COLOR:
1005 g_value_set_uint (value, waveview->fill_color);
1009 g_value_set_boolean (value, waveview->filled);
1012 case PROP_RECTIFIED:
1013 g_value_set_boolean (value, waveview->rectified);
1016 case PROP_ZERO_LINE:
1017 g_value_set_boolean (value, waveview->zero_line);
1020 case PROP_LOGSCALED:
1021 g_value_set_boolean (value, waveview->logscaled);
1024 case PROP_REGION_START:
1025 g_value_set_uint (value, waveview->region_start);
1029 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1035 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1037 GnomeCanvasWaveView *waveview;
1040 waveview = GNOME_CANVAS_WAVEVIEW (item);
1042 // check_cache (waveview, "start of update");
1044 if (parent_class->update)
1045 (* parent_class->update) (item, affine, clip_path, flags);
1047 gnome_canvas_waveview_reset_bounds (item);
1049 /* get the canvas coordinates of the view. Do NOT use affines
1050 for this, because they do not round to the integer units used
1051 by the canvas, resulting in subtle pixel-level errors later.
1057 gnome_canvas_item_i2w (item, &x, &y);
1058 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
1060 waveview->samples = waveview->length_function (waveview->data_src);
1062 x = waveview->x + (waveview->samples / waveview->samples_per_unit);
1063 y = waveview->y + waveview->height;
1065 gnome_canvas_item_i2w (item, &x, &y);
1066 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
1068 /* cache the half-height and the end point in canvas units */
1070 waveview->half_height = waveview->height / 2.0;
1072 /* parse the color */
1074 UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
1076 UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
1078 UINT_TO_RGBA (waveview->fill_color, &waveview->fill_r, &waveview->fill_g, &waveview->fill_b,
1081 // check_cache (waveview, "end of update");
1085 gnome_canvas_waveview_render (GnomeCanvasItem *item,
1086 GnomeCanvasBuf *buf)
1088 GnomeCanvasWaveView *waveview;
1090 int clip_length = 0;
1098 waveview = GNOME_CANVAS_WAVEVIEW (item);
1100 // check_cache (waveview, "start of render");
1102 if (parent_class->render) {
1103 (*parent_class->render) (item, buf);
1107 gnome_canvas_buf_ensure_buf (buf);
1111 begin = MAX(waveview->bbox_ulx, buf->rect.x0);
1113 if (begin == waveview->bbox_ulx) {
1119 if (waveview->bbox_lrx >= 0) {
1120 end = MIN(waveview->bbox_lrx,buf->rect.x1);
1125 if (end == waveview->bbox_lrx) {
1135 s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1137 // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
1139 if (end == waveview->bbox_lrx) {
1140 /* This avoids minor rounding errors when we have the
1141 entire region visible.
1143 s2 = waveview->samples;
1145 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
1149 printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
1150 " b/e %d..%d s= %lu..%lu @ %f\n",
1161 waveview->samples_per_unit);
1164 /* now ensure that the cache is full and properly
1168 // check_cache (waveview, "pre-ensure");
1170 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1171 waveview->cache->start = 0;
1172 waveview->cache->end = 0;
1173 waveview->reload_cache_in_render = FALSE;
1176 // check_cache (waveview, "post-ensure");
1178 /* don't rectify at single-sample zoom */
1179 if(waveview->rectified && waveview->samples_per_unit > 1) {
1186 clip_length = MIN(5,(waveview->height/4));
1189 Now draw each line, clipping it appropriately. The clipping
1190 is done by the macros PAINT_FOO().
1193 half_height = waveview->half_height;
1195 /* this makes it slightly easier to comprehend whats going on */
1196 #define origin half_height
1198 if(waveview->filled && !rectify) {
1203 int next_pymin, next_pymax;
1205 int next_clip_max = 0;
1206 int next_clip_min = 0;
1208 if(s1 < waveview->samples_per_unit) {
1209 /* we haven't got a prev vars to compare with, so outline the whole line here */
1210 prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1211 prev_pymin = prev_pymax;
1214 s1 -= waveview->samples_per_unit;
1217 if(end == waveview->bbox_lrx) {
1218 /* we don't have the NEXT vars for the last sample */
1219 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1220 last_pymin = last_pymax;
1223 s2 += waveview->samples_per_unit;
1226 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1229 * Compute the variables outside the rendering rect
1231 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 int index = cache_index + (end - begin);
1240 last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1241 last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1245 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1247 max = waveview->cache->data[cache_index].max;
1248 min = waveview->cache->data[cache_index].min;
1263 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1264 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1269 for(x = begin; x < end; ++x) {
1270 int clip_max = next_clip_max;
1271 int clip_min = next_clip_min;
1272 int fill_max, fill_min;
1279 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1280 next_pymax = last_pymax;
1281 next_pymin = last_pymin;
1286 max = waveview->cache->data[cache_index].max;
1287 min = waveview->cache->data[cache_index].min;
1305 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1306 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1310 if (pymax == pymin) {
1311 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1313 if((prev_pymax < pymax && next_pymax < pymax) ||
1314 (prev_pymax == pymax && next_pymax == pymax)) {
1315 fill_max = pymax + 1;
1316 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1319 fill_max = MAX(prev_pymax, next_pymax);
1320 if(pymax == fill_max) {
1321 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1325 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1329 if((prev_pymin > pymin && next_pymin > pymin) ||
1330 (prev_pymin == pymin && next_pymin == pymin)) {
1331 fill_min = pymin - 1;
1332 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1335 fill_min = MIN(prev_pymin, next_pymin);
1336 if(pymin == fill_min) {
1337 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1340 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin);
1344 if(fill_max < fill_min) {
1345 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min);
1347 else if(fill_max == fill_min) {
1348 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1353 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1357 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1364 } else if (waveview->filled && rectify) {
1366 int prev_pymax = -1;
1367 int last_pymax = -1;
1370 int next_clip_max = 0;
1371 int next_clip_min = 0;
1373 // for rectified, this stays constant throughout the loop
1374 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1376 if(s1 < waveview->samples_per_unit) {
1377 /* we haven't got a prev vars to compare with, so outline the whole line here */
1381 s1 -= waveview->samples_per_unit;
1384 if(end == waveview->bbox_lrx) {
1385 /* we don't have the NEXT vars for the last sample */
1389 s2 += waveview->samples_per_unit;
1392 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1395 * Compute the variables outside the rendering rect
1397 if(prev_pymax < 0) {
1398 max = MIN(waveview->cache->data[cache_index].max, 1.0);
1399 min = MAX(waveview->cache->data[cache_index].min, -1.0);
1401 if (fabs (min) > fabs (max)) {
1405 prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1408 if(last_pymax < 0) {
1409 /* take the index of one sample right of what we render */
1410 int index = cache_index + (end - begin);
1412 max = MIN(waveview->cache->data[index].max, 1.0);
1413 min = MAX(waveview->cache->data[index].min, -1.0);
1415 if (fabs (min) > fabs (max)) {
1419 last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1423 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1425 max = waveview->cache->data[cache_index].max;
1426 min = waveview->cache->data[cache_index].min;
1438 if (fabs (min) > fabs (max)) {
1442 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1447 for(x = begin; x < end; ++x) {
1448 int clip_max = next_clip_max;
1449 int clip_min = next_clip_min;
1456 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1457 next_pymax = last_pymax;
1462 max = waveview->cache->data[cache_index].max;
1463 min = waveview->cache->data[cache_index].min;
1475 if (fabs (min) > fabs (max)) {
1479 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1483 if (pymax == pymin) {
1484 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1486 if((prev_pymax < pymax && next_pymax < pymax) ||
1487 (prev_pymax == pymax && next_pymax == pymax)) {
1488 fill_max = pymax + 1;
1489 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1492 fill_max = MAX(prev_pymax, next_pymax);
1493 if(pymax == fill_max) {
1494 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1498 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1502 if(fill_max < pymin) {
1503 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
1505 else if(fill_max == pymin) {
1506 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
1511 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1515 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1522 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1524 for (x = begin; x < end; x++) {
1527 int clip_max, clip_min;
1532 max = waveview->cache->data[cache_index].max;
1533 min = waveview->cache->data[cache_index].min;
1547 if (fabs (min) > fabs (max)) {
1551 max = max * waveview->height;
1553 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1554 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1558 max = max * half_height;
1559 min = min * half_height;
1561 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1562 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1565 /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1566 or, if samples_per_unit == 1, then a dot at each location.
1569 if (pymax == pymin) {
1570 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1572 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1575 /* show clipped waveforms with small red lines */
1578 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1582 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1585 /* presto, we're done */
1591 if (!waveview->rectified && waveview->zero_line) {
1593 //PAINT_HORIZA(buf, waveview->zero_r, waveview->zero_g, waveview->zero_b, waveview->zero_a, begin, endi-1, origin );
1595 unsigned char zero_r, zero_g, zero_b, zero_a;
1596 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a );
1597 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1598 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, end, zeroline_y);
1605 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1606 GdkDrawable *drawable,
1608 int width, int height)
1610 GnomeCanvasWaveView *waveview;
1614 double zbegin, zend;
1625 waveview = GNOME_CANVAS_WAVEVIEW (item);
1627 /* compute intersection of Drawable area and waveview,
1628 in canvas coordinate space
1631 if (x > waveview->bbox_ulx) {
1635 ulx = waveview->bbox_ulx;
1639 if (y > waveview->bbox_uly) {
1642 uly = waveview->bbox_uly;
1645 if (x + width > waveview->bbox_lrx) {
1646 lrx = waveview->bbox_lrx;
1653 if (y + height > waveview->bbox_lry) {
1654 lry = waveview->bbox_lry;
1659 /* figure out which samples we need for the resulting intersection */
1661 s1 = floor ((ulx - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1663 if (lrx == waveview->bbox_lrx) {
1664 /* This avoids minor rounding errors when we have the
1665 entire region visible.
1667 s2 = waveview->samples;
1669 s2 = s1 + floor ((lrx - ulx) * waveview->samples_per_unit);
1672 /* translate back to buffer coordinate space */
1681 /* don't rectify at single-sample zoom */
1682 if(waveview->rectified && waveview->samples_per_unit > 1.0) {
1688 clip_length = MIN(5,(waveview->height/4));
1690 cr = gdk_cairo_create (drawable);
1691 cairo_set_line_width (cr, 0.5);
1693 origin = waveview->bbox_uly - y + waveview->half_height;
1695 cairo_rectangle (cr, ulx, uly, lrx - ulx, lry - uly);
1698 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1699 waveview->cache->start = 0;
1700 waveview->cache->end = 0;
1701 waveview->reload_cache_in_render = FALSE;
1704 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1707 printf ("%p r (%d,%d)(%d,%d)[%d x %d] bbox (%d,%d)(%d,%d)[%d x %d]"
1708 " draw (%.1f,%.1f)(%.1f,%.1f)[%.1f x %.1f] s= %lu..%lu\n",
1719 waveview->bbox_lrx - waveview->bbox_ulx,
1720 waveview->bbox_lry - waveview->bbox_uly,
1728 /* draw the top half */
1730 for (xoff = ulx; xoff < lrx; xoff++) {
1733 max = waveview->cache->data[cache_index].max;
1734 min = waveview->cache->data[cache_index].min;
1745 if (fabs (min) > fabs (max)) {
1750 yoff = origin - (waveview->half_height * max) + 0.5;
1754 cairo_move_to (cr, xoff+0.5, yoff);
1756 cairo_line_to (cr, xoff+0.5, yoff);
1762 /* from the final top point, move out of the clip zone */
1764 cairo_line_to (cr, xoff + 10, yoff);
1766 /* now draw the bottom half */
1768 for (--xoff, --cache_index; xoff >= ulx; --xoff) {
1771 min = waveview->cache->data[cache_index].min;
1777 yoff = origin - (waveview->half_height * min) + 0.5;
1779 cairo_line_to (cr, xoff+0.5, yoff);
1783 /* from the final lower point, move out of the clip zone */
1785 cairo_line_to (cr, xoff - 10, yoff);
1787 /* close path to fill */
1789 cairo_close_path (cr);
1791 /* fill and stroke */
1793 cairo_set_source_rgba (cr,
1794 (waveview->fill_r/255.0),
1795 (waveview->fill_g/255.0),
1796 (waveview->fill_b/255.0),
1797 (waveview->fill_a/255.0));
1798 cairo_fill_preserve (cr);
1799 cairo_set_source_rgba (cr,
1800 (waveview->wave_r/255.0),
1801 (waveview->wave_g/255.0),
1802 (waveview->wave_b/255.0),
1803 (waveview->wave_a/255.0));
1810 if (clip_max || clip_min) {
1811 cairo_set_source_rgba (cr, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a);
1815 cairo_move_to (cr, xoff, yoff1);
1816 cairo_line_to (cr, xoff, yoff1 + clip_length);
1821 cairo_move_to (cr, xoff, yoff2);
1822 cairo_line_to (cr, xoff, yoff2 - clip_length);
1829 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1831 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1836 *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1837 *y2 = *y1 + waveview->height;
1841 gnome_canvas_item_i2w (item, &x, &y);
1842 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1845 gnome_canvas_item_i2w (item, &x, &y);
1846 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1847 printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1853 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1855 /* XXX for now, point is never inside the wave
1856 GnomeCanvasWaveView *waveview;
1857 double x1, y1, x2, y2;
1864 waveview = GNOME_CANVAS_WAVEVIEW (item);
1866 *actual_item = item;
1868 /* Find the bounds for the rectangle plus its outline width */
1870 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1872 /* Is point inside rectangle */
1874 if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1878 /* Point is outside rectangle */
1894 return sqrt (dx * dx + dy * dy);