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"
33 extern void c_stacktrace();
40 PROP_SOURCEFILE_LENGTH_FUNCTION,
46 PROP_SAMPLES_PER_UNIT,
47 PROP_AMPLITUDE_ABOVE_AXIS,
57 static void gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class);
59 static void gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview);
61 static void gnome_canvas_waveview_destroy (GtkObject *object);
63 static void gnome_canvas_waveview_set_property (GObject *object,
67 static void gnome_canvas_waveview_get_property (GObject *object,
72 static void gnome_canvas_waveview_update (GnomeCanvasItem *item,
77 static void gnome_canvas_waveview_bounds (GnomeCanvasItem *item,
83 static double gnome_canvas_waveview_point (GnomeCanvasItem *item,
88 GnomeCanvasItem **actual_item);
90 static void gnome_canvas_waveview_render (GnomeCanvasItem *item,
93 static void gnome_canvas_waveview_draw (GnomeCanvasItem *item,
94 GdkDrawable *drawable,
100 static void gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *,
103 static void gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *,
106 static gint32 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview,
110 static GnomeCanvasItemClass *parent_class;
113 gnome_canvas_waveview_get_type (void)
115 static GType waveview_type;
117 if (!waveview_type) {
118 static const GTypeInfo object_info = {
119 sizeof (GnomeCanvasWaveViewClass),
120 (GBaseInitFunc) NULL,
121 (GBaseFinalizeFunc) NULL,
122 (GClassInitFunc) gnome_canvas_waveview_class_init,
123 (GClassFinalizeFunc) NULL,
124 NULL, /* class_data */
125 sizeof (GnomeCanvasWaveView),
127 (GInstanceInitFunc) gnome_canvas_waveview_init,
128 NULL /* value_table */
131 waveview_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasWaveView",
135 return waveview_type;
139 gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class)
141 GObjectClass *gobject_class;
142 GtkObjectClass *object_class;
143 GnomeCanvasItemClass *item_class;
145 gobject_class = (GObjectClass *) class;
146 object_class = (GtkObjectClass *) class;
147 item_class = (GnomeCanvasItemClass *) class;
149 parent_class = g_type_class_peek_parent (class);
151 gobject_class->set_property = gnome_canvas_waveview_set_property;
152 gobject_class->get_property = gnome_canvas_waveview_get_property;
154 g_object_class_install_property
157 g_param_spec_pointer ("data_src", NULL, NULL,
158 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
160 g_object_class_install_property
163 g_param_spec_uint ("channel", NULL, NULL,
165 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
167 g_object_class_install_property
169 PROP_LENGTH_FUNCTION,
170 g_param_spec_pointer ("length_function", NULL, NULL,
171 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
173 g_object_class_install_property
175 PROP_SOURCEFILE_LENGTH_FUNCTION,
176 g_param_spec_pointer ("sourcefile_length_function", NULL, NULL,
177 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
179 g_object_class_install_property
182 g_param_spec_pointer ("peak_function", NULL, NULL,
183 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
185 g_object_class_install_property
188 g_param_spec_pointer ("gain_function", NULL, NULL,
189 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
191 g_object_class_install_property
194 g_param_spec_pointer ("gain_src", NULL, NULL,
195 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
197 g_object_class_install_property
200 g_param_spec_pointer ("cache", NULL, NULL,
201 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
203 g_object_class_install_property
206 g_param_spec_boolean ("cache_updater", NULL, NULL,
208 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
210 g_object_class_install_property
212 PROP_SAMPLES_PER_UNIT,
213 g_param_spec_double ("samples_per_unit", NULL, NULL,
214 0.0, G_MAXDOUBLE, 0.0,
215 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
217 g_object_class_install_property
219 PROP_AMPLITUDE_ABOVE_AXIS,
220 g_param_spec_double ("amplitude_above_axis", NULL, NULL,
221 0.0, G_MAXDOUBLE, 0.0,
222 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
224 g_object_class_install_property
227 g_param_spec_double ("x", 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 ("y", 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 ("height", 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_uint ("wave_color", NULL, NULL,
250 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
252 g_object_class_install_property
255 g_param_spec_boolean ("rectified", NULL, NULL,
257 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
259 g_object_class_install_property
262 g_param_spec_boolean ("logscaled", NULL, NULL,
264 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
266 g_object_class_install_property
269 g_param_spec_uint ("region_start", NULL, NULL,
271 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
273 object_class->destroy = gnome_canvas_waveview_destroy;
275 item_class->update = gnome_canvas_waveview_update;
276 item_class->bounds = gnome_canvas_waveview_bounds;
277 item_class->point = gnome_canvas_waveview_point;
278 item_class->render = gnome_canvas_waveview_render;
279 item_class->draw = gnome_canvas_waveview_draw;
282 GnomeCanvasWaveViewCache*
283 gnome_canvas_waveview_cache_new ()
285 GnomeCanvasWaveViewCache *c;
287 c = g_malloc (sizeof (GnomeCanvasWaveViewCache));
290 c->data = g_malloc (sizeof (GnomeCanvasWaveViewCacheEntry) * c->allocated);
299 gnome_canvas_waveview_cache_destroy (GnomeCanvasWaveViewCache* cache)
301 g_free (cache->data);
306 gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview)
311 waveview->cache_updater = FALSE;
312 waveview->data_src = NULL;
313 waveview->channel = 0;
314 waveview->peak_function = NULL;
315 waveview->length_function = NULL;
316 waveview->sourcefile_length_function = NULL;
317 waveview->gain_curve_function = NULL;
318 waveview->gain_src = NULL;
319 waveview->rectified = FALSE;
320 waveview->logscaled = FALSE;
321 waveview->region_start = 0;
322 waveview->samples_per_unit = 1.0;
323 waveview->amplitude_above_axis = 1.0;
324 waveview->height = 100.0;
325 waveview->screen_width = gdk_screen_width ();
326 waveview->reload_cache_in_render = FALSE;
328 waveview->wave_color = RGBA_TO_UINT(44,35,126,255);
332 gnome_canvas_waveview_destroy (GtkObject *object)
334 GnomeCanvasWaveView *waveview;
336 g_return_if_fail (object != NULL);
337 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
339 waveview = GNOME_CANVAS_WAVEVIEW (object);
341 if (GTK_OBJECT_CLASS (parent_class)->destroy)
342 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
345 #define DEBUG_CACHE 0
346 #undef CACHE_MEMMOVE_OPTIMIZATION
349 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_sample, gulong end_sample)
351 gulong required_cache_entries;
352 gulong rf1, rf2,rf3, required_frames;
353 gulong new_cache_start, new_cache_end;
359 GnomeCanvasWaveViewCache *cache;
361 #ifdef CACHE_MEMMOVE_OPTIMIZATION
362 gulong present_frames;
363 gulong present_entries;
366 cache = waveview->cache;
368 start_sample = start_sample + waveview->region_start;
369 end_sample = end_sample + waveview->region_start;
371 // printf("waveview->region_start == %lu\n",waveview->region_start);
372 printf ("=> 0x%x cache @ 0x%x range: %lu - %lu request: %lu - %lu (%lu frames)\n",
374 cache->start, cache->end,
375 start_sample, end_sample, end_sample - start_sample);
378 if (cache->start <= start_sample && cache->end >= end_sample) {
380 // printf ("0x%x: cache hit for %lu-%lu (cache holds: %lu-%lu\n",
381 // waveview, start_sample, end_sample, cache->start, cache->end);
386 /* make sure the cache is at least twice as wide as the screen width, and put the start sample
387 in the middle, ensuring that we cover the end_sample.
390 /* Note the assumption that we have a 1:1 units:pixel ratio for the canvas. Its everywhere ... */
392 half_width = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit)/2.0 + 0.5);
394 if (start_sample < half_width) {
397 new_cache_start = start_sample - half_width;
400 /* figure out how many frames we want */
402 rf1 = end_sample - start_sample + 1;
403 rf2 = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit * 2.0f));
404 required_frames = MAX(rf1,rf2);
406 /* but make sure it doesn't extend beyond the end of the source material */
408 rf3 = (gulong) (waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit)) + 1;
409 if (rf3 < new_cache_start) {
412 rf3 -= new_cache_start;
416 fprintf (stderr, "\n\nAVAILABLE FRAMES = %lu of %lu, start = %lu, sstart = %lu, cstart = %lu\n",
417 rf3, waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit),
418 waveview->region_start, start_sample, new_cache_start);
421 required_frames = MIN(required_frames,rf3);
423 new_cache_end = new_cache_start + required_frames - 1;
425 required_cache_entries = (gulong) floor (required_frames / waveview->samples_per_unit );
428 fprintf (stderr, "new cache = %lu - %lu\n", new_cache_start, new_cache_end);
429 fprintf(stderr,"required_cach_entries = %lu, samples_per_unit = %f req frames = %lu\n",
430 required_cache_entries,waveview->samples_per_unit, required_frames);
433 if (required_cache_entries > cache->allocated) {
434 cache->data = g_realloc (cache->data, sizeof (GnomeCanvasWaveViewCacheEntry) * required_cache_entries);
435 cache->allocated = required_cache_entries;
440 ostart = new_cache_start;
442 #ifdef CACHE_MEMMOVE_OPTIMIZATION
444 /* data is not entirely in the cache, so go fetch it, making sure to fill the cache */
446 /* some of the required cache entries are in the cache, but in the wrong
447 locations. use memmove to fix this.
450 if (cache->start < new_cache_start && new_cache_start < cache->end) {
452 /* case one: the common area is at the end of the existing cache. move it
453 to the beginning of the cache, and set up to refill whatever remains.
456 wv->cache_start wv->cache_end
457 |-------------------------------------------------------| cache
458 |--------------------------------| requested
459 <------------------->
461 new_cache_start new_cache_end
465 present_frames = cache->end - new_cache_start;
466 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
469 fprintf (stderr, "existing material at end of current cache, move to start of new cache\n"
470 "\tcopy from %lu to start\n", cache->data_size - present_entries);
473 memmove (&cache->data[0],
474 &cache->data[cache->data_size - present_entries],
475 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
478 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
479 present_frames, required_frames, present_entries, new_cache_start + present_entries,
480 cache->data + present_entries);
483 copied = present_entries;
484 offset = present_entries;
485 new_cache_start += present_frames;
486 required_frames -= present_frames;
488 } else if (new_cache_end > cache->start && new_cache_end < cache->end) {
490 /* case two: the common area lives at the beginning of the existing cache.
492 wv->cache_start wv->cache_end
493 |-----------------------------------------------------|
494 |--------------------------------|
498 new_cache_start new_cache_end
501 present_frames = new_cache_end - cache->start;
502 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
504 memmove (&cache->data[cache->data_size - present_entries],
506 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
509 fprintf (stderr, "existing material at start of current cache, move to start of end cache\n");
513 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
514 present_entries, required_frames, present_entries, new_cache_start + present_entries,
515 cache->data + present_entries);
518 copied = present_entries;
520 required_frames -= present_frames;
533 #endif /* CACHE_MEMMOVE_OPTIMIZATION */
535 // fprintf(stderr,"length == %lu\n",waveview->length_function (waveview->data_src));
536 // required_frames = MIN (waveview->length_function (waveview->data_src) - new_cache_start, required_frames);
538 npeaks = (gulong) floor (required_frames / waveview->samples_per_unit);
539 required_frames = npeaks * waveview->samples_per_unit;
544 printf ("requesting %lu/%f to cover %lu-%lu at %f spu (request was %lu-%lu) into cache + %lu\n",
545 required_frames, required_frames/waveview->samples_per_unit, new_cache_start, new_cache_end,
546 waveview->samples_per_unit, start_sample, end_sample, offset);
550 // printf ("cache holds %lu entries, requesting %lu to cover %lu-%lu (request was %lu-%lu)\n",
551 // cache->data_size, npeaks, new_cache_start, new_cache_end,
552 // start_sample, end_sample);
555 if (required_frames) {
556 waveview->peak_function (waveview->data_src, npeaks, new_cache_start, required_frames, cache->data + offset, waveview->channel,waveview->samples_per_unit);
558 /* take into account any copied peaks */
565 if (npeaks < cache->allocated) {
567 fprintf (stderr, "zero fill cache for %lu at %lu\n", cache->allocated - npeaks, npeaks);
569 memset (&cache->data[npeaks], 0, sizeof (GnomeCanvasWaveViewCacheEntry) * (cache->allocated - npeaks));
570 cache->data_size = npeaks;
572 cache->data_size = cache->allocated;
575 if (waveview->gain_curve_function) {
578 gain = (float*) malloc (sizeof (float) * cache->data_size);
580 waveview->gain_curve_function (waveview->gain_src, new_cache_start, new_cache_end, gain, cache->data_size);
582 for (n = 0; n < cache->data_size; ++n) {
583 cache->data[n].min *= gain[n];
584 cache->data[n].max *= gain[n];
591 /* do optional log scaling. this implementation is not particularly efficient */
593 if (waveview->logscaled) {
595 GnomeCanvasWaveViewCacheEntry* buf = cache->data;
597 for (n = 0; n < cache->data_size; ++n) {
599 if (buf[n].max > 0.0f) {
600 buf[n].max = alt_log_meter(coefficient_to_dB(buf[n].max));
601 } else if (buf[n].max < 0.0f) {
602 buf[n].max = -alt_log_meter(coefficient_to_dB(-buf[n].max));
605 if (buf[n].min > 0.0f) {
606 buf[n].min = alt_log_meter(coefficient_to_dB(buf[n].min));
607 } else if (buf[n].min < 0.0f) {
608 buf[n].min = -alt_log_meter(coefficient_to_dB(-buf[n].min));
613 cache->start = ostart;
614 cache->end = new_cache_end;
618 fprintf (stderr, "return cache index = %d\n",
619 (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5));
621 return (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5);
626 gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *waveview, void *data_src)
629 if (waveview->cache_updater) {
630 if (waveview->data_src == data_src) {
631 waveview->reload_cache_in_render = TRUE;
635 waveview->cache->start = 0;
636 waveview->cache->end = 0;
639 waveview->data_src = data_src;
643 gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *waveview, guint32 chan)
645 if (waveview->channel == chan) {
649 waveview->channel = chan;
653 gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
656 double x1, x2, y1, y2;
659 int Ix1, Ix2, Iy1, Iy2;
662 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
669 gnome_canvas_item_i2w_affine (item, i2w);
670 art_affine_point (&w1, &i1, i2w);
671 art_affine_point (&w2, &i2, i2w);
673 Ix1 = (int) rint(w1.x);
674 Ix2 = (int) rint(w2.x);
675 Iy1 = (int) rint(w1.y);
676 Iy2 = (int) rint(w2.y);
678 gnome_canvas_update_bbox (item, Ix1, Iy1, Ix2, Iy2);
686 gnome_canvas_waveview_set_property (GObject *object,
692 GnomeCanvasItem *item;
693 GnomeCanvasWaveView *waveview;
695 int calc_bounds = FALSE;
697 g_return_if_fail (object != NULL);
698 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
700 item = GNOME_CANVAS_ITEM (object);
701 waveview = GNOME_CANVAS_WAVEVIEW (object);
705 gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
710 gnome_canvas_waveview_set_channel (waveview, g_value_get_uint(value));
714 case PROP_LENGTH_FUNCTION:
715 waveview->length_function = g_value_get_pointer(value);
718 case PROP_SOURCEFILE_LENGTH_FUNCTION:
719 waveview->sourcefile_length_function = g_value_get_pointer(value);
723 case PROP_PEAK_FUNCTION:
724 waveview->peak_function = g_value_get_pointer(value);
728 case PROP_GAIN_FUNCTION:
729 waveview->gain_curve_function = g_value_get_pointer(value);
734 waveview->gain_src = g_value_get_pointer(value);
735 if (waveview->cache_updater) {
736 waveview->cache->start = 0;
737 waveview->cache->end = 0;
744 waveview->cache = g_value_get_pointer(value);
749 case PROP_CACHE_UPDATER:
750 waveview->cache_updater = g_value_get_boolean(value);
754 case PROP_SAMPLES_PER_UNIT:
755 if ((waveview->samples_per_unit = g_value_get_double(value)) < 1.0) {
756 waveview->samples_per_unit = 1.0;
758 if (waveview->cache_updater) {
759 waveview->cache->start = 0;
760 waveview->cache->end = 0;
766 case PROP_AMPLITUDE_ABOVE_AXIS:
767 waveview->amplitude_above_axis = g_value_get_double(value);
772 if (waveview->x != g_value_get_double (value)) {
773 waveview->x = g_value_get_double (value);
779 if (waveview->y != g_value_get_double (value)) {
780 waveview->y = g_value_get_double (value);
786 if (waveview->height != fabs (g_value_get_double (value))) {
787 waveview->height = fabs (g_value_get_double (value));
792 case PROP_WAVE_COLOR:
793 if (waveview->wave_color != g_value_get_uint(value)) {
794 waveview->wave_color = g_value_get_uint(value);
800 if (waveview->rectified != g_value_get_boolean(value)) {
801 waveview->rectified = g_value_get_boolean(value);
806 if (waveview->logscaled != g_value_get_boolean(value)) {
807 waveview->logscaled = g_value_get_boolean(value);
808 if (waveview->cache_updater) {
809 waveview->cache->start = 0;
810 waveview->cache->end = 0;
816 case PROP_REGION_START:
817 waveview->region_start = g_value_get_uint(value);
828 gnome_canvas_waveview_reset_bounds (item);
832 gnome_canvas_item_request_update (item);
838 gnome_canvas_waveview_get_property (GObject *object,
845 g_return_if_fail (object != NULL);
846 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
848 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
852 g_value_set_pointer(value, waveview->data_src);
856 g_value_set_uint(value, waveview->channel);
859 case PROP_LENGTH_FUNCTION:
860 g_value_set_pointer(value, waveview->length_function);
863 case PROP_SOURCEFILE_LENGTH_FUNCTION:
864 g_value_set_pointer(value, waveview->sourcefile_length_function);
867 case PROP_PEAK_FUNCTION:
868 g_value_set_pointer(value, waveview->peak_function);
871 case PROP_GAIN_FUNCTION:
872 g_value_set_pointer(value, waveview->gain_curve_function);
876 g_value_set_pointer(value, waveview->gain_src);
880 g_value_set_pointer(value, waveview->cache);
883 case PROP_CACHE_UPDATER:
884 g_value_set_boolean(value, waveview->cache_updater);
887 case PROP_SAMPLES_PER_UNIT:
888 g_value_set_double(value, waveview->samples_per_unit);
891 case PROP_AMPLITUDE_ABOVE_AXIS:
892 g_value_set_double(value, waveview->amplitude_above_axis);
896 g_value_set_double (value, waveview->x);
900 g_value_set_double (value, waveview->y);
904 g_value_set_double (value, waveview->height);
907 case PROP_WAVE_COLOR:
908 g_value_set_uint (value, waveview->wave_color);
912 g_value_set_boolean (value, waveview->rectified);
916 g_value_set_boolean (value, waveview->logscaled);
919 case PROP_REGION_START:
920 g_value_set_uint (value, waveview->region_start);
924 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
930 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
932 GnomeCanvasWaveView *waveview;
935 waveview = GNOME_CANVAS_WAVEVIEW (item);
937 // check_cache (waveview, "start of update");
939 if (parent_class->update)
940 (* parent_class->update) (item, affine, clip_path, flags);
942 gnome_canvas_waveview_reset_bounds (item);
944 /* get the canvas coordinates of the view. Do NOT use affines
945 for this, because they do not round to the integer units used
946 by the canvas, resulting in subtle pixel-level errors later.
952 gnome_canvas_item_i2w (item, &x, &y);
953 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
955 waveview->samples = waveview->length_function (waveview->data_src);
957 x = waveview->x + (waveview->samples / waveview->samples_per_unit);
958 y = waveview->y + waveview->height;
960 gnome_canvas_item_i2w (item, &x, &y);
961 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
963 /* cache the half-height and the end point in canvas units */
965 waveview->half_height = waveview->height / 2.0;
967 /* parse the color */
969 UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
972 // check_cache (waveview, "end of update");
976 gnome_canvas_waveview_render (GnomeCanvasItem *item,
979 GnomeCanvasWaveView *waveview;
987 waveview = GNOME_CANVAS_WAVEVIEW (item);
989 // check_cache (waveview, "start of render");
991 if (parent_class->render) {
992 (*parent_class->render) (item, buf);
996 gnome_canvas_buf_ensure_buf (buf);
1000 begin = MAX(waveview->bbox_ulx,buf->rect.x0);
1002 if (waveview->bbox_lrx >= 0) {
1003 end = MIN(waveview->bbox_lrx,buf->rect.x1);
1012 s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1014 // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
1016 if (end == waveview->bbox_lrx) {
1017 /* This avoids minor rounding errors when we have the
1018 entire region visible.
1020 s2 = waveview->samples;
1022 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
1026 printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
1027 " b/e %d..%d s= %lu..%lu @ %f\n",
1038 waveview->samples_per_unit);
1041 /* now ensure that the cache is full and properly
1045 // check_cache (waveview, "pre-ensure");
1047 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1048 waveview->cache->start = 0;
1049 waveview->cache->end = 0;
1050 waveview->reload_cache_in_render = FALSE;
1053 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1055 // check_cache (waveview, "post-ensure");
1058 Now draw each line, clipping it appropriately. The clipping
1059 is done by the macros PAINT_FOO().
1062 half_height = waveview->half_height;
1064 /* this makes it slightly easier to comprehend whats going on */
1066 #define origin half_height
1068 for (x = begin; x < end; x++) {
1071 int clip_max, clip_min;
1076 max = waveview->cache->data[cache_index].max;
1077 min = waveview->cache->data[cache_index].min;
1089 /* don't rectify at single-sample zoom */
1091 if (waveview->rectified && waveview->samples_per_unit > 1) {
1093 if (fabs (min) > fabs (max)) {
1097 max = max * waveview->height;
1099 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1100 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1104 max = max * half_height;
1105 min = min * half_height;
1107 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1108 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1111 /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1112 or, if samples_per_unit == 1, then a dot at each location.
1115 if (pymax == pymin) {
1116 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1118 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1121 /* show clipped waveforms with small red lines */
1123 if (clip_max || clip_min) {
1124 clip_length = MIN(5,(waveview->height/4));
1128 PAINT_VERT(buf, 255, 0, 0, x, pymax, pymax+clip_length);
1132 PAINT_VERT(buf, 255, 0, 0, x, pymin-clip_length, pymin);
1135 /* presto, we're done */
1145 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1146 GdkDrawable *drawable,
1148 int width, int height)
1150 GnomeCanvasWaveView *waveview;
1152 waveview = GNOME_CANVAS_WAVEVIEW (item);
1154 if (parent_class->draw) {
1155 (* parent_class->draw) (item, drawable, x, y, width, height);
1158 fprintf (stderr, "please don't use the CanvasWaveView item in a non-aa Canvas\n");
1163 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1165 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1170 *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1171 *y2 = *y1 + waveview->height;
1175 gnome_canvas_item_i2w (item, &x, &y);
1176 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1179 gnome_canvas_item_i2w (item, &x, &y);
1180 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1181 printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1187 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1189 /* XXX for now, point is never inside the wave
1190 GnomeCanvasWaveView *waveview;
1191 double x1, y1, x2, y2;
1198 waveview = GNOME_CANVAS_WAVEVIEW (item);
1200 *actual_item = item;
1202 /* Find the bounds for the rectangle plus its outline width */
1204 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1206 /* Is point inside rectangle */
1208 if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1212 /* Point is outside rectangle */
1228 return sqrt (dx * dx + dy * dy);