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>
29 #include "ardour/dB.h"
32 #include "canvas-waveview.h"
33 #include "rgb_macros.h"
35 /* POSIX guarantees casting between void* and function pointers, ISO C doesn't
36 * We can work around warnings by going one step deeper in our casts
39 #define POSIX_FUNC_PTR_CAST(type, object) *((type*) &(object))
40 #endif // _POSIX_VERSION
42 extern void c_stacktrace();
49 PROP_SOURCEFILE_LENGTH_FUNCTION,
55 PROP_SAMPLES_PER_UNIT,
56 PROP_AMPLITUDE_ABOVE_AXIS,
71 static void gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class);
73 static void gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview);
75 static void gnome_canvas_waveview_destroy (GtkObject *object);
77 static void gnome_canvas_waveview_set_property (GObject *object,
81 static void gnome_canvas_waveview_get_property (GObject *object,
86 static void gnome_canvas_waveview_update (GnomeCanvasItem *item,
91 static void gnome_canvas_waveview_bounds (GnomeCanvasItem *item,
97 static double gnome_canvas_waveview_point (GnomeCanvasItem *item,
102 GnomeCanvasItem **actual_item);
104 static void gnome_canvas_waveview_render (GnomeCanvasItem *item,
105 GnomeCanvasBuf *buf);
107 static void gnome_canvas_waveview_draw (GnomeCanvasItem *item,
108 GdkDrawable *drawable,
114 static void gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *,
117 static void gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *,
120 static guint32 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview,
124 static GnomeCanvasItemClass *parent_class;
127 gnome_canvas_waveview_get_type (void)
129 static GType waveview_type;
131 if (!waveview_type) {
132 static const GTypeInfo object_info = {
133 sizeof (GnomeCanvasWaveViewClass),
134 (GBaseInitFunc) NULL,
135 (GBaseFinalizeFunc) NULL,
136 (GClassInitFunc) gnome_canvas_waveview_class_init,
137 (GClassFinalizeFunc) NULL,
138 NULL, /* class_data */
139 sizeof (GnomeCanvasWaveView),
141 (GInstanceInitFunc) gnome_canvas_waveview_init,
142 NULL /* value_table */
145 waveview_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasWaveView",
149 return waveview_type;
153 gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class)
155 GObjectClass *gobject_class;
156 GtkObjectClass *object_class;
157 GnomeCanvasItemClass *item_class;
159 gobject_class = (GObjectClass *) class;
160 object_class = (GtkObjectClass *) class;
161 item_class = (GnomeCanvasItemClass *) class;
163 parent_class = g_type_class_peek_parent (class);
165 gobject_class->set_property = gnome_canvas_waveview_set_property;
166 gobject_class->get_property = gnome_canvas_waveview_get_property;
168 g_object_class_install_property
171 g_param_spec_pointer ("data_src", NULL, NULL,
172 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
174 g_object_class_install_property
177 g_param_spec_uint ("channel", NULL, NULL,
179 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
181 g_object_class_install_property
183 PROP_LENGTH_FUNCTION,
184 g_param_spec_pointer ("length_function", NULL, NULL,
185 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
187 g_object_class_install_property
189 PROP_SOURCEFILE_LENGTH_FUNCTION,
190 g_param_spec_pointer ("sourcefile_length_function", NULL, NULL,
191 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
193 g_object_class_install_property
196 g_param_spec_pointer ("peak_function", NULL, NULL,
197 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
199 g_object_class_install_property
202 g_param_spec_pointer ("gain_function", NULL, NULL,
203 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
205 g_object_class_install_property
208 g_param_spec_pointer ("gain_src", NULL, NULL,
209 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
211 g_object_class_install_property
214 g_param_spec_pointer ("cache", NULL, NULL,
215 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
217 g_object_class_install_property
220 g_param_spec_boolean ("cache_updater", NULL, NULL,
222 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
224 g_object_class_install_property
226 PROP_SAMPLES_PER_UNIT,
227 g_param_spec_double ("samples_per_unit", NULL, NULL,
228 0.0, G_MAXDOUBLE, 0.0,
229 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
231 g_object_class_install_property
233 PROP_AMPLITUDE_ABOVE_AXIS,
234 g_param_spec_double ("amplitude_above_axis", 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 ("x", 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 ("y", 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_double ("height", NULL, NULL,
256 0.0, G_MAXDOUBLE, 0.0,
257 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
259 g_object_class_install_property
262 g_param_spec_uint ("wave_color", NULL, NULL,
264 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
266 g_object_class_install_property
269 g_param_spec_uint ("clip_color", NULL, NULL,
271 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
273 g_object_class_install_property
276 g_param_spec_uint ("zero_color", NULL, NULL,
278 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
280 g_object_class_install_property
283 g_param_spec_uint ("fill_color", NULL, NULL,
285 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
287 g_object_class_install_property
290 g_param_spec_boolean ("filled", NULL, NULL,
292 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
294 g_object_class_install_property
297 g_param_spec_boolean ("rectified", NULL, NULL,
299 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
301 g_object_class_install_property
304 g_param_spec_boolean ("zero_line", NULL, NULL,
306 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
308 g_object_class_install_property
311 g_param_spec_boolean ("logscaled", NULL, NULL,
313 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
315 g_object_class_install_property
318 g_param_spec_uint ("region_start", NULL, NULL,
320 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
322 object_class->destroy = gnome_canvas_waveview_destroy;
324 item_class->update = gnome_canvas_waveview_update;
325 item_class->bounds = gnome_canvas_waveview_bounds;
326 item_class->point = gnome_canvas_waveview_point;
327 item_class->render = gnome_canvas_waveview_render;
328 item_class->draw = gnome_canvas_waveview_draw;
331 GnomeCanvasWaveViewCache*
332 gnome_canvas_waveview_cache_new ()
334 GnomeCanvasWaveViewCache *c;
336 c = g_malloc (sizeof (GnomeCanvasWaveViewCache));
339 c->data = g_malloc (sizeof (GnomeCanvasWaveViewCacheEntry) * c->allocated);
348 gnome_canvas_waveview_cache_destroy (GnomeCanvasWaveViewCache* cache)
350 g_free (cache->data);
355 gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview)
360 waveview->cache_updater = FALSE;
361 waveview->data_src = NULL;
362 waveview->channel = 0;
363 waveview->peak_function = NULL;
364 waveview->length_function = NULL;
365 waveview->sourcefile_length_function = NULL;
366 waveview->gain_curve_function = NULL;
367 waveview->gain_src = NULL;
368 waveview->rectified = FALSE;
369 waveview->logscaled = FALSE;
370 waveview->filled = TRUE;
371 waveview->zero_line = FALSE;
372 waveview->region_start = 0;
373 waveview->samples_per_unit = 1.0;
374 waveview->amplitude_above_axis = 1.0;
375 waveview->height = 100.0;
376 waveview->screen_width = gdk_screen_width ();
377 waveview->reload_cache_in_render = FALSE;
379 waveview->wave_color = 0;
380 waveview->clip_color = 0;
381 waveview->zero_color = 0;
382 waveview->fill_color = 0;
386 gnome_canvas_waveview_destroy (GtkObject *object)
388 g_return_if_fail (object != NULL);
389 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
391 if (GTK_OBJECT_CLASS (parent_class)->destroy)
392 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
395 #define DEBUG_CACHE 0
396 #undef CACHE_MEMMOVE_OPTIMIZATION
398 /** @return cache index of start_sample within the cache */
400 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_sample, gulong end_sample)
402 gulong required_cache_entries;
403 gulong rf1, rf2,rf3, required_frames;
404 gulong new_cache_start, new_cache_end;
410 GnomeCanvasWaveViewCache *cache;
412 #ifdef CACHE_MEMMOVE_OPTIMIZATION
413 gulong present_frames;
414 gulong present_entries;
417 cache = waveview->cache;
419 start_sample = start_sample + waveview->region_start;
420 end_sample = end_sample + waveview->region_start;
422 // printf("waveview->region_start == %lu\n",waveview->region_start);
424 printf ("\n\n=> 0x%x cache @ 0x%x range: %lu - %lu request: %lu - %lu (%lu frames)\n",
426 cache->start, cache->end,
427 start_sample, end_sample, end_sample - start_sample);
430 if (cache->start <= start_sample && cache->end >= end_sample) {
432 // printf ("0x%x: cache hit for %lu-%lu (cache holds: %lu-%lu\n",
433 // waveview, start_sample, end_sample, cache->start, cache->end);
438 /* make sure the cache is at least twice as wide as the screen width, and put the start sample
439 in the middle, ensuring that we cover the end_sample.
442 /* Note the assumption that we have a 1:1 units:pixel ratio for the canvas. Its everywhere ... */
444 half_width = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit)/2.0 + 0.5);
446 if (start_sample < half_width) {
449 new_cache_start = start_sample - half_width;
452 /* figure out how many frames we want */
454 rf1 = end_sample - start_sample + 1;
455 rf2 = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit * 2.0f));
456 required_frames = MAX(rf1,rf2);
458 /* but make sure it doesn't extend beyond the end of the source material */
460 rf3 = (gulong) (waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit)) + 1;
461 if (rf3 < new_cache_start) {
464 rf3 -= new_cache_start;
468 fprintf (stderr, "AVAILABLE FRAMES = %lu of %lu, start = %lu, sstart = %lu, cstart = %lu\n",
469 rf3, waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit),
470 waveview->region_start, start_sample, new_cache_start);
473 required_frames = MIN(required_frames,rf3);
475 new_cache_end = new_cache_start + required_frames - 1;
477 required_cache_entries = (gulong) floor (required_frames / waveview->samples_per_unit );
480 fprintf (stderr, "new cache = %lu - %lu\n", new_cache_start, new_cache_end);
481 fprintf(stderr,"required_cach_entries = %lu, samples_per_unit = %f req frames = %lu\n",
482 required_cache_entries,waveview->samples_per_unit, required_frames);
485 if (required_cache_entries > cache->allocated) {
486 cache->data = g_realloc (cache->data, sizeof (GnomeCanvasWaveViewCacheEntry) * required_cache_entries);
487 cache->allocated = required_cache_entries;
492 ostart = new_cache_start;
494 #ifdef CACHE_MEMMOVE_OPTIMIZATION
496 /* data is not entirely in the cache, so go fetch it, making sure to fill the cache */
498 /* some of the required cache entries are in the cache, but in the wrong
499 locations. use memmove to fix this.
502 if (cache->start < new_cache_start && new_cache_start < cache->end) {
504 /* case one: the common area is at the end of the existing cache. move it
505 to the beginning of the cache, and set up to refill whatever remains.
508 wv->cache_start wv->cache_end
509 |-------------------------------------------------------| cache
510 |--------------------------------| requested
511 <------------------->
513 new_cache_start new_cache_end
517 present_frames = cache->end - new_cache_start;
518 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
521 fprintf (stderr, "existing material at end of current cache, move to start of new cache\n"
522 "\tcopy from %lu to start\n", cache->data_size - present_entries);
525 memmove (&cache->data[0],
526 &cache->data[cache->data_size - present_entries],
527 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
530 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
531 present_frames, required_frames, present_entries, new_cache_start + present_entries,
532 cache->data + present_entries);
535 copied = present_entries;
536 offset = present_entries;
537 new_cache_start += present_frames;
538 required_frames -= present_frames;
540 } else if (new_cache_end > cache->start && new_cache_end < cache->end) {
542 /* case two: the common area lives at the beginning of the existing cache.
544 wv->cache_start wv->cache_end
545 |-----------------------------------------------------|
546 |--------------------------------|
550 new_cache_start new_cache_end
553 present_frames = new_cache_end - cache->start;
554 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
556 memmove (&cache->data[cache->data_size - present_entries],
558 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
561 fprintf (stderr, "existing material at start of current cache, move to start of end cache\n");
565 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
566 present_entries, required_frames, present_entries, new_cache_start + present_entries,
567 cache->data + present_entries);
570 copied = present_entries;
572 required_frames -= present_frames;
585 #endif /* CACHE_MEMMOVE_OPTIMIZATION */
587 // fprintf(stderr,"length == %lu\n",waveview->length_function (waveview->data_src));
588 // required_frames = MIN (waveview->length_function (waveview->data_src) - new_cache_start, required_frames);
590 npeaks = (gulong) floor (required_frames / waveview->samples_per_unit);
591 required_frames = npeaks * waveview->samples_per_unit;
596 printf ("requesting %lu/%f to cover %lu-%lu at %f spu (request was %lu-%lu) into cache + %lu\n",
597 required_frames, required_frames/waveview->samples_per_unit, new_cache_start, new_cache_end,
598 waveview->samples_per_unit, start_sample, end_sample, offset);
602 // printf ("cache holds %lu entries, requesting %lu to cover %lu-%lu (request was %lu-%lu)\n",
603 // cache->data_size, npeaks, new_cache_start, new_cache_end,
604 // start_sample, end_sample);
607 if (required_frames) {
608 waveview->peak_function (waveview->data_src, npeaks, new_cache_start, required_frames, cache->data + offset, waveview->channel,waveview->samples_per_unit);
610 /* take into account any copied peaks */
617 if (npeaks < cache->allocated) {
619 fprintf (stderr, "zero fill cache for %lu at %lu\n", cache->allocated - npeaks, npeaks);
621 memset (&cache->data[npeaks], 0, sizeof (GnomeCanvasWaveViewCacheEntry) * (cache->allocated - npeaks));
622 cache->data_size = npeaks;
624 cache->data_size = cache->allocated;
627 if (waveview->gain_curve_function) {
630 gain = (float*) malloc (sizeof (float) * cache->data_size);
632 waveview->gain_curve_function (waveview->gain_src, new_cache_start, new_cache_end, gain, cache->data_size);
634 for (n = 0; n < cache->data_size; ++n) {
635 cache->data[n].min *= gain[n];
636 cache->data[n].max *= gain[n];
643 /* do optional log scaling. this implementation is not particularly efficient */
645 if (waveview->logscaled) {
647 GnomeCanvasWaveViewCacheEntry* buf = cache->data;
649 for (n = 0; n < cache->data_size; ++n) {
651 if (buf[n].max > 0.0f) {
652 buf[n].max = alt_log_meter(fast_coefficient_to_dB(buf[n].max));
653 } else if (buf[n].max < 0.0f) {
654 buf[n].max = -alt_log_meter(fast_coefficient_to_dB(-buf[n].max));
657 if (buf[n].min > 0.0f) {
658 buf[n].min = alt_log_meter(fast_coefficient_to_dB(buf[n].min));
659 } else if (buf[n].min < 0.0f) {
660 buf[n].min = -alt_log_meter(fast_coefficient_to_dB(-buf[n].min));
665 cache->start = ostart;
666 cache->end = new_cache_end;
670 fprintf (stderr, "return cache index = %d\n",
671 (guint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5));
673 return (guint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5);
678 gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *waveview, void *data_src)
681 if (waveview->cache_updater) {
682 if (waveview->data_src == data_src) {
683 waveview->reload_cache_in_render = TRUE;
687 waveview->cache->start = 0;
688 waveview->cache->end = 0;
691 waveview->data_src = data_src;
695 gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *waveview, guint32 chan)
697 if (waveview->channel == chan) {
701 waveview->channel = chan;
705 gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
708 double x1, x2, y1, y2;
711 int Ix1, Ix2, Iy1, Iy2;
714 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
721 gnome_canvas_item_i2w_affine (item, i2w);
722 art_affine_point (&w1, &i1, i2w);
723 art_affine_point (&w2, &i2, i2w);
725 Ix1 = (int) rint(w1.x);
726 Ix2 = (int) rint(w2.x);
727 Iy1 = (int) rint(w1.y);
728 Iy2 = (int) rint(w2.y);
730 gnome_canvas_update_bbox (item, Ix1, Iy1, Ix2, Iy2);
738 gnome_canvas_waveview_set_property (GObject *object,
746 GnomeCanvasItem *item;
747 GnomeCanvasWaveView *waveview;
749 int calc_bounds = FALSE;
751 g_return_if_fail (object != NULL);
752 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
754 item = GNOME_CANVAS_ITEM (object);
755 waveview = GNOME_CANVAS_WAVEVIEW (object);
760 gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
765 gnome_canvas_waveview_set_channel (waveview, g_value_get_uint(value));
769 case PROP_LENGTH_FUNCTION:
770 ptr = g_value_get_pointer(value);
771 waveview->length_function = POSIX_FUNC_PTR_CAST(waveview_length_function_t, ptr);
775 case PROP_SOURCEFILE_LENGTH_FUNCTION:
776 ptr = g_value_get_pointer(value);
777 waveview->sourcefile_length_function = POSIX_FUNC_PTR_CAST(waveview_sourcefile_length_function_t, ptr);
781 case PROP_PEAK_FUNCTION:
782 ptr = g_value_get_pointer(value);
783 waveview->peak_function = POSIX_FUNC_PTR_CAST(waveview_peak_function_t, ptr);
787 case PROP_GAIN_FUNCTION:
788 ptr = g_value_get_pointer(value);
789 waveview->gain_curve_function = POSIX_FUNC_PTR_CAST(waveview_gain_curve_function_t, ptr);
794 waveview->gain_src = g_value_get_pointer(value);
795 if (waveview->cache_updater) {
796 waveview->cache->start = 0;
797 waveview->cache->end = 0;
804 waveview->cache = g_value_get_pointer(value);
809 case PROP_CACHE_UPDATER:
810 waveview->cache_updater = g_value_get_boolean(value);
814 case PROP_SAMPLES_PER_UNIT:
815 if ((waveview->samples_per_unit = g_value_get_double(value)) < 1.0) {
816 waveview->samples_per_unit = 1.0;
818 if (waveview->cache_updater) {
819 waveview->cache->start = 0;
820 waveview->cache->end = 0;
826 case PROP_AMPLITUDE_ABOVE_AXIS:
827 waveview->amplitude_above_axis = g_value_get_double(value);
832 if (waveview->x != g_value_get_double (value)) {
833 waveview->x = g_value_get_double (value);
839 if (waveview->y != g_value_get_double (value)) {
840 waveview->y = g_value_get_double (value);
846 if (waveview->height != fabs (g_value_get_double (value))) {
847 waveview->height = fabs (g_value_get_double (value));
852 case PROP_WAVE_COLOR:
853 if (waveview->wave_color != g_value_get_uint(value)) {
854 waveview->wave_color = g_value_get_uint(value);
859 case PROP_CLIP_COLOR:
860 if (waveview->clip_color != g_value_get_uint(value)) {
861 waveview->clip_color = g_value_get_uint(value);
866 case PROP_ZERO_COLOR:
867 if (waveview->zero_color != g_value_get_uint(value)) {
868 waveview->zero_color = g_value_get_uint(value);
873 case PROP_FILL_COLOR:
874 if (waveview->fill_color != g_value_get_uint(value)) {
875 waveview->fill_color = g_value_get_uint(value);
881 if (waveview->filled != g_value_get_boolean(value)) {
882 waveview->filled = g_value_get_boolean(value);
888 if (waveview->rectified != g_value_get_boolean(value)) {
889 waveview->rectified = g_value_get_boolean(value);
895 if (waveview->zero_line != g_value_get_boolean(value)) {
896 waveview->zero_line = g_value_get_boolean(value);
902 if (waveview->logscaled != g_value_get_boolean(value)) {
903 waveview->logscaled = g_value_get_boolean(value);
904 if (waveview->cache_updater) {
905 waveview->cache->start = 0;
906 waveview->cache->end = 0;
912 case PROP_REGION_START:
913 waveview->region_start = g_value_get_uint(value);
924 gnome_canvas_waveview_reset_bounds (item);
928 gnome_canvas_item_request_update (item);
934 gnome_canvas_waveview_get_property (
942 g_return_if_fail (object != NULL);
943 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
945 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
949 g_value_set_pointer(value, waveview->data_src);
953 g_value_set_uint(value, waveview->channel);
956 case PROP_LENGTH_FUNCTION:
957 g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->length_function));
960 case PROP_SOURCEFILE_LENGTH_FUNCTION:
961 g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->sourcefile_length_function));
964 case PROP_PEAK_FUNCTION:
965 g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->peak_function));
968 case PROP_GAIN_FUNCTION:
969 g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->gain_curve_function));
973 g_value_set_pointer(value, waveview->gain_src);
977 g_value_set_pointer(value, waveview->cache);
980 case PROP_CACHE_UPDATER:
981 g_value_set_boolean(value, waveview->cache_updater);
984 case PROP_SAMPLES_PER_UNIT:
985 g_value_set_double(value, waveview->samples_per_unit);
988 case PROP_AMPLITUDE_ABOVE_AXIS:
989 g_value_set_double(value, waveview->amplitude_above_axis);
993 g_value_set_double (value, waveview->x);
997 g_value_set_double (value, waveview->y);
1001 g_value_set_double (value, waveview->height);
1004 case PROP_WAVE_COLOR:
1005 g_value_set_uint (value, waveview->wave_color);
1008 case PROP_CLIP_COLOR:
1009 g_value_set_uint (value, waveview->clip_color);
1012 case PROP_ZERO_COLOR:
1013 g_value_set_uint (value, waveview->zero_color);
1016 case PROP_FILL_COLOR:
1017 g_value_set_uint (value, waveview->fill_color);
1021 g_value_set_boolean (value, waveview->filled);
1024 case PROP_RECTIFIED:
1025 g_value_set_boolean (value, waveview->rectified);
1028 case PROP_ZERO_LINE:
1029 g_value_set_boolean (value, waveview->zero_line);
1032 case PROP_LOGSCALED:
1033 g_value_set_boolean (value, waveview->logscaled);
1036 case PROP_REGION_START:
1037 g_value_set_uint (value, waveview->region_start);
1041 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1047 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1049 GnomeCanvasWaveView *waveview;
1052 waveview = GNOME_CANVAS_WAVEVIEW (item);
1054 // check_cache (waveview, "start of update");
1056 if (parent_class->update)
1057 (* parent_class->update) (item, affine, clip_path, flags);
1059 gnome_canvas_waveview_reset_bounds (item);
1061 /* get the canvas coordinates of the view. Do NOT use affines
1062 for this, because they do not round to the integer units used
1063 by the canvas, resulting in subtle pixel-level errors later.
1069 gnome_canvas_item_i2w (item, &x, &y);
1070 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
1072 waveview->samples = waveview->length_function (waveview->data_src);
1074 x = waveview->x + (waveview->samples / waveview->samples_per_unit);
1075 y = waveview->y + waveview->height;
1077 gnome_canvas_item_i2w (item, &x, &y);
1078 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
1080 /* cache the half-height and the end point in canvas units */
1082 waveview->half_height = waveview->height / 2.0;
1084 /* parse the color */
1086 UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
1088 UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
1090 UINT_TO_RGBA (waveview->fill_color, &waveview->fill_r, &waveview->fill_g, &waveview->fill_b,
1093 // check_cache (waveview, "end of update");
1097 gnome_canvas_waveview_render (GnomeCanvasItem *item,
1098 GnomeCanvasBuf *buf)
1100 GnomeCanvasWaveView *waveview;
1102 int clip_length = 0;
1109 waveview = GNOME_CANVAS_WAVEVIEW (item);
1111 // check_cache (waveview, "start of render");
1113 if (parent_class->render) {
1114 (*parent_class->render) (item, buf);
1118 gnome_canvas_buf_ensure_buf (buf);
1122 /* a "unit" means a pixel */
1124 /* begin: render start x (units) */
1125 int const begin = MAX (waveview->bbox_ulx, buf->rect.x0);
1127 /* zbegin: start x for zero line (units) */
1128 int const zbegin = (begin == waveview->bbox_ulx) ? (begin + 1) : begin;
1130 /* end: render end x (units) */
1131 int const end = (waveview->bbox_lrx >= 0) ? MIN (waveview->bbox_lrx,buf->rect.x1) : buf->rect.x1;
1133 /* zend: end x for zero-line (units) */
1134 int const zend = (end == waveview->bbox_lrx) ? (end - 1) : end;
1144 s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit);
1146 // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
1148 if (end == waveview->bbox_lrx) {
1149 /* This avoids minor rounding errors when we have the
1150 entire region visible.
1152 s2 = waveview->samples;
1154 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
1158 printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
1159 " b/e %d..%d s= %lu..%lu @ %f\n",
1170 waveview->samples_per_unit);
1173 /* now ensure that the cache is full and properly
1177 // check_cache (waveview, "pre-ensure");
1179 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1180 waveview->cache->start = 0;
1181 waveview->cache->end = 0;
1182 waveview->reload_cache_in_render = FALSE;
1185 // check_cache (waveview, "post-ensure");
1187 /* don't rectify at single-sample zoom */
1188 if (waveview->rectified && waveview->samples_per_unit > 1) {
1195 clip_length = MIN(5,(waveview->height/4));
1198 Now draw each line, clipping it appropriately. The clipping
1199 is done by the macros PAINT_FOO().
1202 half_height = waveview->half_height;
1204 /* this makes it slightly easier to comprehend whats going on */
1205 #define origin half_height
1207 if (waveview->filled && !rectify) {
1212 int next_pymin, next_pymax;
1214 int next_clip_max = 0;
1215 int next_clip_min = 0;
1217 if (s1 < waveview->samples_per_unit) {
1218 /* we haven't got a prev vars to compare with, so outline the whole line here */
1219 prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1220 prev_pymin = prev_pymax;
1223 s1 -= waveview->samples_per_unit;
1226 if(end == waveview->bbox_lrx) {
1227 /* we don't have the NEXT vars for the last sample */
1228 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1229 last_pymin = last_pymax;
1232 s2 += waveview->samples_per_unit;
1235 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1238 * Compute the variables outside the rendering rect
1240 if(prev_pymax != prev_pymin) {
1242 prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1243 prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1246 if(last_pymax != last_pymin) {
1247 /* take the index of one sample right of what we render */
1248 guint index = cache_index + (end - begin);
1250 if (index >= waveview->cache->data_size) {
1252 /* the data we want is off the end of the cache, which must mean its beyond
1253 the end of the region's source; hence the peak values are 0 */
1254 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1255 last_pymin = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1259 last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1260 last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1267 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1269 max = waveview->cache->data[cache_index].max;
1270 min = waveview->cache->data[cache_index].min;
1285 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1286 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1291 for(x = begin; x < end; ++x) {
1292 int clip_max = next_clip_max;
1293 int clip_min = next_clip_min;
1294 int fill_max, fill_min;
1301 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1302 next_pymax = last_pymax;
1303 next_pymin = last_pymin;
1308 if (cache_index < waveview->cache->data_size) {
1309 max = waveview->cache->data[cache_index].max;
1310 min = waveview->cache->data[cache_index].min;
1331 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1332 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1336 if (pymax == pymin) {
1337 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1339 if((prev_pymax < pymax && next_pymax < pymax) ||
1340 (prev_pymax == pymax && next_pymax == pymax)) {
1341 fill_max = pymax + 1;
1342 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1345 fill_max = MAX(prev_pymax, next_pymax);
1346 if(pymax == fill_max) {
1347 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1351 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1355 if((prev_pymin > pymin && next_pymin > pymin) ||
1356 (prev_pymin == pymin && next_pymin == pymin)) {
1357 fill_min = pymin - 1;
1358 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1361 fill_min = MIN(prev_pymin, next_pymin);
1362 if(pymin == fill_min) {
1363 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1366 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin);
1370 if(fill_max < fill_min) {
1371 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min);
1373 else if(fill_max == fill_min) {
1374 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1379 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1383 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1390 } else if (waveview->filled && rectify) {
1392 int prev_pymax = -1;
1393 int last_pymax = -1;
1396 int next_clip_max = 0;
1397 int next_clip_min = 0;
1399 // for rectified, this stays constant throughout the loop
1400 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1402 if(s1 < waveview->samples_per_unit) {
1403 /* we haven't got a prev vars to compare with, so outline the whole line here */
1407 s1 -= waveview->samples_per_unit;
1410 if(end == waveview->bbox_lrx) {
1411 /* we don't have the NEXT vars for the last sample */
1415 s2 += waveview->samples_per_unit;
1418 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1421 * Compute the variables outside the rendering rect
1423 if(prev_pymax < 0) {
1424 max = MIN(waveview->cache->data[cache_index].max, 1.0);
1425 min = MAX(waveview->cache->data[cache_index].min, -1.0);
1427 if (fabs (min) > fabs (max)) {
1431 prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1434 if(last_pymax < 0) {
1435 /* take the index of one sample right of what we render */
1436 int index = cache_index + (end - begin);
1438 max = MIN(waveview->cache->data[index].max, 1.0);
1439 min = MAX(waveview->cache->data[index].min, -1.0);
1441 if (fabs (min) > fabs (max)) {
1445 last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1449 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1451 max = waveview->cache->data[cache_index].max;
1452 min = waveview->cache->data[cache_index].min;
1464 if (fabs (min) > fabs (max)) {
1468 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1473 for(x = begin; x < end; ++x) {
1474 int clip_max = next_clip_max;
1475 int clip_min = next_clip_min;
1482 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1483 next_pymax = last_pymax;
1488 max = waveview->cache->data[cache_index].max;
1489 min = waveview->cache->data[cache_index].min;
1501 if (fabs (min) > fabs (max)) {
1505 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1509 if (pymax == pymin) {
1510 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1512 if((prev_pymax < pymax && next_pymax < pymax) ||
1513 (prev_pymax == pymax && next_pymax == pymax)) {
1514 fill_max = pymax + 1;
1515 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1518 fill_max = MAX(prev_pymax, next_pymax);
1519 if(pymax == fill_max) {
1520 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1524 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1528 if(fill_max < pymin) {
1529 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
1531 else if(fill_max == pymin) {
1532 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
1537 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1541 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1548 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1550 for (x = begin; x < end; x++) {
1553 int clip_max, clip_min;
1558 max = waveview->cache->data[cache_index].max;
1559 min = waveview->cache->data[cache_index].min;
1573 if (fabs (min) > fabs (max)) {
1577 max = max * waveview->height;
1579 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1580 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1584 max = max * half_height;
1585 min = min * half_height;
1587 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1588 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1591 /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1592 or, if samples_per_unit == 1, then a dot at each location.
1595 if (pymax == pymin) {
1596 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1598 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1601 /* show clipped waveforms with small red lines */
1604 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1608 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1611 /* presto, we're done */
1617 if (!waveview->rectified && waveview->zero_line && waveview->height >= 100) {
1620 unsigned char zero_r, zero_g, zero_b, zero_a;
1621 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a);
1622 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1623 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, zend, zeroline_y);
1630 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1631 GdkDrawable *drawable,
1633 int width, int height)
1635 GnomeCanvasWaveView *waveview;
1648 waveview = GNOME_CANVAS_WAVEVIEW (item);
1650 /* compute intersection of Drawable area and waveview,
1651 in canvas coordinate space
1654 if (x > waveview->bbox_ulx) {
1657 ulx = waveview->bbox_ulx;
1660 if (y > waveview->bbox_uly) {
1663 uly = waveview->bbox_uly;
1666 if (x + width > waveview->bbox_lrx) {
1667 lrx = waveview->bbox_lrx;
1672 if (y + height > waveview->bbox_lry) {
1673 lry = waveview->bbox_lry;
1678 /* figure out which samples we need for the resulting intersection */
1680 s1 = floor ((ulx - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1682 if (lrx == waveview->bbox_lrx) {
1683 /* This avoids minor rounding errors when we have the
1684 entire region visible.
1686 s2 = waveview->samples;
1688 s2 = s1 + floor ((lrx - ulx) * waveview->samples_per_unit);
1691 /* translate back to buffer coordinate space */
1698 /* don't rectify at single-sample zoom */
1699 if(waveview->rectified && waveview->samples_per_unit > 1.0) {
1705 cr = gdk_cairo_create (drawable);
1706 cairo_set_line_width (cr, 0.5);
1708 origin = waveview->bbox_uly - y + waveview->half_height;
1710 cairo_rectangle (cr, ulx, uly, lrx - ulx, lry - uly);
1713 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1714 waveview->cache->start = 0;
1715 waveview->cache->end = 0;
1716 waveview->reload_cache_in_render = FALSE;
1719 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1722 printf ("%p r (%d,%d)(%d,%d)[%d x %d] bbox (%d,%d)(%d,%d)[%d x %d]"
1723 " draw (%.1f,%.1f)(%.1f,%.1f)[%.1f x %.1f] s= %lu..%lu\n",
1734 waveview->bbox_lrx - waveview->bbox_ulx,
1735 waveview->bbox_lry - waveview->bbox_uly,
1743 /* draw the top half */
1745 for (xoff = ulx; xoff < lrx; xoff++) {
1748 max = waveview->cache->data[cache_index].max;
1749 min = waveview->cache->data[cache_index].min;
1760 if (fabs (min) > fabs (max)) {
1765 yoff = origin - (waveview->half_height * max) + 0.5;
1769 cairo_move_to (cr, xoff+0.5, yoff);
1771 cairo_line_to (cr, xoff+0.5, yoff);
1777 /* from the final top point, move out of the clip zone */
1779 cairo_line_to (cr, xoff + 10, yoff);
1781 /* now draw the bottom half */
1783 for (--xoff, --cache_index; xoff >= ulx; --xoff) {
1786 min = waveview->cache->data[cache_index].min;
1792 yoff = origin - (waveview->half_height * min) + 0.5;
1794 cairo_line_to (cr, xoff+0.5, yoff);
1798 /* from the final lower point, move out of the clip zone */
1800 cairo_line_to (cr, xoff - 10, yoff);
1802 /* close path to fill */
1804 cairo_close_path (cr);
1806 /* fill and stroke */
1808 cairo_set_source_rgba (cr,
1809 (waveview->fill_r/255.0),
1810 (waveview->fill_g/255.0),
1811 (waveview->fill_b/255.0),
1812 (waveview->fill_a/255.0));
1813 cairo_fill_preserve (cr);
1814 cairo_set_source_rgba (cr,
1815 (waveview->wave_r/255.0),
1816 (waveview->wave_g/255.0),
1817 (waveview->wave_b/255.0),
1818 (waveview->wave_a/255.0));
1825 if (clip_max || clip_min) {
1826 cairo_set_source_rgba (cr, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a);
1830 cairo_move_to (cr, xoff, yoff1);
1831 cairo_line_to (cr, xoff, yoff1 + clip_length);
1836 cairo_move_to (cr, xoff, yoff2);
1837 cairo_line_to (cr, xoff, yoff2 - clip_length);
1844 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1846 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1851 *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1852 *y2 = *y1 + waveview->height;
1856 gnome_canvas_item_i2w (item, &x, &y);
1857 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1860 gnome_canvas_item_i2w (item, &x, &y);
1861 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1862 printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1868 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1877 /* XXX for now, point is never inside the wave
1878 GnomeCanvasWaveView *waveview;
1879 double x1, y1, x2, y2;
1886 waveview = GNOME_CANVAS_WAVEVIEW (item);
1888 *actual_item = item;
1890 /* Find the bounds for the rectangle plus its outline width */
1892 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1894 /* Is point inside rectangle */
1896 if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1900 /* Point is outside rectangle */
1916 return sqrt (dx * dx + dy * dy);