allow zero-input (i.e. tone generator) processors to be added
[ardour2.git] / gtk2_ardour / canvas-waveview.c
blobdb95732b167de6eca3b9687b478ceb547ebf3b28
1 /*
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.
18 $Id$
21 #include <stdio.h>
22 #include <math.h>
23 #include <libgnomecanvas/libgnomecanvas.h>
24 #include <cairo.h>
25 #include <string.h>
26 #include <limits.h>
27 #include <unistd.h>
29 #include "ardour/dB.h"
31 #include "logmeter.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
38 #ifdef _POSIX_VERSION
39 #define POSIX_FUNC_PTR_CAST(type, object) *((type*) &(object))
40 #endif // _POSIX_VERSION
42 extern void c_stacktrace();
44 enum {
45 PROP_0,
46 PROP_DATA_SRC,
47 PROP_CHANNEL,
48 PROP_LENGTH_FUNCTION,
49 PROP_SOURCEFILE_LENGTH_FUNCTION,
50 PROP_PEAK_FUNCTION,
51 PROP_GAIN_FUNCTION,
52 PROP_GAIN_SRC,
53 PROP_CACHE,
54 PROP_CACHE_UPDATER,
55 PROP_SAMPLES_PER_UNIT,
56 PROP_AMPLITUDE_ABOVE_AXIS,
57 PROP_X,
58 PROP_Y,
59 PROP_HEIGHT,
60 PROP_WAVE_COLOR,
61 PROP_CLIP_COLOR,
62 PROP_ZERO_COLOR,
63 PROP_FILL_COLOR,
64 PROP_FILLED,
65 PROP_RECTIFIED,
66 PROP_ZERO_LINE,
67 PROP_REGION_START,
68 PROP_LOGSCALED,
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,
78 guint prop_id,
79 const GValue *value,
80 GParamSpec *pspec);
81 static void gnome_canvas_waveview_get_property (GObject *object,
82 guint prop_id,
83 GValue *value,
84 GParamSpec *pspec);
86 static void gnome_canvas_waveview_update (GnomeCanvasItem *item,
87 double *affine,
88 ArtSVP *clip_path,
89 int flags);
91 static void gnome_canvas_waveview_bounds (GnomeCanvasItem *item,
92 double *x1,
93 double *y1,
94 double *x2,
95 double *y2);
97 static double gnome_canvas_waveview_point (GnomeCanvasItem *item,
98 double x,
99 double y,
100 int cx,
101 int cy,
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,
109 int x,
110 int y,
111 int w,
112 int h);
114 static void gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *,
115 void *);
117 static void gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *,
118 guint32);
120 static guint32 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview,
121 gulong start_sample,
122 gulong end_sample);
124 static GnomeCanvasItemClass *parent_class;
126 GType
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),
140 0, /* n_preallocs */
141 (GInstanceInitFunc) gnome_canvas_waveview_init,
142 NULL /* value_table */
145 waveview_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasWaveView",
146 &object_info, 0);
149 return waveview_type;
152 static void
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
169 (gobject_class,
170 PROP_DATA_SRC,
171 g_param_spec_pointer ("data_src", NULL, NULL,
172 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
174 g_object_class_install_property
175 (gobject_class,
176 PROP_CHANNEL,
177 g_param_spec_uint ("channel", NULL, NULL,
178 0, G_MAXUINT, 0,
179 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
181 g_object_class_install_property
182 (gobject_class,
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
188 (gobject_class,
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
194 (gobject_class,
195 PROP_PEAK_FUNCTION,
196 g_param_spec_pointer ("peak_function", NULL, NULL,
197 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
199 g_object_class_install_property
200 (gobject_class,
201 PROP_GAIN_FUNCTION,
202 g_param_spec_pointer ("gain_function", NULL, NULL,
203 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
205 g_object_class_install_property
206 (gobject_class,
207 PROP_GAIN_SRC,
208 g_param_spec_pointer ("gain_src", NULL, NULL,
209 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
211 g_object_class_install_property
212 (gobject_class,
213 PROP_CACHE,
214 g_param_spec_pointer ("cache", NULL, NULL,
215 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
217 g_object_class_install_property
218 (gobject_class,
219 PROP_CACHE_UPDATER,
220 g_param_spec_boolean ("cache_updater", NULL, NULL,
221 FALSE,
222 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
224 g_object_class_install_property
225 (gobject_class,
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
232 (gobject_class,
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
239 (gobject_class,
240 PROP_X,
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
246 (gobject_class,
247 PROP_Y,
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
253 (gobject_class,
254 PROP_HEIGHT,
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
260 (gobject_class,
261 PROP_WAVE_COLOR,
262 g_param_spec_uint ("wave_color", NULL, NULL,
263 0, G_MAXUINT, 0,
264 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
266 g_object_class_install_property
267 (gobject_class,
268 PROP_CLIP_COLOR,
269 g_param_spec_uint ("clip_color", NULL, NULL,
270 0, G_MAXUINT, 0,
271 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
273 g_object_class_install_property
274 (gobject_class,
275 PROP_ZERO_COLOR,
276 g_param_spec_uint ("zero_color", NULL, NULL,
277 0, G_MAXUINT, 0,
278 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
280 g_object_class_install_property
281 (gobject_class,
282 PROP_FILL_COLOR,
283 g_param_spec_uint ("fill_color", NULL, NULL,
284 0, G_MAXUINT, 0,
285 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
287 g_object_class_install_property
288 (gobject_class,
289 PROP_FILLED,
290 g_param_spec_boolean ("filled", NULL, NULL,
291 FALSE,
292 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
294 g_object_class_install_property
295 (gobject_class,
296 PROP_RECTIFIED,
297 g_param_spec_boolean ("rectified", NULL, NULL,
298 FALSE,
299 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
301 g_object_class_install_property
302 (gobject_class,
303 PROP_ZERO_LINE,
304 g_param_spec_boolean ("zero_line", NULL, NULL,
305 FALSE,
306 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
308 g_object_class_install_property
309 (gobject_class,
310 PROP_LOGSCALED,
311 g_param_spec_boolean ("logscaled", NULL, NULL,
312 FALSE,
313 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
315 g_object_class_install_property
316 (gobject_class,
317 PROP_REGION_START,
318 g_param_spec_uint ("region_start", NULL, NULL,
319 0, G_MAXUINT, 0,
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));
338 c->allocated = 2048;
339 c->data = g_malloc (sizeof (GnomeCanvasWaveViewCacheEntry) * c->allocated);
340 c->data_size = 0;
341 c->start = 0;
342 c->end = 0;
344 return c;
347 void
348 gnome_canvas_waveview_cache_destroy (GnomeCanvasWaveViewCache* cache)
350 g_free (cache->data);
351 g_free (cache);
354 static void
355 gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview)
357 waveview->x = 0.0;
358 waveview->y = 0.0;
359 waveview->cache = 0;
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;
385 static void
386 gnome_canvas_waveview_destroy (GtkObject *object)
388 GnomeCanvasWaveView *waveview;
390 g_return_if_fail (object != NULL);
391 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
393 waveview = GNOME_CANVAS_WAVEVIEW (object);
395 if (GTK_OBJECT_CLASS (parent_class)->destroy)
396 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
399 #define DEBUG_CACHE 0
400 #undef CACHE_MEMMOVE_OPTIMIZATION
402 /** @return cache index of start_sample within the cache */
403 static guint32
404 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_sample, gulong end_sample)
406 gulong required_cache_entries;
407 gulong rf1, rf2,rf3, required_frames;
408 gulong new_cache_start, new_cache_end;
409 gulong half_width;
410 gulong npeaks;
411 gulong offset;
412 gulong ostart;
413 gulong copied;
414 GnomeCanvasWaveViewCache *cache;
415 float* gain;
416 #ifdef CACHE_MEMMOVE_OPTIMIZATION
417 gulong present_frames;
418 gulong present_entries;
419 #endif
421 cache = waveview->cache;
423 start_sample = start_sample + waveview->region_start;
424 end_sample = end_sample + waveview->region_start;
425 #if DEBUG_CACHE
426 // printf("waveview->region_start == %lu\n",waveview->region_start);
427 // c_stacktrace ();
428 printf ("\n\n=> 0x%x cache @ 0x%x range: %lu - %lu request: %lu - %lu (%lu frames)\n",
429 waveview, cache,
430 cache->start, cache->end,
431 start_sample, end_sample, end_sample - start_sample);
432 #endif
434 if (cache->start <= start_sample && cache->end >= end_sample) {
435 #if DEBUG_CACHE
436 // printf ("0x%x: cache hit for %lu-%lu (cache holds: %lu-%lu\n",
437 // waveview, start_sample, end_sample, cache->start, cache->end);
438 #endif
439 goto out;
442 /* make sure the cache is at least twice as wide as the screen width, and put the start sample
443 in the middle, ensuring that we cover the end_sample.
446 /* Note the assumption that we have a 1:1 units:pixel ratio for the canvas. Its everywhere ... */
448 half_width = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit)/2.0 + 0.5);
450 if (start_sample < half_width) {
451 new_cache_start = 0;
452 } else {
453 new_cache_start = start_sample - half_width;
456 /* figure out how many frames we want */
458 rf1 = end_sample - start_sample + 1;
459 rf2 = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit * 2.0f));
460 required_frames = MAX(rf1,rf2);
462 /* but make sure it doesn't extend beyond the end of the source material */
464 rf3 = (gulong) (waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit)) + 1;
465 if (rf3 < new_cache_start) {
466 rf3 = 0;
467 } else {
468 rf3 -= new_cache_start;
471 #if DEBUG_CACHE
472 fprintf (stderr, "AVAILABLE FRAMES = %lu of %lu, start = %lu, sstart = %lu, cstart = %lu\n",
473 rf3, waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit),
474 waveview->region_start, start_sample, new_cache_start);
475 #endif
477 required_frames = MIN(required_frames,rf3);
479 new_cache_end = new_cache_start + required_frames - 1;
481 required_cache_entries = (gulong) floor (required_frames / waveview->samples_per_unit );
483 #if DEBUG_CACHE
484 fprintf (stderr, "new cache = %lu - %lu\n", new_cache_start, new_cache_end);
485 fprintf(stderr,"required_cach_entries = %lu, samples_per_unit = %f req frames = %lu\n",
486 required_cache_entries,waveview->samples_per_unit, required_frames);
487 #endif
489 if (required_cache_entries > cache->allocated) {
490 cache->data = g_realloc (cache->data, sizeof (GnomeCanvasWaveViewCacheEntry) * required_cache_entries);
491 cache->allocated = required_cache_entries;
492 // cache->start = 0;
493 // cache->end = 0;
496 ostart = new_cache_start;
498 #ifdef CACHE_MEMMOVE_OPTIMIZATION
500 /* data is not entirely in the cache, so go fetch it, making sure to fill the cache */
502 /* some of the required cache entries are in the cache, but in the wrong
503 locations. use memmove to fix this.
506 if (cache->start < new_cache_start && new_cache_start < cache->end) {
508 /* case one: the common area is at the end of the existing cache. move it
509 to the beginning of the cache, and set up to refill whatever remains.
512 wv->cache_start wv->cache_end
513 |-------------------------------------------------------| cache
514 |--------------------------------| requested
515 <------------------->
516 "present"
517 new_cache_start new_cache_end
521 present_frames = cache->end - new_cache_start;
522 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
524 #if DEBUG_CACHE
525 fprintf (stderr, "existing material at end of current cache, move to start of new cache\n"
526 "\tcopy from %lu to start\n", cache->data_size - present_entries);
527 #endif
529 memmove (&cache->data[0],
530 &cache->data[cache->data_size - present_entries],
531 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
533 #if DEBUG_CACHE
534 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
535 present_frames, required_frames, present_entries, new_cache_start + present_entries,
536 cache->data + present_entries);
537 #endif
539 copied = present_entries;
540 offset = present_entries;
541 new_cache_start += present_frames;
542 required_frames -= present_frames;
544 } else if (new_cache_end > cache->start && new_cache_end < cache->end) {
546 /* case two: the common area lives at the beginning of the existing cache.
548 wv->cache_start wv->cache_end
549 |-----------------------------------------------------|
550 |--------------------------------|
551 <----------------->
552 "present"
554 new_cache_start new_cache_end
557 present_frames = new_cache_end - cache->start;
558 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
560 memmove (&cache->data[cache->data_size - present_entries],
561 &cache->data[0],
562 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
564 #if DEBUG_CACHE
565 fprintf (stderr, "existing material at start of current cache, move to start of end cache\n");
566 #endif
568 #if DEBUG_CACHE
569 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
570 present_entries, required_frames, present_entries, new_cache_start + present_entries,
571 cache->data + present_entries);
572 #endif
574 copied = present_entries;
575 offset = 0;
576 required_frames -= present_frames;
579 } else {
580 copied = 0;
581 offset = 0;
585 #else
586 copied = 0;
587 offset = 0;
589 #endif /* CACHE_MEMMOVE_OPTIMIZATION */
591 // fprintf(stderr,"length == %lu\n",waveview->length_function (waveview->data_src));
592 // required_frames = MIN (waveview->length_function (waveview->data_src) - new_cache_start, required_frames);
594 npeaks = (gulong) floor (required_frames / waveview->samples_per_unit);
595 required_frames = npeaks * waveview->samples_per_unit;
597 #if DEBUG_CACHE
600 printf ("requesting %lu/%f to cover %lu-%lu at %f spu (request was %lu-%lu) into cache + %lu\n",
601 required_frames, required_frames/waveview->samples_per_unit, new_cache_start, new_cache_end,
602 waveview->samples_per_unit, start_sample, end_sample, offset);
603 #endif
605 #if DEBUG_CACHE
606 // printf ("cache holds %lu entries, requesting %lu to cover %lu-%lu (request was %lu-%lu)\n",
607 // cache->data_size, npeaks, new_cache_start, new_cache_end,
608 // start_sample, end_sample);
609 #endif
611 if (required_frames) {
612 waveview->peak_function (waveview->data_src, npeaks, new_cache_start, required_frames, cache->data + offset, waveview->channel,waveview->samples_per_unit);
614 /* take into account any copied peaks */
616 npeaks += copied;
617 } else {
618 npeaks = copied;
621 if (npeaks < cache->allocated) {
622 #if DEBUG_CACHE
623 fprintf (stderr, "zero fill cache for %lu at %lu\n", cache->allocated - npeaks, npeaks);
624 #endif
625 memset (&cache->data[npeaks], 0, sizeof (GnomeCanvasWaveViewCacheEntry) * (cache->allocated - npeaks));
626 cache->data_size = npeaks;
627 } else {
628 cache->data_size = cache->allocated;
631 if (waveview->gain_curve_function) {
632 guint32 n;
634 gain = (float*) malloc (sizeof (float) * cache->data_size);
636 waveview->gain_curve_function (waveview->gain_src, new_cache_start, new_cache_end, gain, cache->data_size);
638 for (n = 0; n < cache->data_size; ++n) {
639 cache->data[n].min *= gain[n];
640 cache->data[n].max *= gain[n];
643 free (gain);
647 /* do optional log scaling. this implementation is not particularly efficient */
649 if (waveview->logscaled) {
650 guint32 n;
651 GnomeCanvasWaveViewCacheEntry* buf = cache->data;
653 for (n = 0; n < cache->data_size; ++n) {
655 if (buf[n].max > 0.0f) {
656 buf[n].max = alt_log_meter(fast_coefficient_to_dB(buf[n].max));
657 } else if (buf[n].max < 0.0f) {
658 buf[n].max = -alt_log_meter(fast_coefficient_to_dB(-buf[n].max));
661 if (buf[n].min > 0.0f) {
662 buf[n].min = alt_log_meter(fast_coefficient_to_dB(buf[n].min));
663 } else if (buf[n].min < 0.0f) {
664 buf[n].min = -alt_log_meter(fast_coefficient_to_dB(-buf[n].min));
669 cache->start = ostart;
670 cache->end = new_cache_end;
672 out:
673 #if DEBUG_CACHE
674 fprintf (stderr, "return cache index = %d\n",
675 (guint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5));
676 #endif
677 return (guint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5);
681 void
682 gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *waveview, void *data_src)
685 if (waveview->cache_updater) {
686 if (waveview->data_src == data_src) {
687 waveview->reload_cache_in_render = TRUE;
688 return;
691 waveview->cache->start = 0;
692 waveview->cache->end = 0;
695 waveview->data_src = data_src;
698 void
699 gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *waveview, guint32 chan)
701 if (waveview->channel == chan) {
702 return;
705 waveview->channel = chan;
708 static void
709 gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
712 double x1, x2, y1, y2;
713 ArtPoint i1, i2;
714 ArtPoint w1, w2;
715 int Ix1, Ix2, Iy1, Iy2;
716 double i2w[6];
718 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
720 i1.x = x1;
721 i1.y = y1;
722 i2.x = x2;
723 i2.y = y2;
725 gnome_canvas_item_i2w_affine (item, i2w);
726 art_affine_point (&w1, &i1, i2w);
727 art_affine_point (&w2, &i2, i2w);
729 Ix1 = (int) rint(w1.x);
730 Ix2 = (int) rint(w2.x);
731 Iy1 = (int) rint(w1.y);
732 Iy2 = (int) rint(w2.y);
734 gnome_canvas_update_bbox (item, Ix1, Iy1, Ix2, Iy2);
738 * CANVAS CALLBACKS
741 static void
742 gnome_canvas_waveview_set_property (GObject *object,
743 guint prop_id,
744 const GValue *value,
745 GParamSpec *pspec)
748 (void) pspec;
750 GnomeCanvasItem *item;
751 GnomeCanvasWaveView *waveview;
752 int redraw = FALSE;
753 int calc_bounds = FALSE;
755 g_return_if_fail (object != NULL);
756 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
758 item = GNOME_CANVAS_ITEM (object);
759 waveview = GNOME_CANVAS_WAVEVIEW (object);
761 void * ptr;
762 switch (prop_id) {
763 case PROP_DATA_SRC:
764 gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
765 redraw = TRUE;
766 break;
768 case PROP_CHANNEL:
769 gnome_canvas_waveview_set_channel (waveview, g_value_get_uint(value));
770 redraw = TRUE;
771 break;
773 case PROP_LENGTH_FUNCTION:
774 ptr = g_value_get_pointer(value);
775 waveview->length_function = POSIX_FUNC_PTR_CAST(waveview_length_function_t, ptr);
776 redraw = TRUE;
777 break;
779 case PROP_SOURCEFILE_LENGTH_FUNCTION:
780 ptr = g_value_get_pointer(value);
781 waveview->sourcefile_length_function = POSIX_FUNC_PTR_CAST(waveview_sourcefile_length_function_t, ptr);
782 redraw = TRUE;
783 break;
785 case PROP_PEAK_FUNCTION:
786 ptr = g_value_get_pointer(value);
787 waveview->peak_function = POSIX_FUNC_PTR_CAST(waveview_peak_function_t, ptr);
788 redraw = TRUE;
789 break;
791 case PROP_GAIN_FUNCTION:
792 ptr = g_value_get_pointer(value);
793 waveview->gain_curve_function = POSIX_FUNC_PTR_CAST(waveview_gain_curve_function_t, ptr);
794 redraw = TRUE;
795 break;
797 case PROP_GAIN_SRC:
798 waveview->gain_src = g_value_get_pointer(value);
799 if (waveview->cache_updater) {
800 waveview->cache->start = 0;
801 waveview->cache->end = 0;
803 redraw = TRUE;
804 calc_bounds = TRUE;
805 break;
807 case PROP_CACHE:
808 waveview->cache = g_value_get_pointer(value);
809 redraw = TRUE;
810 break;
813 case PROP_CACHE_UPDATER:
814 waveview->cache_updater = g_value_get_boolean(value);
815 redraw = TRUE;
816 break;
818 case PROP_SAMPLES_PER_UNIT:
819 if ((waveview->samples_per_unit = g_value_get_double(value)) < 1.0) {
820 waveview->samples_per_unit = 1.0;
822 if (waveview->cache_updater) {
823 waveview->cache->start = 0;
824 waveview->cache->end = 0;
826 redraw = TRUE;
827 calc_bounds = TRUE;
828 break;
830 case PROP_AMPLITUDE_ABOVE_AXIS:
831 waveview->amplitude_above_axis = g_value_get_double(value);
832 redraw = TRUE;
833 break;
835 case PROP_X:
836 if (waveview->x != g_value_get_double (value)) {
837 waveview->x = g_value_get_double (value);
838 calc_bounds = TRUE;
840 break;
842 case PROP_Y:
843 if (waveview->y != g_value_get_double (value)) {
844 waveview->y = g_value_get_double (value);
845 calc_bounds = TRUE;
847 break;
849 case PROP_HEIGHT:
850 if (waveview->height != fabs (g_value_get_double (value))) {
851 waveview->height = fabs (g_value_get_double (value));
852 redraw = TRUE;
854 break;
856 case PROP_WAVE_COLOR:
857 if (waveview->wave_color != g_value_get_uint(value)) {
858 waveview->wave_color = g_value_get_uint(value);
859 redraw = TRUE;
861 break;
863 case PROP_CLIP_COLOR:
864 if (waveview->clip_color != g_value_get_uint(value)) {
865 waveview->clip_color = g_value_get_uint(value);
866 redraw = TRUE;
868 break;
870 case PROP_ZERO_COLOR:
871 if (waveview->zero_color != g_value_get_uint(value)) {
872 waveview->zero_color = g_value_get_uint(value);
873 redraw = TRUE;
875 break;
877 case PROP_FILL_COLOR:
878 if (waveview->fill_color != g_value_get_uint(value)) {
879 waveview->fill_color = g_value_get_uint(value);
880 redraw = TRUE;
882 break;
884 case PROP_FILLED:
885 if (waveview->filled != g_value_get_boolean(value)) {
886 waveview->filled = g_value_get_boolean(value);
887 redraw = TRUE;
889 break;
891 case PROP_RECTIFIED:
892 if (waveview->rectified != g_value_get_boolean(value)) {
893 waveview->rectified = g_value_get_boolean(value);
894 redraw = TRUE;
896 break;
898 case PROP_ZERO_LINE:
899 if (waveview->zero_line != g_value_get_boolean(value)) {
900 waveview->zero_line = g_value_get_boolean(value);
901 redraw = TRUE;
903 break;
905 case PROP_LOGSCALED:
906 if (waveview->logscaled != g_value_get_boolean(value)) {
907 waveview->logscaled = g_value_get_boolean(value);
908 if (waveview->cache_updater) {
909 waveview->cache->start = 0;
910 waveview->cache->end = 0;
912 redraw = TRUE;
913 calc_bounds = TRUE;
915 break;
916 case PROP_REGION_START:
917 waveview->region_start = g_value_get_uint(value);
918 redraw = TRUE;
919 calc_bounds = TRUE;
920 break;
923 default:
924 break;
927 if (calc_bounds) {
928 gnome_canvas_waveview_reset_bounds (item);
931 if (redraw) {
932 gnome_canvas_item_request_update (item);
937 static void
938 gnome_canvas_waveview_get_property (
939 GObject *object,
940 guint prop_id,
941 GValue *value,
942 GParamSpec *pspec)
946 g_return_if_fail (object != NULL);
947 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
949 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
951 switch (prop_id) {
952 case PROP_DATA_SRC:
953 g_value_set_pointer(value, waveview->data_src);
954 break;
956 case PROP_CHANNEL:
957 g_value_set_uint(value, waveview->channel);
958 break;
960 case PROP_LENGTH_FUNCTION:
961 g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->length_function));
962 break;
964 case PROP_SOURCEFILE_LENGTH_FUNCTION:
965 g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->sourcefile_length_function));
966 break;
968 case PROP_PEAK_FUNCTION:
969 g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->peak_function));
970 break;
972 case PROP_GAIN_FUNCTION:
973 g_value_set_pointer(value, POSIX_FUNC_PTR_CAST(void*, waveview->gain_curve_function));
974 break;
976 case PROP_GAIN_SRC:
977 g_value_set_pointer(value, waveview->gain_src);
978 break;
980 case PROP_CACHE:
981 g_value_set_pointer(value, waveview->cache);
982 break;
984 case PROP_CACHE_UPDATER:
985 g_value_set_boolean(value, waveview->cache_updater);
986 break;
988 case PROP_SAMPLES_PER_UNIT:
989 g_value_set_double(value, waveview->samples_per_unit);
990 break;
992 case PROP_AMPLITUDE_ABOVE_AXIS:
993 g_value_set_double(value, waveview->amplitude_above_axis);
994 break;
996 case PROP_X:
997 g_value_set_double (value, waveview->x);
998 break;
1000 case PROP_Y:
1001 g_value_set_double (value, waveview->y);
1002 break;
1004 case PROP_HEIGHT:
1005 g_value_set_double (value, waveview->height);
1006 break;
1008 case PROP_WAVE_COLOR:
1009 g_value_set_uint (value, waveview->wave_color);
1010 break;
1012 case PROP_CLIP_COLOR:
1013 g_value_set_uint (value, waveview->clip_color);
1014 break;
1016 case PROP_ZERO_COLOR:
1017 g_value_set_uint (value, waveview->zero_color);
1018 break;
1020 case PROP_FILL_COLOR:
1021 g_value_set_uint (value, waveview->fill_color);
1022 break;
1024 case PROP_FILLED:
1025 g_value_set_boolean (value, waveview->filled);
1026 break;
1028 case PROP_RECTIFIED:
1029 g_value_set_boolean (value, waveview->rectified);
1030 break;
1032 case PROP_ZERO_LINE:
1033 g_value_set_boolean (value, waveview->zero_line);
1034 break;
1036 case PROP_LOGSCALED:
1037 g_value_set_boolean (value, waveview->logscaled);
1038 break;
1040 case PROP_REGION_START:
1041 g_value_set_uint (value, waveview->region_start);
1042 break;
1044 default:
1045 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1046 break;
1050 static void
1051 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1053 GnomeCanvasWaveView *waveview;
1054 double x, y;
1056 waveview = GNOME_CANVAS_WAVEVIEW (item);
1058 // check_cache (waveview, "start of update");
1060 if (parent_class->update)
1061 (* parent_class->update) (item, affine, clip_path, flags);
1063 gnome_canvas_waveview_reset_bounds (item);
1065 /* get the canvas coordinates of the view. Do NOT use affines
1066 for this, because they do not round to the integer units used
1067 by the canvas, resulting in subtle pixel-level errors later.
1070 x = waveview->x;
1071 y = waveview->y;
1073 gnome_canvas_item_i2w (item, &x, &y);
1074 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
1076 waveview->samples = waveview->length_function (waveview->data_src);
1078 x = waveview->x + (waveview->samples / waveview->samples_per_unit);
1079 y = waveview->y + waveview->height;
1081 gnome_canvas_item_i2w (item, &x, &y);
1082 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
1084 /* cache the half-height and the end point in canvas units */
1086 waveview->half_height = waveview->height / 2.0;
1088 /* parse the color */
1090 UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
1091 &waveview->wave_a);
1092 UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
1093 &waveview->clip_a);
1094 UINT_TO_RGBA (waveview->fill_color, &waveview->fill_r, &waveview->fill_g, &waveview->fill_b,
1095 &waveview->fill_a);
1097 // check_cache (waveview, "end of update");
1100 static void
1101 gnome_canvas_waveview_render (GnomeCanvasItem *item,
1102 GnomeCanvasBuf *buf)
1104 GnomeCanvasWaveView *waveview;
1105 gulong s1, s2;
1106 int clip_length = 0;
1107 int pymin, pymax;
1108 guint cache_index;
1109 double half_height;
1110 int x;
1111 char rectify;
1113 waveview = GNOME_CANVAS_WAVEVIEW (item);
1115 // check_cache (waveview, "start of render");
1117 if (parent_class->render) {
1118 (*parent_class->render) (item, buf);
1121 if (buf->is_bg) {
1122 gnome_canvas_buf_ensure_buf (buf);
1123 buf->is_bg = FALSE;
1126 /* a "unit" means a pixel */
1128 /* begin: render start x (units) */
1129 int const begin = MAX (waveview->bbox_ulx, buf->rect.x0);
1131 /* zbegin: start x for zero line (units) */
1132 int const zbegin = (begin == waveview->bbox_ulx) ? (begin + 1) : begin;
1134 /* end: render end x (units) */
1135 int const end = (waveview->bbox_lrx >= 0) ? MIN (waveview->bbox_lrx,buf->rect.x1) : buf->rect.x1;
1137 /* zend: end x for zero-line (units) */
1138 int const zend = (end == waveview->bbox_lrx) ? (end - 1) : end;
1140 if (begin == end) {
1141 return;
1144 /* s1: start sample
1145 s2: end sample
1148 s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit);
1150 // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
1152 if (end == waveview->bbox_lrx) {
1153 /* This avoids minor rounding errors when we have the
1154 entire region visible.
1156 s2 = waveview->samples;
1157 } else {
1158 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
1161 #if 0
1162 printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
1163 " b/e %d..%d s= %lu..%lu @ %f\n",
1164 waveview,
1165 buf->rect.x0,
1166 buf->rect.x1,
1167 buf->rect.y0,
1168 buf->rect.y1,
1169 waveview->bbox_ulx,
1170 waveview->bbox_lrx,
1171 waveview->bbox_uly,
1172 waveview->bbox_lry,
1173 begin, end, s1, s2,
1174 waveview->samples_per_unit);
1175 #endif
1177 /* now ensure that the cache is full and properly
1178 positioned.
1181 // check_cache (waveview, "pre-ensure");
1183 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1184 waveview->cache->start = 0;
1185 waveview->cache->end = 0;
1186 waveview->reload_cache_in_render = FALSE;
1189 // check_cache (waveview, "post-ensure");
1191 /* don't rectify at single-sample zoom */
1192 if (waveview->rectified && waveview->samples_per_unit > 1) {
1193 rectify = TRUE;
1195 else {
1196 rectify = FALSE;
1199 clip_length = MIN(5,(waveview->height/4));
1202 Now draw each line, clipping it appropriately. The clipping
1203 is done by the macros PAINT_FOO().
1206 half_height = waveview->half_height;
1208 /* this makes it slightly easier to comprehend whats going on */
1209 #define origin half_height
1211 if (waveview->filled && !rectify) {
1212 int prev_pymin = 1;
1213 int prev_pymax = 0;
1214 int last_pymin = 1;
1215 int last_pymax = 0;
1216 int next_pymin, next_pymax;
1217 double max, min;
1218 int next_clip_max = 0;
1219 int next_clip_min = 0;
1221 if (s1 < waveview->samples_per_unit) {
1222 /* we haven't got a prev vars to compare with, so outline the whole line here */
1223 prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1224 prev_pymin = prev_pymax;
1226 else {
1227 s1 -= waveview->samples_per_unit;
1230 if(end == waveview->bbox_lrx) {
1231 /* we don't have the NEXT vars for the last sample */
1232 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1233 last_pymin = last_pymax;
1235 else {
1236 s2 += waveview->samples_per_unit;
1239 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1242 * Compute the variables outside the rendering rect
1244 if(prev_pymax != prev_pymin) {
1246 prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1247 prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1248 ++cache_index;
1250 if(last_pymax != last_pymin) {
1251 /* take the index of one sample right of what we render */
1252 guint index = cache_index + (end - begin);
1254 if (index >= waveview->cache->data_size) {
1256 /* the data we want is off the end of the cache, which must mean its beyond
1257 the end of the region's source; hence the peak values are 0 */
1258 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1259 last_pymin = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1261 } else {
1263 last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1264 last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1271 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1273 max = waveview->cache->data[cache_index].max;
1274 min = waveview->cache->data[cache_index].min;
1276 if (max >= 1.0) {
1277 max = 1.0;
1278 next_clip_max = 1;
1281 if (min <= -1.0) {
1282 min = -1.0;
1283 next_clip_min = 1;
1286 max *= half_height;
1287 min *= half_height;
1289 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1290 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1293 * And now the loop
1295 for(x = begin; x < end; ++x) {
1296 int clip_max = next_clip_max;
1297 int clip_min = next_clip_min;
1298 int fill_max, fill_min;
1300 pymax = next_pymax;
1301 pymin = next_pymin;
1303 /* compute next */
1304 if(x == end - 1) {
1305 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1306 next_pymax = last_pymax;
1307 next_pymin = last_pymin;
1309 else {
1310 ++cache_index;
1312 if (cache_index < waveview->cache->data_size) {
1313 max = waveview->cache->data[cache_index].max;
1314 min = waveview->cache->data[cache_index].min;
1315 } else {
1316 max = min = 0;
1319 next_clip_max = 0;
1320 next_clip_min = 0;
1322 if (max >= 1.0) {
1323 max = 1.0;
1324 next_clip_max = 1;
1327 if (min <= -1.0) {
1328 min = -1.0;
1329 next_clip_min = 1;
1332 max *= half_height;
1333 min *= half_height;
1335 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1336 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1339 /* render */
1340 if (pymax == pymin) {
1341 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1342 } else {
1343 if((prev_pymax < pymax && next_pymax < pymax) ||
1344 (prev_pymax == pymax && next_pymax == pymax)) {
1345 fill_max = pymax + 1;
1346 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1348 else {
1349 fill_max = MAX(prev_pymax, next_pymax);
1350 if(pymax == fill_max) {
1351 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1352 ++fill_max;
1354 else {
1355 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1359 if((prev_pymin > pymin && next_pymin > pymin) ||
1360 (prev_pymin == pymin && next_pymin == pymin)) {
1361 fill_min = pymin - 1;
1362 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1364 else {
1365 fill_min = MIN(prev_pymin, next_pymin);
1366 if(pymin == fill_min) {
1367 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1369 else {
1370 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin);
1374 if(fill_max < fill_min) {
1375 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min);
1377 else if(fill_max == fill_min) {
1378 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1382 if (clip_max) {
1383 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1386 if (clip_min) {
1387 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1390 prev_pymax = pymax;
1391 prev_pymin = pymin;
1394 } else if (waveview->filled && rectify) {
1396 int prev_pymax = -1;
1397 int last_pymax = -1;
1398 int next_pymax;
1399 double max, min;
1400 int next_clip_max = 0;
1401 int next_clip_min = 0;
1403 // for rectified, this stays constant throughout the loop
1404 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1406 if(s1 < waveview->samples_per_unit) {
1407 /* we haven't got a prev vars to compare with, so outline the whole line here */
1408 prev_pymax = pymin;
1410 else {
1411 s1 -= waveview->samples_per_unit;
1414 if(end == waveview->bbox_lrx) {
1415 /* we don't have the NEXT vars for the last sample */
1416 last_pymax = pymin;
1418 else {
1419 s2 += waveview->samples_per_unit;
1422 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1425 * Compute the variables outside the rendering rect
1427 if(prev_pymax < 0) {
1428 max = MIN(waveview->cache->data[cache_index].max, 1.0);
1429 min = MAX(waveview->cache->data[cache_index].min, -1.0);
1431 if (fabs (min) > fabs (max)) {
1432 max = fabs (min);
1435 prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1436 ++cache_index;
1438 if(last_pymax < 0) {
1439 /* take the index of one sample right of what we render */
1440 int index = cache_index + (end - begin);
1442 max = MIN(waveview->cache->data[index].max, 1.0);
1443 min = MAX(waveview->cache->data[index].min, -1.0);
1445 if (fabs (min) > fabs (max)) {
1446 max = fabs (min);
1449 last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1453 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1455 max = waveview->cache->data[cache_index].max;
1456 min = waveview->cache->data[cache_index].min;
1458 if (max >= 1.0) {
1459 max = 1.0;
1460 next_clip_max = 1;
1463 if (min <= -1.0) {
1464 min = -1.0;
1465 next_clip_min = 1;
1468 if (fabs (min) > fabs (max)) {
1469 max = fabs (min);
1472 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1475 * And now the loop
1477 for(x = begin; x < end; ++x) {
1478 int clip_max = next_clip_max;
1479 int clip_min = next_clip_min;
1480 int fill_max;
1482 pymax = next_pymax;
1484 /* compute next */
1485 if(x == end - 1) {
1486 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1487 next_pymax = last_pymax;
1489 else {
1490 ++cache_index;
1492 max = waveview->cache->data[cache_index].max;
1493 min = waveview->cache->data[cache_index].min;
1495 if (max >= 1.0) {
1496 max = 1.0;
1497 next_clip_max = 1;
1500 if (min <= -1.0) {
1501 min = -1.0;
1502 next_clip_min = 1;
1505 if (fabs (min) > fabs (max)) {
1506 max = fabs (min);
1509 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1512 /* render */
1513 if (pymax == pymin) {
1514 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1515 } else {
1516 if((prev_pymax < pymax && next_pymax < pymax) ||
1517 (prev_pymax == pymax && next_pymax == pymax)) {
1518 fill_max = pymax + 1;
1519 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1521 else {
1522 fill_max = MAX(prev_pymax, next_pymax);
1523 if(pymax == fill_max) {
1524 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1525 ++fill_max;
1527 else {
1528 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1532 if(fill_max < pymin) {
1533 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
1535 else if(fill_max == pymin) {
1536 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
1540 if (clip_max) {
1541 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1544 if (clip_min) {
1545 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1548 prev_pymax = pymax;
1551 else {
1552 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1554 for (x = begin; x < end; x++) {
1556 double max, min;
1557 int clip_max, clip_min;
1559 clip_max = 0;
1560 clip_min = 0;
1562 max = waveview->cache->data[cache_index].max;
1563 min = waveview->cache->data[cache_index].min;
1565 if (max >= 1.0) {
1566 max = 1.0;
1567 clip_max = 1;
1570 if (min <= -1.0) {
1571 min = -1.0;
1572 clip_min = 1;
1575 if (rectify) {
1577 if (fabs (min) > fabs (max)) {
1578 max = fabs (min);
1581 max = max * waveview->height;
1583 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1584 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1586 } else {
1588 max = max * half_height;
1589 min = min * half_height;
1591 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1592 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1595 /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1596 or, if samples_per_unit == 1, then a dot at each location.
1599 if (pymax == pymin) {
1600 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1601 } else {
1602 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1605 /* show clipped waveforms with small red lines */
1607 if (clip_max) {
1608 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1611 if (clip_min) {
1612 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1615 /* presto, we're done */
1617 cache_index++;
1621 if (!waveview->rectified && waveview->zero_line) {
1622 // Paint zeroline.
1623 //PAINT_HORIZA(buf, waveview->zero_r, waveview->zero_g, waveview->zero_b, waveview->zero_a, begin, endi-1, origin );
1625 unsigned char zero_r, zero_g, zero_b, zero_a;
1626 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a );
1627 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1628 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, zend, zeroline_y);
1630 #undef origin
1634 static void
1635 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1636 GdkDrawable *drawable,
1637 int x, int y,
1638 int width, int height)
1640 GnomeCanvasWaveView *waveview;
1641 cairo_t* cr;
1642 gulong s1, s2;
1643 int cache_index;
1644 gboolean rectify;
1645 double origin;
1646 double clip_length;
1647 double xoff;
1648 double yoff = 0.0;
1649 double ulx;
1650 double uly;
1651 double lrx;
1652 double lry;
1654 waveview = GNOME_CANVAS_WAVEVIEW (item);
1656 /* compute intersection of Drawable area and waveview,
1657 in canvas coordinate space
1660 if (x > waveview->bbox_ulx) {
1661 ulx = x;
1662 } else {
1663 ulx = waveview->bbox_ulx;
1666 if (y > waveview->bbox_uly) {
1667 uly = y;
1668 } else {
1669 uly = waveview->bbox_uly;
1672 if (x + width > waveview->bbox_lrx) {
1673 lrx = waveview->bbox_lrx;
1674 } else {
1675 lrx = x + width;
1678 if (y + height > waveview->bbox_lry) {
1679 lry = waveview->bbox_lry;
1680 } else {
1681 lry = y + height;
1684 /* figure out which samples we need for the resulting intersection */
1686 s1 = floor ((ulx - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1688 if (lrx == waveview->bbox_lrx) {
1689 /* This avoids minor rounding errors when we have the
1690 entire region visible.
1692 s2 = waveview->samples;
1693 } else {
1694 s2 = s1 + floor ((lrx - ulx) * waveview->samples_per_unit);
1697 /* translate back to buffer coordinate space */
1699 ulx -= x;
1700 uly -= y;
1701 lrx -= x;
1702 lry -= y;
1704 /* don't rectify at single-sample zoom */
1705 if(waveview->rectified && waveview->samples_per_unit > 1.0) {
1706 rectify = TRUE;
1707 } else {
1708 rectify = FALSE;
1711 clip_length = MIN(5,(waveview->height/4));
1713 cr = gdk_cairo_create (drawable);
1714 cairo_set_line_width (cr, 0.5);
1716 origin = waveview->bbox_uly - y + waveview->half_height;
1718 cairo_rectangle (cr, ulx, uly, lrx - ulx, lry - uly);
1719 cairo_clip (cr);
1721 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1722 waveview->cache->start = 0;
1723 waveview->cache->end = 0;
1724 waveview->reload_cache_in_render = FALSE;
1727 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1729 #if 0
1730 printf ("%p r (%d,%d)(%d,%d)[%d x %d] bbox (%d,%d)(%d,%d)[%d x %d]"
1731 " draw (%.1f,%.1f)(%.1f,%.1f)[%.1f x %.1f] s= %lu..%lu\n",
1732 waveview,
1733 x, y,
1734 x + width,
1735 y + height,
1736 width,
1737 height,
1738 waveview->bbox_ulx,
1739 waveview->bbox_uly,
1740 waveview->bbox_lrx,
1741 waveview->bbox_lry,
1742 waveview->bbox_lrx - waveview->bbox_ulx,
1743 waveview->bbox_lry - waveview->bbox_uly,
1744 ulx, uly,
1745 lrx, lry,
1746 lrx - ulx,
1747 lry - uly,
1748 s1, s2);
1749 #endif
1751 /* draw the top half */
1753 for (xoff = ulx; xoff < lrx; xoff++) {
1754 double max, min;
1756 max = waveview->cache->data[cache_index].max;
1757 min = waveview->cache->data[cache_index].min;
1759 if (min <= -1.0) {
1760 min = -1.0;
1763 if (max >= 1.0) {
1764 max = 1.0;
1767 if (rectify) {
1768 if (fabs (min) > fabs (max)) {
1769 max = fabs (min);
1773 yoff = origin - (waveview->half_height * max) + 0.5;
1775 if (xoff == ulx) {
1776 /* first point */
1777 cairo_move_to (cr, xoff+0.5, yoff);
1778 } else {
1779 cairo_line_to (cr, xoff+0.5, yoff);
1782 cache_index++;
1785 /* from the final top point, move out of the clip zone */
1787 cairo_line_to (cr, xoff + 10, yoff);
1789 /* now draw the bottom half */
1791 for (--xoff, --cache_index; xoff >= ulx; --xoff) {
1792 double min;
1794 min = waveview->cache->data[cache_index].min;
1796 if (min <= -1.0) {
1797 min = -1.0;
1800 yoff = origin - (waveview->half_height * min) + 0.5;
1802 cairo_line_to (cr, xoff+0.5, yoff);
1803 cache_index--;
1806 /* from the final lower point, move out of the clip zone */
1808 cairo_line_to (cr, xoff - 10, yoff);
1810 /* close path to fill */
1812 cairo_close_path (cr);
1814 /* fill and stroke */
1816 cairo_set_source_rgba (cr,
1817 (waveview->fill_r/255.0),
1818 (waveview->fill_g/255.0),
1819 (waveview->fill_b/255.0),
1820 (waveview->fill_a/255.0));
1821 cairo_fill_preserve (cr);
1822 cairo_set_source_rgba (cr,
1823 (waveview->wave_r/255.0),
1824 (waveview->wave_g/255.0),
1825 (waveview->wave_b/255.0),
1826 (waveview->wave_a/255.0));
1827 cairo_stroke (cr);
1829 cairo_destroy (cr);
1832 #if 0
1833 if (clip_max || clip_min) {
1834 cairo_set_source_rgba (cr, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a);
1837 if (clip_max) {
1838 cairo_move_to (cr, xoff, yoff1);
1839 cairo_line_to (cr, xoff, yoff1 + clip_length);
1840 cairo_stroke (cr);
1843 if (clip_min) {
1844 cairo_move_to (cr, xoff, yoff2);
1845 cairo_line_to (cr, xoff, yoff2 - clip_length);
1846 cairo_stroke (cr);
1849 #endif
1851 static void
1852 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1854 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1856 *x1 = waveview->x;
1857 *y1 = waveview->y;
1859 *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1860 *y2 = *y1 + waveview->height;
1862 #if 0
1863 x = 0; y = 0;
1864 gnome_canvas_item_i2w (item, &x, &y);
1865 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1866 x = *x2;
1867 y = *y2;
1868 gnome_canvas_item_i2w (item, &x, &y);
1869 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1870 printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1871 #endif
1875 static double
1876 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1878 (void) item;
1879 (void) x;
1880 (void) y;
1881 (void) cx;
1882 (void) cy;
1883 (void) actual_item;
1885 /* XXX for now, point is never inside the wave
1886 GnomeCanvasWaveView *waveview;
1887 double x1, y1, x2, y2;
1888 double dx, dy;
1891 return DBL_MAX;
1893 #if 0
1894 waveview = GNOME_CANVAS_WAVEVIEW (item);
1896 *actual_item = item;
1898 /* Find the bounds for the rectangle plus its outline width */
1900 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1902 /* Is point inside rectangle */
1904 if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1905 return 0.0;
1908 /* Point is outside rectangle */
1910 if (x < x1)
1911 dx = x1 - x;
1912 else if (x > x2)
1913 dx = x - x2;
1914 else
1915 dx = 0.0;
1917 if (y < y1)
1918 dy = y1 - y;
1919 else if (y > y2)
1920 dy = y - y2;
1921 else
1922 dy = 0.0;
1924 return sqrt (dx * dx + dy * dy);
1925 #endif