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,
60 static void gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class);
62 static void gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview);
64 static void gnome_canvas_waveview_destroy (GtkObject *object);
66 static void gnome_canvas_waveview_set_property (GObject *object,
70 static void gnome_canvas_waveview_get_property (GObject *object,
75 static void gnome_canvas_waveview_update (GnomeCanvasItem *item,
80 static void gnome_canvas_waveview_bounds (GnomeCanvasItem *item,
86 static double gnome_canvas_waveview_point (GnomeCanvasItem *item,
91 GnomeCanvasItem **actual_item);
93 static void gnome_canvas_waveview_render (GnomeCanvasItem *item,
96 static void gnome_canvas_waveview_draw (GnomeCanvasItem *item,
97 GdkDrawable *drawable,
103 static void gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *,
106 static void gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *,
109 static gint32 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview,
113 static GnomeCanvasItemClass *parent_class;
116 gnome_canvas_waveview_get_type (void)
118 static GType waveview_type;
120 if (!waveview_type) {
121 static const GTypeInfo object_info = {
122 sizeof (GnomeCanvasWaveViewClass),
123 (GBaseInitFunc) NULL,
124 (GBaseFinalizeFunc) NULL,
125 (GClassInitFunc) gnome_canvas_waveview_class_init,
126 (GClassFinalizeFunc) NULL,
127 NULL, /* class_data */
128 sizeof (GnomeCanvasWaveView),
130 (GInstanceInitFunc) gnome_canvas_waveview_init,
131 NULL /* value_table */
134 waveview_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasWaveView",
138 return waveview_type;
142 gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class)
144 GObjectClass *gobject_class;
145 GtkObjectClass *object_class;
146 GnomeCanvasItemClass *item_class;
148 gobject_class = (GObjectClass *) class;
149 object_class = (GtkObjectClass *) class;
150 item_class = (GnomeCanvasItemClass *) class;
152 parent_class = g_type_class_peek_parent (class);
154 gobject_class->set_property = gnome_canvas_waveview_set_property;
155 gobject_class->get_property = gnome_canvas_waveview_get_property;
157 g_object_class_install_property
160 g_param_spec_pointer ("data_src", NULL, NULL,
161 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
163 g_object_class_install_property
166 g_param_spec_uint ("channel", NULL, NULL,
168 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
170 g_object_class_install_property
172 PROP_LENGTH_FUNCTION,
173 g_param_spec_pointer ("length_function", NULL, NULL,
174 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
176 g_object_class_install_property
178 PROP_SOURCEFILE_LENGTH_FUNCTION,
179 g_param_spec_pointer ("sourcefile_length_function", NULL, NULL,
180 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
182 g_object_class_install_property
185 g_param_spec_pointer ("peak_function", NULL, NULL,
186 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
188 g_object_class_install_property
191 g_param_spec_pointer ("gain_function", NULL, NULL,
192 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
194 g_object_class_install_property
197 g_param_spec_pointer ("gain_src", NULL, NULL,
198 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
200 g_object_class_install_property
203 g_param_spec_pointer ("cache", NULL, NULL,
204 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
206 g_object_class_install_property
209 g_param_spec_boolean ("cache_updater", NULL, NULL,
211 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
213 g_object_class_install_property
215 PROP_SAMPLES_PER_UNIT,
216 g_param_spec_double ("samples_per_unit", NULL, NULL,
217 0.0, G_MAXDOUBLE, 0.0,
218 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
220 g_object_class_install_property
222 PROP_AMPLITUDE_ABOVE_AXIS,
223 g_param_spec_double ("amplitude_above_axis", NULL, NULL,
224 0.0, G_MAXDOUBLE, 0.0,
225 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
227 g_object_class_install_property
230 g_param_spec_double ("x", NULL, NULL,
231 0.0, G_MAXDOUBLE, 0.0,
232 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
234 g_object_class_install_property
237 g_param_spec_double ("y", NULL, NULL,
238 0.0, G_MAXDOUBLE, 0.0,
239 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
241 g_object_class_install_property
244 g_param_spec_double ("height", NULL, NULL,
245 0.0, G_MAXDOUBLE, 0.0,
246 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
248 g_object_class_install_property
251 g_param_spec_uint ("wave_color", NULL, NULL,
253 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
255 g_object_class_install_property
258 g_param_spec_uint ("clip_color", NULL, NULL,
260 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
262 g_object_class_install_property
265 g_param_spec_uint ("zero_color", NULL, NULL,
267 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
269 g_object_class_install_property
272 g_param_spec_boolean ("rectified", NULL, NULL,
274 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
276 g_object_class_install_property
279 g_param_spec_boolean ("logscaled", NULL, NULL,
281 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
283 g_object_class_install_property
286 g_param_spec_uint ("region_start", NULL, NULL,
288 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
290 object_class->destroy = gnome_canvas_waveview_destroy;
292 item_class->update = gnome_canvas_waveview_update;
293 item_class->bounds = gnome_canvas_waveview_bounds;
294 item_class->point = gnome_canvas_waveview_point;
295 item_class->render = gnome_canvas_waveview_render;
296 item_class->draw = gnome_canvas_waveview_draw;
299 GnomeCanvasWaveViewCache*
300 gnome_canvas_waveview_cache_new ()
302 GnomeCanvasWaveViewCache *c;
304 c = g_malloc (sizeof (GnomeCanvasWaveViewCache));
307 c->data = g_malloc (sizeof (GnomeCanvasWaveViewCacheEntry) * c->allocated);
316 gnome_canvas_waveview_cache_destroy (GnomeCanvasWaveViewCache* cache)
318 g_free (cache->data);
323 gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview)
328 waveview->cache_updater = FALSE;
329 waveview->data_src = NULL;
330 waveview->channel = 0;
331 waveview->peak_function = NULL;
332 waveview->length_function = NULL;
333 waveview->sourcefile_length_function = NULL;
334 waveview->gain_curve_function = NULL;
335 waveview->gain_src = NULL;
336 waveview->rectified = FALSE;
337 waveview->logscaled = FALSE;
338 waveview->region_start = 0;
339 waveview->samples_per_unit = 1.0;
340 waveview->amplitude_above_axis = 1.0;
341 waveview->height = 100.0;
342 waveview->screen_width = gdk_screen_width ();
343 waveview->reload_cache_in_render = FALSE;
345 waveview->wave_color = RGBA_TO_UINT(44,35,126,255);
346 waveview->clip_color = RGBA_TO_UINT(44,0,0,100);
347 waveview->zero_color = RGBA_TO_UINT(44,0,128,100);
351 gnome_canvas_waveview_destroy (GtkObject *object)
353 GnomeCanvasWaveView *waveview;
355 g_return_if_fail (object != NULL);
356 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
358 waveview = GNOME_CANVAS_WAVEVIEW (object);
360 if (GTK_OBJECT_CLASS (parent_class)->destroy)
361 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
364 #define DEBUG_CACHE 0
365 #undef CACHE_MEMMOVE_OPTIMIZATION
368 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_sample, gulong end_sample)
370 gulong required_cache_entries;
371 gulong rf1, rf2,rf3, required_frames;
372 gulong new_cache_start, new_cache_end;
378 GnomeCanvasWaveViewCache *cache;
380 #ifdef CACHE_MEMMOVE_OPTIMIZATION
381 gulong present_frames;
382 gulong present_entries;
385 cache = waveview->cache;
387 start_sample = start_sample + waveview->region_start;
388 end_sample = end_sample + waveview->region_start;
390 // printf("waveview->region_start == %lu\n",waveview->region_start);
392 printf ("\n\n=> 0x%x cache @ 0x%x range: %lu - %lu request: %lu - %lu (%lu frames)\n",
394 cache->start, cache->end,
395 start_sample, end_sample, end_sample - start_sample);
398 if (cache->start <= start_sample && cache->end >= end_sample) {
400 // printf ("0x%x: cache hit for %lu-%lu (cache holds: %lu-%lu\n",
401 // waveview, start_sample, end_sample, cache->start, cache->end);
406 /* make sure the cache is at least twice as wide as the screen width, and put the start sample
407 in the middle, ensuring that we cover the end_sample.
410 /* Note the assumption that we have a 1:1 units:pixel ratio for the canvas. Its everywhere ... */
412 half_width = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit)/2.0 + 0.5);
414 if (start_sample < half_width) {
417 new_cache_start = start_sample - half_width;
420 /* figure out how many frames we want */
422 rf1 = end_sample - start_sample + 1;
423 rf2 = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit * 2.0f));
424 required_frames = MAX(rf1,rf2);
426 /* but make sure it doesn't extend beyond the end of the source material */
428 rf3 = (gulong) (waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit)) + 1;
429 if (rf3 < new_cache_start) {
432 rf3 -= new_cache_start;
436 fprintf (stderr, "AVAILABLE FRAMES = %lu of %lu, start = %lu, sstart = %lu, cstart = %lu\n",
437 rf3, waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit),
438 waveview->region_start, start_sample, new_cache_start);
441 required_frames = MIN(required_frames,rf3);
443 new_cache_end = new_cache_start + required_frames - 1;
445 required_cache_entries = (gulong) floor (required_frames / waveview->samples_per_unit );
448 fprintf (stderr, "new cache = %lu - %lu\n", new_cache_start, new_cache_end);
449 fprintf(stderr,"required_cach_entries = %lu, samples_per_unit = %f req frames = %lu\n",
450 required_cache_entries,waveview->samples_per_unit, required_frames);
453 if (required_cache_entries > cache->allocated) {
454 cache->data = g_realloc (cache->data, sizeof (GnomeCanvasWaveViewCacheEntry) * required_cache_entries);
455 cache->allocated = required_cache_entries;
460 ostart = new_cache_start;
462 #ifdef CACHE_MEMMOVE_OPTIMIZATION
464 /* data is not entirely in the cache, so go fetch it, making sure to fill the cache */
466 /* some of the required cache entries are in the cache, but in the wrong
467 locations. use memmove to fix this.
470 if (cache->start < new_cache_start && new_cache_start < cache->end) {
472 /* case one: the common area is at the end of the existing cache. move it
473 to the beginning of the cache, and set up to refill whatever remains.
476 wv->cache_start wv->cache_end
477 |-------------------------------------------------------| cache
478 |--------------------------------| requested
479 <------------------->
481 new_cache_start new_cache_end
485 present_frames = cache->end - new_cache_start;
486 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
489 fprintf (stderr, "existing material at end of current cache, move to start of new cache\n"
490 "\tcopy from %lu to start\n", cache->data_size - present_entries);
493 memmove (&cache->data[0],
494 &cache->data[cache->data_size - present_entries],
495 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
498 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
499 present_frames, required_frames, present_entries, new_cache_start + present_entries,
500 cache->data + present_entries);
503 copied = present_entries;
504 offset = present_entries;
505 new_cache_start += present_frames;
506 required_frames -= present_frames;
508 } else if (new_cache_end > cache->start && new_cache_end < cache->end) {
510 /* case two: the common area lives at the beginning of the existing cache.
512 wv->cache_start wv->cache_end
513 |-----------------------------------------------------|
514 |--------------------------------|
518 new_cache_start new_cache_end
521 present_frames = new_cache_end - cache->start;
522 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
524 memmove (&cache->data[cache->data_size - present_entries],
526 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
529 fprintf (stderr, "existing material at start of current cache, move to start of end cache\n");
533 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
534 present_entries, required_frames, present_entries, new_cache_start + present_entries,
535 cache->data + present_entries);
538 copied = present_entries;
540 required_frames -= present_frames;
553 #endif /* CACHE_MEMMOVE_OPTIMIZATION */
555 // fprintf(stderr,"length == %lu\n",waveview->length_function (waveview->data_src));
556 // required_frames = MIN (waveview->length_function (waveview->data_src) - new_cache_start, required_frames);
558 npeaks = (gulong) floor (required_frames / waveview->samples_per_unit);
559 required_frames = npeaks * waveview->samples_per_unit;
564 printf ("requesting %lu/%f to cover %lu-%lu at %f spu (request was %lu-%lu) into cache + %lu\n",
565 required_frames, required_frames/waveview->samples_per_unit, new_cache_start, new_cache_end,
566 waveview->samples_per_unit, start_sample, end_sample, offset);
570 // printf ("cache holds %lu entries, requesting %lu to cover %lu-%lu (request was %lu-%lu)\n",
571 // cache->data_size, npeaks, new_cache_start, new_cache_end,
572 // start_sample, end_sample);
575 if (required_frames) {
576 waveview->peak_function (waveview->data_src, npeaks, new_cache_start, required_frames, cache->data + offset, waveview->channel,waveview->samples_per_unit);
578 /* take into account any copied peaks */
585 if (npeaks < cache->allocated) {
587 fprintf (stderr, "zero fill cache for %lu at %lu\n", cache->allocated - npeaks, npeaks);
589 memset (&cache->data[npeaks], 0, sizeof (GnomeCanvasWaveViewCacheEntry) * (cache->allocated - npeaks));
590 cache->data_size = npeaks;
592 cache->data_size = cache->allocated;
595 if (waveview->gain_curve_function) {
598 gain = (float*) malloc (sizeof (float) * cache->data_size);
600 waveview->gain_curve_function (waveview->gain_src, new_cache_start, new_cache_end, gain, cache->data_size);
602 for (n = 0; n < cache->data_size; ++n) {
603 cache->data[n].min *= gain[n];
604 cache->data[n].max *= gain[n];
611 /* do optional log scaling. this implementation is not particularly efficient */
613 if (waveview->logscaled) {
615 GnomeCanvasWaveViewCacheEntry* buf = cache->data;
617 for (n = 0; n < cache->data_size; ++n) {
619 if (buf[n].max > 0.0f) {
620 buf[n].max = alt_log_meter(coefficient_to_dB(buf[n].max));
621 } else if (buf[n].max < 0.0f) {
622 buf[n].max = -alt_log_meter(coefficient_to_dB(-buf[n].max));
625 if (buf[n].min > 0.0f) {
626 buf[n].min = alt_log_meter(coefficient_to_dB(buf[n].min));
627 } else if (buf[n].min < 0.0f) {
628 buf[n].min = -alt_log_meter(coefficient_to_dB(-buf[n].min));
633 cache->start = ostart;
634 cache->end = new_cache_end;
638 fprintf (stderr, "return cache index = %d\n",
639 (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5));
641 return (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5);
646 gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *waveview, void *data_src)
649 if (waveview->cache_updater) {
650 if (waveview->data_src == data_src) {
651 waveview->reload_cache_in_render = TRUE;
655 waveview->cache->start = 0;
656 waveview->cache->end = 0;
659 waveview->data_src = data_src;
663 gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *waveview, guint32 chan)
665 if (waveview->channel == chan) {
669 waveview->channel = chan;
673 gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
676 double x1, x2, y1, y2;
679 int Ix1, Ix2, Iy1, Iy2;
682 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
689 gnome_canvas_item_i2w_affine (item, i2w);
690 art_affine_point (&w1, &i1, i2w);
691 art_affine_point (&w2, &i2, i2w);
693 Ix1 = (int) rint(w1.x);
694 Ix2 = (int) rint(w2.x);
695 Iy1 = (int) rint(w1.y);
696 Iy2 = (int) rint(w2.y);
698 gnome_canvas_update_bbox (item, Ix1, Iy1, Ix2, Iy2);
706 gnome_canvas_waveview_set_property (GObject *object,
712 GnomeCanvasItem *item;
713 GnomeCanvasWaveView *waveview;
715 int calc_bounds = FALSE;
717 g_return_if_fail (object != NULL);
718 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
720 item = GNOME_CANVAS_ITEM (object);
721 waveview = GNOME_CANVAS_WAVEVIEW (object);
725 gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
730 gnome_canvas_waveview_set_channel (waveview, g_value_get_uint(value));
734 case PROP_LENGTH_FUNCTION:
735 waveview->length_function = g_value_get_pointer(value);
738 case PROP_SOURCEFILE_LENGTH_FUNCTION:
739 waveview->sourcefile_length_function = g_value_get_pointer(value);
743 case PROP_PEAK_FUNCTION:
744 waveview->peak_function = g_value_get_pointer(value);
748 case PROP_GAIN_FUNCTION:
749 waveview->gain_curve_function = g_value_get_pointer(value);
754 waveview->gain_src = g_value_get_pointer(value);
755 if (waveview->cache_updater) {
756 waveview->cache->start = 0;
757 waveview->cache->end = 0;
764 waveview->cache = g_value_get_pointer(value);
769 case PROP_CACHE_UPDATER:
770 waveview->cache_updater = g_value_get_boolean(value);
774 case PROP_SAMPLES_PER_UNIT:
775 if ((waveview->samples_per_unit = g_value_get_double(value)) < 1.0) {
776 waveview->samples_per_unit = 1.0;
778 if (waveview->cache_updater) {
779 waveview->cache->start = 0;
780 waveview->cache->end = 0;
786 case PROP_AMPLITUDE_ABOVE_AXIS:
787 waveview->amplitude_above_axis = g_value_get_double(value);
792 if (waveview->x != g_value_get_double (value)) {
793 waveview->x = g_value_get_double (value);
799 if (waveview->y != g_value_get_double (value)) {
800 waveview->y = g_value_get_double (value);
806 if (waveview->height != fabs (g_value_get_double (value))) {
807 waveview->height = fabs (g_value_get_double (value));
812 case PROP_WAVE_COLOR:
813 if (waveview->wave_color != g_value_get_uint(value)) {
814 waveview->wave_color = g_value_get_uint(value);
819 case PROP_CLIP_COLOR:
820 if (waveview->clip_color != g_value_get_uint(value)) {
821 waveview->clip_color = g_value_get_uint(value);
826 case PROP_ZERO_COLOR:
827 if (waveview->zero_color != g_value_get_uint(value)) {
828 waveview->zero_color = g_value_get_uint(value);
834 if (waveview->rectified != g_value_get_boolean(value)) {
835 waveview->rectified = g_value_get_boolean(value);
840 if (waveview->logscaled != g_value_get_boolean(value)) {
841 waveview->logscaled = g_value_get_boolean(value);
842 if (waveview->cache_updater) {
843 waveview->cache->start = 0;
844 waveview->cache->end = 0;
850 case PROP_REGION_START:
851 waveview->region_start = g_value_get_uint(value);
862 gnome_canvas_waveview_reset_bounds (item);
866 gnome_canvas_item_request_update (item);
872 gnome_canvas_waveview_get_property (GObject *object,
879 g_return_if_fail (object != NULL);
880 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
882 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
886 g_value_set_pointer(value, waveview->data_src);
890 g_value_set_uint(value, waveview->channel);
893 case PROP_LENGTH_FUNCTION:
894 g_value_set_pointer(value, waveview->length_function);
897 case PROP_SOURCEFILE_LENGTH_FUNCTION:
898 g_value_set_pointer(value, waveview->sourcefile_length_function);
901 case PROP_PEAK_FUNCTION:
902 g_value_set_pointer(value, waveview->peak_function);
905 case PROP_GAIN_FUNCTION:
906 g_value_set_pointer(value, waveview->gain_curve_function);
910 g_value_set_pointer(value, waveview->gain_src);
914 g_value_set_pointer(value, waveview->cache);
917 case PROP_CACHE_UPDATER:
918 g_value_set_boolean(value, waveview->cache_updater);
921 case PROP_SAMPLES_PER_UNIT:
922 g_value_set_double(value, waveview->samples_per_unit);
925 case PROP_AMPLITUDE_ABOVE_AXIS:
926 g_value_set_double(value, waveview->amplitude_above_axis);
930 g_value_set_double (value, waveview->x);
934 g_value_set_double (value, waveview->y);
938 g_value_set_double (value, waveview->height);
941 case PROP_WAVE_COLOR:
942 g_value_set_uint (value, waveview->wave_color);
945 case PROP_CLIP_COLOR:
946 g_value_set_uint (value, waveview->clip_color);
949 case PROP_ZERO_COLOR:
950 g_value_set_uint (value, waveview->zero_color);
954 g_value_set_boolean (value, waveview->rectified);
958 g_value_set_boolean (value, waveview->logscaled);
961 case PROP_REGION_START:
962 g_value_set_uint (value, waveview->region_start);
966 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
972 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
974 GnomeCanvasWaveView *waveview;
977 waveview = GNOME_CANVAS_WAVEVIEW (item);
979 // check_cache (waveview, "start of update");
981 if (parent_class->update)
982 (* parent_class->update) (item, affine, clip_path, flags);
984 gnome_canvas_waveview_reset_bounds (item);
986 /* get the canvas coordinates of the view. Do NOT use affines
987 for this, because they do not round to the integer units used
988 by the canvas, resulting in subtle pixel-level errors later.
994 gnome_canvas_item_i2w (item, &x, &y);
995 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
997 waveview->samples = waveview->length_function (waveview->data_src);
999 x = waveview->x + (waveview->samples / waveview->samples_per_unit);
1000 y = waveview->y + waveview->height;
1002 gnome_canvas_item_i2w (item, &x, &y);
1003 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
1005 /* cache the half-height and the end point in canvas units */
1007 waveview->half_height = waveview->height / 2.0;
1009 /* parse the color */
1011 UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
1013 UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
1016 // check_cache (waveview, "end of update");
1020 gnome_canvas_waveview_render (GnomeCanvasItem *item,
1021 GnomeCanvasBuf *buf)
1023 GnomeCanvasWaveView *waveview;
1025 int clip_length = 0;
1032 waveview = GNOME_CANVAS_WAVEVIEW (item);
1034 // check_cache (waveview, "start of render");
1036 if (parent_class->render) {
1037 (*parent_class->render) (item, buf);
1041 gnome_canvas_buf_ensure_buf (buf);
1045 begin = MAX(waveview->bbox_ulx, buf->rect.x0);
1047 if (begin == waveview->bbox_ulx) {
1053 if (waveview->bbox_lrx >= 0) {
1054 end = MIN(waveview->bbox_lrx,buf->rect.x1);
1059 if (end == waveview->bbox_lrx) {
1069 s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1071 // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
1073 if (end == waveview->bbox_lrx) {
1074 /* This avoids minor rounding errors when we have the
1075 entire region visible.
1077 s2 = waveview->samples;
1079 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
1083 printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
1084 " b/e %d..%d s= %lu..%lu @ %f\n",
1095 waveview->samples_per_unit);
1098 /* now ensure that the cache is full and properly
1102 // check_cache (waveview, "pre-ensure");
1104 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1105 waveview->cache->start = 0;
1106 waveview->cache->end = 0;
1107 waveview->reload_cache_in_render = FALSE;
1110 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1112 // check_cache (waveview, "post-ensure");
1115 Now draw each line, clipping it appropriately. The clipping
1116 is done by the macros PAINT_FOO().
1119 half_height = waveview->half_height;
1121 /* this makes it slightly easier to comprehend whats going on */
1123 #define origin half_height
1126 for (x = begin; x < end; x++) {
1129 int clip_max, clip_min;
1134 max = waveview->cache->data[cache_index].max;
1135 min = waveview->cache->data[cache_index].min;
1147 /* don't rectify at single-sample zoom */
1149 if (waveview->rectified && waveview->samples_per_unit > 1) {
1151 if (fabs (min) > fabs (max)) {
1155 max = max * waveview->height;
1157 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1158 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1162 max = max * half_height;
1163 min = min * half_height;
1165 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1166 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1169 /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1170 or, if samples_per_unit == 1, then a dot at each location.
1173 if (pymax == pymin) {
1174 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1176 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1179 /* show clipped waveforms with small red lines */
1181 if (clip_max || clip_min) {
1182 clip_length = MIN(5,(waveview->height/4));
1186 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1190 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1193 /* presto, we're done */
1198 if (!waveview->rectified) {
1200 //PAINT_HORIZA(buf, waveview->zero_r, waveview->zero_g, waveview->zero_b, waveview->zero_a, begin, endi-1, origin );
1202 unsigned char zero_r, zero_g, zero_b, zero_a;
1203 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a );
1204 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1205 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, end, zeroline_y);
1212 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1213 GdkDrawable *drawable,
1215 int width, int height)
1217 GnomeCanvasWaveView *waveview;
1219 waveview = GNOME_CANVAS_WAVEVIEW (item);
1221 if (parent_class->draw) {
1222 (* parent_class->draw) (item, drawable, x, y, width, height);
1225 fprintf (stderr, "please don't use the CanvasWaveView item in a non-aa Canvas\n");
1230 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1232 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1237 *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1238 *y2 = *y1 + waveview->height;
1242 gnome_canvas_item_i2w (item, &x, &y);
1243 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1246 gnome_canvas_item_i2w (item, &x, &y);
1247 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1248 printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1254 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1256 /* XXX for now, point is never inside the wave
1257 GnomeCanvasWaveView *waveview;
1258 double x1, y1, x2, y2;
1265 waveview = GNOME_CANVAS_WAVEVIEW (item);
1267 *actual_item = item;
1269 /* Find the bounds for the rectangle plus its outline width */
1271 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1273 /* Is point inside rectangle */
1275 if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1279 /* Point is outside rectangle */
1295 return sqrt (dx * dx + dy * dy);