force AIFF format exported files to big-endian
[ardour2.git] / gtk2_ardour / canvas-waveview.c
blob29cc8502333ba79ad2759ef17750f75e7047ffee
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>
28 #include <ardour/dB.h>
30 #include "logmeter.h"
31 #include "canvas-waveview.h"
32 #include "rgb_macros.h"
35 extern void c_stacktrace();
37 enum {
38 PROP_0,
39 PROP_DATA_SRC,
40 PROP_CHANNEL,
41 PROP_LENGTH_FUNCTION,
42 PROP_SOURCEFILE_LENGTH_FUNCTION,
43 PROP_PEAK_FUNCTION,
44 PROP_GAIN_FUNCTION,
45 PROP_GAIN_SRC,
46 PROP_CACHE,
47 PROP_CACHE_UPDATER,
48 PROP_SAMPLES_PER_UNIT,
49 PROP_AMPLITUDE_ABOVE_AXIS,
50 PROP_X,
51 PROP_Y,
52 PROP_HEIGHT,
53 PROP_WAVE_COLOR,
54 PROP_CLIP_COLOR,
55 PROP_ZERO_COLOR,
56 PROP_FILL_COLOR,
57 PROP_FILLED,
58 PROP_RECTIFIED,
59 PROP_ZERO_LINE,
60 PROP_REGION_START,
61 PROP_LOGSCALED,
64 static void gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class);
66 static void gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview);
68 static void gnome_canvas_waveview_destroy (GtkObject *object);
70 static void gnome_canvas_waveview_set_property (GObject *object,
71 guint prop_id,
72 const GValue *value,
73 GParamSpec *pspec);
74 static void gnome_canvas_waveview_get_property (GObject *object,
75 guint prop_id,
76 GValue *value,
77 GParamSpec *pspec);
79 static void gnome_canvas_waveview_update (GnomeCanvasItem *item,
80 double *affine,
81 ArtSVP *clip_path,
82 int flags);
84 static void gnome_canvas_waveview_bounds (GnomeCanvasItem *item,
85 double *x1,
86 double *y1,
87 double *x2,
88 double *y2);
90 static double gnome_canvas_waveview_point (GnomeCanvasItem *item,
91 double x,
92 double y,
93 int cx,
94 int cy,
95 GnomeCanvasItem **actual_item);
97 static void gnome_canvas_waveview_render (GnomeCanvasItem *item,
98 GnomeCanvasBuf *buf);
100 static void gnome_canvas_waveview_draw (GnomeCanvasItem *item,
101 GdkDrawable *drawable,
102 int x,
103 int y,
104 int w,
105 int h);
107 static void gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *,
108 void *);
110 static void gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *,
111 guint32);
113 static gint32 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview,
114 gulong start_sample,
115 gulong end_sample);
117 static GnomeCanvasItemClass *parent_class;
119 GType
120 gnome_canvas_waveview_get_type (void)
122 static GType waveview_type;
124 if (!waveview_type) {
125 static const GTypeInfo object_info = {
126 sizeof (GnomeCanvasWaveViewClass),
127 (GBaseInitFunc) NULL,
128 (GBaseFinalizeFunc) NULL,
129 (GClassInitFunc) gnome_canvas_waveview_class_init,
130 (GClassFinalizeFunc) NULL,
131 NULL, /* class_data */
132 sizeof (GnomeCanvasWaveView),
133 0, /* n_preallocs */
134 (GInstanceInitFunc) gnome_canvas_waveview_init,
135 NULL /* value_table */
138 waveview_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasWaveView",
139 &object_info, 0);
142 return waveview_type;
145 static void
146 gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class)
148 GObjectClass *gobject_class;
149 GtkObjectClass *object_class;
150 GnomeCanvasItemClass *item_class;
152 gobject_class = (GObjectClass *) class;
153 object_class = (GtkObjectClass *) class;
154 item_class = (GnomeCanvasItemClass *) class;
156 parent_class = g_type_class_peek_parent (class);
158 gobject_class->set_property = gnome_canvas_waveview_set_property;
159 gobject_class->get_property = gnome_canvas_waveview_get_property;
161 g_object_class_install_property
162 (gobject_class,
163 PROP_DATA_SRC,
164 g_param_spec_pointer ("data_src", NULL, NULL,
165 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
167 g_object_class_install_property
168 (gobject_class,
169 PROP_CHANNEL,
170 g_param_spec_uint ("channel", NULL, NULL,
171 0, G_MAXUINT, 0,
172 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
174 g_object_class_install_property
175 (gobject_class,
176 PROP_LENGTH_FUNCTION,
177 g_param_spec_pointer ("length_function", NULL, NULL,
178 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
180 g_object_class_install_property
181 (gobject_class,
182 PROP_SOURCEFILE_LENGTH_FUNCTION,
183 g_param_spec_pointer ("sourcefile_length_function", NULL, NULL,
184 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
186 g_object_class_install_property
187 (gobject_class,
188 PROP_PEAK_FUNCTION,
189 g_param_spec_pointer ("peak_function", NULL, NULL,
190 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
192 g_object_class_install_property
193 (gobject_class,
194 PROP_GAIN_FUNCTION,
195 g_param_spec_pointer ("gain_function", NULL, NULL,
196 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
198 g_object_class_install_property
199 (gobject_class,
200 PROP_GAIN_SRC,
201 g_param_spec_pointer ("gain_src", NULL, NULL,
202 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
204 g_object_class_install_property
205 (gobject_class,
206 PROP_CACHE,
207 g_param_spec_pointer ("cache", NULL, NULL,
208 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
210 g_object_class_install_property
211 (gobject_class,
212 PROP_CACHE_UPDATER,
213 g_param_spec_boolean ("cache_updater", NULL, NULL,
214 FALSE,
215 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
217 g_object_class_install_property
218 (gobject_class,
219 PROP_SAMPLES_PER_UNIT,
220 g_param_spec_double ("samples_per_unit", NULL, NULL,
221 0.0, G_MAXDOUBLE, 0.0,
222 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
224 g_object_class_install_property
225 (gobject_class,
226 PROP_AMPLITUDE_ABOVE_AXIS,
227 g_param_spec_double ("amplitude_above_axis", NULL, NULL,
228 0.0, G_MAXDOUBLE, 0.0,
229 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
231 g_object_class_install_property
232 (gobject_class,
233 PROP_X,
234 g_param_spec_double ("x", NULL, NULL,
235 0.0, G_MAXDOUBLE, 0.0,
236 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
238 g_object_class_install_property
239 (gobject_class,
240 PROP_Y,
241 g_param_spec_double ("y", NULL, NULL,
242 0.0, G_MAXDOUBLE, 0.0,
243 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
245 g_object_class_install_property
246 (gobject_class,
247 PROP_HEIGHT,
248 g_param_spec_double ("height", NULL, NULL,
249 0.0, G_MAXDOUBLE, 0.0,
250 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
252 g_object_class_install_property
253 (gobject_class,
254 PROP_WAVE_COLOR,
255 g_param_spec_uint ("wave_color", NULL, NULL,
256 0, G_MAXUINT, 0,
257 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
259 g_object_class_install_property
260 (gobject_class,
261 PROP_CLIP_COLOR,
262 g_param_spec_uint ("clip_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_ZERO_COLOR,
269 g_param_spec_uint ("zero_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_FILL_COLOR,
276 g_param_spec_uint ("fill_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_FILLED,
283 g_param_spec_boolean ("filled", NULL, NULL,
284 FALSE,
285 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
287 g_object_class_install_property
288 (gobject_class,
289 PROP_RECTIFIED,
290 g_param_spec_boolean ("rectified", NULL, NULL,
291 FALSE,
292 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
294 g_object_class_install_property
295 (gobject_class,
296 PROP_ZERO_LINE,
297 g_param_spec_boolean ("zero_line", NULL, NULL,
298 FALSE,
299 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
301 g_object_class_install_property
302 (gobject_class,
303 PROP_LOGSCALED,
304 g_param_spec_boolean ("logscaled", NULL, NULL,
305 FALSE,
306 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
308 g_object_class_install_property
309 (gobject_class,
310 PROP_REGION_START,
311 g_param_spec_uint ("region_start", NULL, NULL,
312 0, G_MAXUINT, 0,
313 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
315 object_class->destroy = gnome_canvas_waveview_destroy;
317 item_class->update = gnome_canvas_waveview_update;
318 item_class->bounds = gnome_canvas_waveview_bounds;
319 item_class->point = gnome_canvas_waveview_point;
320 item_class->render = gnome_canvas_waveview_render;
321 item_class->draw = gnome_canvas_waveview_draw;
324 GnomeCanvasWaveViewCache*
325 gnome_canvas_waveview_cache_new ()
327 GnomeCanvasWaveViewCache *c;
329 c = g_malloc (sizeof (GnomeCanvasWaveViewCache));
331 c->allocated = 2048;
332 c->data = g_malloc (sizeof (GnomeCanvasWaveViewCacheEntry) * c->allocated);
333 c->data_size = 0;
334 c->start = 0;
335 c->end = 0;
337 return c;
340 void
341 gnome_canvas_waveview_cache_destroy (GnomeCanvasWaveViewCache* cache)
343 g_free (cache->data);
344 g_free (cache);
347 static void
348 gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview)
350 waveview->x = 0.0;
351 waveview->y = 0.0;
352 waveview->cache = 0;
353 waveview->cache_updater = FALSE;
354 waveview->data_src = NULL;
355 waveview->channel = 0;
356 waveview->peak_function = NULL;
357 waveview->length_function = NULL;
358 waveview->sourcefile_length_function = NULL;
359 waveview->gain_curve_function = NULL;
360 waveview->gain_src = NULL;
361 waveview->rectified = FALSE;
362 waveview->logscaled = FALSE;
363 waveview->filled = TRUE;
364 waveview->zero_line = FALSE;
365 waveview->region_start = 0;
366 waveview->samples_per_unit = 1.0;
367 waveview->amplitude_above_axis = 1.0;
368 waveview->height = 100.0;
369 waveview->screen_width = gdk_screen_width ();
370 waveview->reload_cache_in_render = FALSE;
372 waveview->wave_color = 0;
373 waveview->clip_color = 0;
374 waveview->zero_color = 0;
375 waveview->fill_color = 0;
378 static void
379 gnome_canvas_waveview_destroy (GtkObject *object)
381 GnomeCanvasWaveView *waveview;
383 g_return_if_fail (object != NULL);
384 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
386 waveview = GNOME_CANVAS_WAVEVIEW (object);
388 if (GTK_OBJECT_CLASS (parent_class)->destroy)
389 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
392 #define DEBUG_CACHE 0
393 #undef CACHE_MEMMOVE_OPTIMIZATION
395 static gint32
396 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_sample, gulong end_sample)
398 gulong required_cache_entries;
399 gulong rf1, rf2,rf3, required_frames;
400 gulong new_cache_start, new_cache_end;
401 gulong half_width;
402 gulong npeaks;
403 gulong offset;
404 gulong ostart;
405 gulong copied;
406 GnomeCanvasWaveViewCache *cache;
407 float* gain;
408 #ifdef CACHE_MEMMOVE_OPTIMIZATION
409 gulong present_frames;
410 gulong present_entries;
411 #endif
413 cache = waveview->cache;
415 start_sample = start_sample + waveview->region_start;
416 end_sample = end_sample + waveview->region_start;
417 #if DEBUG_CACHE
418 // printf("waveview->region_start == %lu\n",waveview->region_start);
419 // c_stacktrace ();
420 printf ("\n\n=> 0x%x cache @ 0x%x range: %lu - %lu request: %lu - %lu (%lu frames)\n",
421 waveview, cache,
422 cache->start, cache->end,
423 start_sample, end_sample, end_sample - start_sample);
424 #endif
426 if (cache->start <= start_sample && cache->end >= end_sample) {
427 #if DEBUG_CACHE
428 // printf ("0x%x: cache hit for %lu-%lu (cache holds: %lu-%lu\n",
429 // waveview, start_sample, end_sample, cache->start, cache->end);
430 #endif
431 goto out;
434 /* make sure the cache is at least twice as wide as the screen width, and put the start sample
435 in the middle, ensuring that we cover the end_sample.
438 /* Note the assumption that we have a 1:1 units:pixel ratio for the canvas. Its everywhere ... */
440 half_width = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit)/2.0 + 0.5);
442 if (start_sample < half_width) {
443 new_cache_start = 0;
444 } else {
445 new_cache_start = start_sample - half_width;
448 /* figure out how many frames we want */
450 rf1 = end_sample - start_sample + 1;
451 rf2 = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit * 2.0f));
452 required_frames = MAX(rf1,rf2);
454 /* but make sure it doesn't extend beyond the end of the source material */
456 rf3 = (gulong) (waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit)) + 1;
457 if (rf3 < new_cache_start) {
458 rf3 = 0;
459 } else {
460 rf3 -= new_cache_start;
463 #if DEBUG_CACHE
464 fprintf (stderr, "AVAILABLE FRAMES = %lu of %lu, start = %lu, sstart = %lu, cstart = %lu\n",
465 rf3, waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit),
466 waveview->region_start, start_sample, new_cache_start);
467 #endif
469 required_frames = MIN(required_frames,rf3);
471 new_cache_end = new_cache_start + required_frames - 1;
473 required_cache_entries = (gulong) floor (required_frames / waveview->samples_per_unit );
475 #if DEBUG_CACHE
476 fprintf (stderr, "new cache = %lu - %lu\n", new_cache_start, new_cache_end);
477 fprintf(stderr,"required_cach_entries = %lu, samples_per_unit = %f req frames = %lu\n",
478 required_cache_entries,waveview->samples_per_unit, required_frames);
479 #endif
481 if (required_cache_entries > cache->allocated) {
482 cache->data = g_realloc (cache->data, sizeof (GnomeCanvasWaveViewCacheEntry) * required_cache_entries);
483 cache->allocated = required_cache_entries;
484 // cache->start = 0;
485 // cache->end = 0;
488 ostart = new_cache_start;
490 #ifdef CACHE_MEMMOVE_OPTIMIZATION
492 /* data is not entirely in the cache, so go fetch it, making sure to fill the cache */
494 /* some of the required cache entries are in the cache, but in the wrong
495 locations. use memmove to fix this.
498 if (cache->start < new_cache_start && new_cache_start < cache->end) {
500 /* case one: the common area is at the end of the existing cache. move it
501 to the beginning of the cache, and set up to refill whatever remains.
504 wv->cache_start wv->cache_end
505 |-------------------------------------------------------| cache
506 |--------------------------------| requested
507 <------------------->
508 "present"
509 new_cache_start new_cache_end
513 present_frames = cache->end - new_cache_start;
514 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
516 #if DEBUG_CACHE
517 fprintf (stderr, "existing material at end of current cache, move to start of new cache\n"
518 "\tcopy from %lu to start\n", cache->data_size - present_entries);
519 #endif
521 memmove (&cache->data[0],
522 &cache->data[cache->data_size - present_entries],
523 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
525 #if DEBUG_CACHE
526 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
527 present_frames, required_frames, present_entries, new_cache_start + present_entries,
528 cache->data + present_entries);
529 #endif
531 copied = present_entries;
532 offset = present_entries;
533 new_cache_start += present_frames;
534 required_frames -= present_frames;
536 } else if (new_cache_end > cache->start && new_cache_end < cache->end) {
538 /* case two: the common area lives at the beginning of the existing cache.
540 wv->cache_start wv->cache_end
541 |-----------------------------------------------------|
542 |--------------------------------|
543 <----------------->
544 "present"
546 new_cache_start new_cache_end
549 present_frames = new_cache_end - cache->start;
550 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
552 memmove (&cache->data[cache->data_size - present_entries],
553 &cache->data[0],
554 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
556 #if DEBUG_CACHE
557 fprintf (stderr, "existing material at start of current cache, move to start of end cache\n");
558 #endif
560 #if DEBUG_CACHE
561 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
562 present_entries, required_frames, present_entries, new_cache_start + present_entries,
563 cache->data + present_entries);
564 #endif
566 copied = present_entries;
567 offset = 0;
568 required_frames -= present_frames;
571 } else {
572 copied = 0;
573 offset = 0;
577 #else
578 copied = 0;
579 offset = 0;
581 #endif /* CACHE_MEMMOVE_OPTIMIZATION */
583 // fprintf(stderr,"length == %lu\n",waveview->length_function (waveview->data_src));
584 // required_frames = MIN (waveview->length_function (waveview->data_src) - new_cache_start, required_frames);
586 npeaks = (gulong) floor (required_frames / waveview->samples_per_unit);
587 required_frames = npeaks * waveview->samples_per_unit;
589 #if DEBUG_CACHE
592 printf ("requesting %lu/%f to cover %lu-%lu at %f spu (request was %lu-%lu) into cache + %lu\n",
593 required_frames, required_frames/waveview->samples_per_unit, new_cache_start, new_cache_end,
594 waveview->samples_per_unit, start_sample, end_sample, offset);
595 #endif
597 #if DEBUG_CACHE
598 // printf ("cache holds %lu entries, requesting %lu to cover %lu-%lu (request was %lu-%lu)\n",
599 // cache->data_size, npeaks, new_cache_start, new_cache_end,
600 // start_sample, end_sample);
601 #endif
603 if (required_frames) {
604 waveview->peak_function (waveview->data_src, npeaks, new_cache_start, required_frames, cache->data + offset, waveview->channel,waveview->samples_per_unit);
606 /* take into account any copied peaks */
608 npeaks += copied;
609 } else {
610 npeaks = copied;
613 if (npeaks < cache->allocated) {
614 #if DEBUG_CACHE
615 fprintf (stderr, "zero fill cache for %lu at %lu\n", cache->allocated - npeaks, npeaks);
616 #endif
617 memset (&cache->data[npeaks], 0, sizeof (GnomeCanvasWaveViewCacheEntry) * (cache->allocated - npeaks));
618 cache->data_size = npeaks;
619 } else {
620 cache->data_size = cache->allocated;
623 if (waveview->gain_curve_function) {
624 guint32 n;
626 gain = (float*) malloc (sizeof (float) * cache->data_size);
628 waveview->gain_curve_function (waveview->gain_src, new_cache_start, new_cache_end, gain, cache->data_size);
630 for (n = 0; n < cache->data_size; ++n) {
631 cache->data[n].min *= gain[n];
632 cache->data[n].max *= gain[n];
635 free (gain);
639 /* do optional log scaling. this implementation is not particularly efficient */
641 if (waveview->logscaled) {
642 guint32 n;
643 GnomeCanvasWaveViewCacheEntry* buf = cache->data;
645 for (n = 0; n < cache->data_size; ++n) {
647 if (buf[n].max > 0.0f) {
648 buf[n].max = alt_log_meter(fast_coefficient_to_dB(buf[n].max));
649 } else if (buf[n].max < 0.0f) {
650 buf[n].max = -alt_log_meter(fast_coefficient_to_dB(-buf[n].max));
653 if (buf[n].min > 0.0f) {
654 buf[n].min = alt_log_meter(fast_coefficient_to_dB(buf[n].min));
655 } else if (buf[n].min < 0.0f) {
656 buf[n].min = -alt_log_meter(fast_coefficient_to_dB(-buf[n].min));
661 cache->start = ostart;
662 cache->end = new_cache_end;
664 out:
665 #if DEBUG_CACHE
666 fprintf (stderr, "return cache index = %d\n",
667 (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5));
668 #endif
669 return (gint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5);
673 void
674 gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *waveview, void *data_src)
677 if (waveview->cache_updater) {
678 if (waveview->data_src == data_src) {
679 waveview->reload_cache_in_render = TRUE;
680 return;
683 waveview->cache->start = 0;
684 waveview->cache->end = 0;
687 waveview->data_src = data_src;
690 void
691 gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *waveview, guint32 chan)
693 if (waveview->channel == chan) {
694 return;
697 waveview->channel = chan;
700 static void
701 gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
704 double x1, x2, y1, y2;
705 ArtPoint i1, i2;
706 ArtPoint w1, w2;
707 int Ix1, Ix2, Iy1, Iy2;
708 double i2w[6];
710 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
712 i1.x = x1;
713 i1.y = y1;
714 i2.x = x2;
715 i2.y = y2;
717 gnome_canvas_item_i2w_affine (item, i2w);
718 art_affine_point (&w1, &i1, i2w);
719 art_affine_point (&w2, &i2, i2w);
721 Ix1 = (int) rint(w1.x);
722 Ix2 = (int) rint(w2.x);
723 Iy1 = (int) rint(w1.y);
724 Iy2 = (int) rint(w2.y);
726 gnome_canvas_update_bbox (item, Ix1, Iy1, Ix2, Iy2);
730 * CANVAS CALLBACKS
733 static void
734 gnome_canvas_waveview_set_property (GObject *object,
735 guint prop_id,
736 const GValue *value,
737 GParamSpec *pspec)
740 GnomeCanvasItem *item;
741 GnomeCanvasWaveView *waveview;
742 int redraw = FALSE;
743 int calc_bounds = FALSE;
745 g_return_if_fail (object != NULL);
746 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
748 item = GNOME_CANVAS_ITEM (object);
749 waveview = GNOME_CANVAS_WAVEVIEW (object);
751 switch (prop_id) {
752 case PROP_DATA_SRC:
753 gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
754 redraw = TRUE;
755 break;
757 case PROP_CHANNEL:
758 gnome_canvas_waveview_set_channel (waveview, g_value_get_uint(value));
759 redraw = TRUE;
760 break;
762 case PROP_LENGTH_FUNCTION:
763 waveview->length_function = g_value_get_pointer(value);
764 redraw = TRUE;
765 break;
766 case PROP_SOURCEFILE_LENGTH_FUNCTION:
767 waveview->sourcefile_length_function = g_value_get_pointer(value);
768 redraw = TRUE;
769 break;
771 case PROP_PEAK_FUNCTION:
772 waveview->peak_function = g_value_get_pointer(value);
773 redraw = TRUE;
774 break;
776 case PROP_GAIN_FUNCTION:
777 waveview->gain_curve_function = g_value_get_pointer(value);
778 redraw = TRUE;
779 break;
781 case PROP_GAIN_SRC:
782 waveview->gain_src = g_value_get_pointer(value);
783 if (waveview->cache_updater) {
784 waveview->cache->start = 0;
785 waveview->cache->end = 0;
787 redraw = TRUE;
788 calc_bounds = TRUE;
789 break;
791 case PROP_CACHE:
792 waveview->cache = g_value_get_pointer(value);
793 redraw = TRUE;
794 break;
797 case PROP_CACHE_UPDATER:
798 waveview->cache_updater = g_value_get_boolean(value);
799 redraw = TRUE;
800 break;
802 case PROP_SAMPLES_PER_UNIT:
803 if ((waveview->samples_per_unit = g_value_get_double(value)) < 1.0) {
804 waveview->samples_per_unit = 1.0;
806 if (waveview->cache_updater) {
807 waveview->cache->start = 0;
808 waveview->cache->end = 0;
810 redraw = TRUE;
811 calc_bounds = TRUE;
812 break;
814 case PROP_AMPLITUDE_ABOVE_AXIS:
815 waveview->amplitude_above_axis = g_value_get_double(value);
816 redraw = TRUE;
817 break;
819 case PROP_X:
820 if (waveview->x != g_value_get_double (value)) {
821 waveview->x = g_value_get_double (value);
822 calc_bounds = TRUE;
824 break;
826 case PROP_Y:
827 if (waveview->y != g_value_get_double (value)) {
828 waveview->y = g_value_get_double (value);
829 calc_bounds = TRUE;
831 break;
833 case PROP_HEIGHT:
834 if (waveview->height != fabs (g_value_get_double (value))) {
835 waveview->height = fabs (g_value_get_double (value));
836 redraw = TRUE;
838 break;
840 case PROP_WAVE_COLOR:
841 if (waveview->wave_color != g_value_get_uint(value)) {
842 waveview->wave_color = g_value_get_uint(value);
843 redraw = TRUE;
845 break;
847 case PROP_CLIP_COLOR:
848 if (waveview->clip_color != g_value_get_uint(value)) {
849 waveview->clip_color = g_value_get_uint(value);
850 redraw = TRUE;
852 break;
854 case PROP_ZERO_COLOR:
855 if (waveview->zero_color != g_value_get_uint(value)) {
856 waveview->zero_color = g_value_get_uint(value);
857 redraw = TRUE;
859 break;
861 case PROP_FILL_COLOR:
862 if (waveview->fill_color != g_value_get_uint(value)) {
863 waveview->fill_color = g_value_get_uint(value);
864 redraw = TRUE;
866 break;
868 case PROP_FILLED:
869 if (waveview->filled != g_value_get_boolean(value)) {
870 waveview->filled = g_value_get_boolean(value);
871 redraw = TRUE;
873 break;
875 case PROP_RECTIFIED:
876 if (waveview->rectified != g_value_get_boolean(value)) {
877 waveview->rectified = g_value_get_boolean(value);
878 redraw = TRUE;
880 break;
882 case PROP_ZERO_LINE:
883 if (waveview->zero_line != g_value_get_boolean(value)) {
884 waveview->zero_line = g_value_get_boolean(value);
885 redraw = TRUE;
887 break;
889 case PROP_LOGSCALED:
890 if (waveview->logscaled != g_value_get_boolean(value)) {
891 waveview->logscaled = g_value_get_boolean(value);
892 if (waveview->cache_updater) {
893 waveview->cache->start = 0;
894 waveview->cache->end = 0;
896 redraw = TRUE;
897 calc_bounds = TRUE;
899 break;
900 case PROP_REGION_START:
901 waveview->region_start = g_value_get_uint(value);
902 redraw = TRUE;
903 calc_bounds = TRUE;
904 break;
907 default:
908 break;
911 if (calc_bounds) {
912 gnome_canvas_waveview_reset_bounds (item);
915 if (redraw) {
916 gnome_canvas_item_request_update (item);
921 static void
922 gnome_canvas_waveview_get_property (GObject *object,
923 guint prop_id,
924 GValue *value,
925 GParamSpec *pspec)
929 g_return_if_fail (object != NULL);
930 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
932 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
934 switch (prop_id) {
935 case PROP_DATA_SRC:
936 g_value_set_pointer(value, waveview->data_src);
937 break;
939 case PROP_CHANNEL:
940 g_value_set_uint(value, waveview->channel);
941 break;
943 case PROP_LENGTH_FUNCTION:
944 g_value_set_pointer(value, waveview->length_function);
945 break;
947 case PROP_SOURCEFILE_LENGTH_FUNCTION:
948 g_value_set_pointer(value, waveview->sourcefile_length_function);
949 break;
951 case PROP_PEAK_FUNCTION:
952 g_value_set_pointer(value, waveview->peak_function);
953 break;
955 case PROP_GAIN_FUNCTION:
956 g_value_set_pointer(value, waveview->gain_curve_function);
957 break;
959 case PROP_GAIN_SRC:
960 g_value_set_pointer(value, waveview->gain_src);
961 break;
963 case PROP_CACHE:
964 g_value_set_pointer(value, waveview->cache);
965 break;
967 case PROP_CACHE_UPDATER:
968 g_value_set_boolean(value, waveview->cache_updater);
969 break;
971 case PROP_SAMPLES_PER_UNIT:
972 g_value_set_double(value, waveview->samples_per_unit);
973 break;
975 case PROP_AMPLITUDE_ABOVE_AXIS:
976 g_value_set_double(value, waveview->amplitude_above_axis);
977 break;
979 case PROP_X:
980 g_value_set_double (value, waveview->x);
981 break;
983 case PROP_Y:
984 g_value_set_double (value, waveview->y);
985 break;
987 case PROP_HEIGHT:
988 g_value_set_double (value, waveview->height);
989 break;
991 case PROP_WAVE_COLOR:
992 g_value_set_uint (value, waveview->wave_color);
993 break;
995 case PROP_CLIP_COLOR:
996 g_value_set_uint (value, waveview->clip_color);
997 break;
999 case PROP_ZERO_COLOR:
1000 g_value_set_uint (value, waveview->zero_color);
1001 break;
1003 case PROP_FILL_COLOR:
1004 g_value_set_uint (value, waveview->fill_color);
1005 break;
1007 case PROP_FILLED:
1008 g_value_set_boolean (value, waveview->filled);
1009 break;
1011 case PROP_RECTIFIED:
1012 g_value_set_boolean (value, waveview->rectified);
1013 break;
1015 case PROP_ZERO_LINE:
1016 g_value_set_boolean (value, waveview->zero_line);
1017 break;
1019 case PROP_LOGSCALED:
1020 g_value_set_boolean (value, waveview->logscaled);
1021 break;
1023 case PROP_REGION_START:
1024 g_value_set_uint (value, waveview->region_start);
1025 break;
1027 default:
1028 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1029 break;
1033 static void
1034 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1036 GnomeCanvasWaveView *waveview;
1037 double x, y;
1039 waveview = GNOME_CANVAS_WAVEVIEW (item);
1041 // check_cache (waveview, "start of update");
1043 if (parent_class->update)
1044 (* parent_class->update) (item, affine, clip_path, flags);
1046 gnome_canvas_waveview_reset_bounds (item);
1048 /* get the canvas coordinates of the view. Do NOT use affines
1049 for this, because they do not round to the integer units used
1050 by the canvas, resulting in subtle pixel-level errors later.
1053 x = waveview->x;
1054 y = waveview->y;
1056 gnome_canvas_item_i2w (item, &x, &y);
1057 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
1059 waveview->samples = waveview->length_function (waveview->data_src);
1061 x = waveview->x + (waveview->samples / waveview->samples_per_unit);
1062 y = waveview->y + waveview->height;
1064 gnome_canvas_item_i2w (item, &x, &y);
1065 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
1067 /* cache the half-height and the end point in canvas units */
1069 waveview->half_height = waveview->height / 2.0;
1071 /* parse the color */
1073 UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
1074 &waveview->wave_a);
1075 UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
1076 &waveview->clip_a);
1077 UINT_TO_RGBA (waveview->fill_color, &waveview->fill_r, &waveview->fill_g, &waveview->fill_b,
1078 &waveview->fill_a);
1080 // check_cache (waveview, "end of update");
1083 static void
1084 gnome_canvas_waveview_render (GnomeCanvasItem *item,
1085 GnomeCanvasBuf *buf)
1087 GnomeCanvasWaveView *waveview;
1088 gulong s1, s2;
1089 int clip_length = 0;
1090 int pymin, pymax;
1091 int cache_index;
1092 double half_height;
1093 int x, end, begin;
1094 int zbegin, zend;
1095 char rectify;
1097 waveview = GNOME_CANVAS_WAVEVIEW (item);
1099 // check_cache (waveview, "start of render");
1101 if (parent_class->render) {
1102 (*parent_class->render) (item, buf);
1105 if (buf->is_bg) {
1106 gnome_canvas_buf_ensure_buf (buf);
1107 buf->is_bg = FALSE;
1110 begin = MAX(waveview->bbox_ulx, buf->rect.x0);
1112 if (begin == waveview->bbox_ulx) {
1113 zbegin = begin + 1;
1114 } else {
1115 zbegin = begin;
1118 if (waveview->bbox_lrx >= 0) {
1119 end = MIN(waveview->bbox_lrx,buf->rect.x1);
1120 } else {
1121 end = buf->rect.x1;
1124 if (end == waveview->bbox_lrx) {
1125 zend = end - 1;
1126 } else {
1127 zend = end;
1130 if (begin == end) {
1131 return;
1134 s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1136 // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
1138 if (end == waveview->bbox_lrx) {
1139 /* This avoids minor rounding errors when we have the
1140 entire region visible.
1142 s2 = waveview->samples;
1143 } else {
1144 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
1147 #if 0
1148 printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
1149 " b/e %d..%d s= %lu..%lu @ %f\n",
1150 waveview,
1151 buf->rect.x0,
1152 buf->rect.x1,
1153 buf->rect.y0,
1154 buf->rect.y1,
1155 waveview->bbox_ulx,
1156 waveview->bbox_lrx,
1157 waveview->bbox_uly,
1158 waveview->bbox_lry,
1159 begin, end, s1, s2,
1160 waveview->samples_per_unit);
1161 #endif
1163 /* now ensure that the cache is full and properly
1164 positioned.
1167 // check_cache (waveview, "pre-ensure");
1169 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1170 waveview->cache->start = 0;
1171 waveview->cache->end = 0;
1172 waveview->reload_cache_in_render = FALSE;
1175 // check_cache (waveview, "post-ensure");
1177 /* don't rectify at single-sample zoom */
1178 if(waveview->rectified && waveview->samples_per_unit > 1) {
1179 rectify = TRUE;
1181 else {
1182 rectify = FALSE;
1185 clip_length = MIN(5,(waveview->height/4));
1188 Now draw each line, clipping it appropriately. The clipping
1189 is done by the macros PAINT_FOO().
1192 half_height = waveview->half_height;
1194 /* this makes it slightly easier to comprehend whats going on */
1195 #define origin half_height
1197 if(waveview->filled && !rectify) {
1198 int prev_pymin = 1;
1199 int prev_pymax = 0;
1200 int last_pymin = 1;
1201 int last_pymax = 0;
1202 int next_pymin, next_pymax;
1203 double max, min;
1204 int next_clip_max = 0;
1205 int next_clip_min = 0;
1207 if(s1 < waveview->samples_per_unit) {
1208 /* we haven't got a prev vars to compare with, so outline the whole line here */
1209 prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1210 prev_pymin = prev_pymax;
1212 else {
1213 s1 -= waveview->samples_per_unit;
1216 if(end == waveview->bbox_lrx) {
1217 /* we don't have the NEXT vars for the last sample */
1218 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1219 last_pymin = last_pymax;
1221 else {
1222 s2 += waveview->samples_per_unit;
1225 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1228 * Compute the variables outside the rendering rect
1230 if(prev_pymax != prev_pymin) {
1231 prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1232 prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1233 ++cache_index;
1235 if(last_pymax != last_pymin) {
1236 /* take the index of one sample right of what we render */
1237 int index = cache_index + (end - begin);
1239 last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1240 last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1244 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1246 max = waveview->cache->data[cache_index].max;
1247 min = waveview->cache->data[cache_index].min;
1249 if (max >= 1.0) {
1250 max = 1.0;
1251 next_clip_max = 1;
1254 if (min <= -1.0) {
1255 min = -1.0;
1256 next_clip_min = 1;
1259 max *= half_height;
1260 min *= half_height;
1262 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1263 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1266 * And now the loop
1268 for(x = begin; x < end; ++x) {
1269 int clip_max = next_clip_max;
1270 int clip_min = next_clip_min;
1271 int fill_max, fill_min;
1273 pymax = next_pymax;
1274 pymin = next_pymin;
1276 /* compute next */
1277 if(x == end - 1) {
1278 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1279 next_pymax = last_pymax;
1280 next_pymin = last_pymin;
1282 else {
1283 ++cache_index;
1285 max = waveview->cache->data[cache_index].max;
1286 min = waveview->cache->data[cache_index].min;
1288 next_clip_max = 0;
1289 next_clip_min = 0;
1291 if (max >= 1.0) {
1292 max = 1.0;
1293 next_clip_max = 1;
1296 if (min <= -1.0) {
1297 min = -1.0;
1298 next_clip_min = 1;
1301 max *= half_height;
1302 min *= half_height;
1304 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1305 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1308 /* render */
1309 if (pymax == pymin) {
1310 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1311 } else {
1312 if((prev_pymax < pymax && next_pymax < pymax) ||
1313 (prev_pymax == pymax && next_pymax == pymax)) {
1314 fill_max = pymax + 1;
1315 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1317 else {
1318 fill_max = MAX(prev_pymax, next_pymax);
1319 if(pymax == fill_max) {
1320 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1321 ++fill_max;
1323 else {
1324 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1328 if((prev_pymin > pymin && next_pymin > pymin) ||
1329 (prev_pymin == pymin && next_pymin == pymin)) {
1330 fill_min = pymin - 1;
1331 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1333 else {
1334 fill_min = MIN(prev_pymin, next_pymin);
1335 if(pymin == fill_min) {
1336 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1338 else {
1339 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin);
1343 if(fill_max < fill_min) {
1344 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min);
1346 else if(fill_max == fill_min) {
1347 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1351 if (clip_max) {
1352 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1355 if (clip_min) {
1356 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1359 prev_pymax = pymax;
1360 prev_pymin = pymin;
1363 } else if (waveview->filled && rectify) {
1365 int prev_pymax = -1;
1366 int last_pymax = -1;
1367 int next_pymax;
1368 double max, min;
1369 int next_clip_max = 0;
1370 int next_clip_min = 0;
1372 // for rectified, this stays constant throughout the loop
1373 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1375 if(s1 < waveview->samples_per_unit) {
1376 /* we haven't got a prev vars to compare with, so outline the whole line here */
1377 prev_pymax = pymin;
1379 else {
1380 s1 -= waveview->samples_per_unit;
1383 if(end == waveview->bbox_lrx) {
1384 /* we don't have the NEXT vars for the last sample */
1385 last_pymax = pymin;
1387 else {
1388 s2 += waveview->samples_per_unit;
1391 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1394 * Compute the variables outside the rendering rect
1396 if(prev_pymax < 0) {
1397 max = MIN(waveview->cache->data[cache_index].max, 1.0);
1398 min = MAX(waveview->cache->data[cache_index].min, -1.0);
1400 if (fabs (min) > fabs (max)) {
1401 max = fabs (min);
1404 prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1405 ++cache_index;
1407 if(last_pymax < 0) {
1408 /* take the index of one sample right of what we render */
1409 int index = cache_index + (end - begin);
1411 max = MIN(waveview->cache->data[index].max, 1.0);
1412 min = MAX(waveview->cache->data[index].min, -1.0);
1414 if (fabs (min) > fabs (max)) {
1415 max = fabs (min);
1418 last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1422 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1424 max = waveview->cache->data[cache_index].max;
1425 min = waveview->cache->data[cache_index].min;
1427 if (max >= 1.0) {
1428 max = 1.0;
1429 next_clip_max = 1;
1432 if (min <= -1.0) {
1433 min = -1.0;
1434 next_clip_min = 1;
1437 if (fabs (min) > fabs (max)) {
1438 max = fabs (min);
1441 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1444 * And now the loop
1446 for(x = begin; x < end; ++x) {
1447 int clip_max = next_clip_max;
1448 int clip_min = next_clip_min;
1449 int fill_max;
1451 pymax = next_pymax;
1453 /* compute next */
1454 if(x == end - 1) {
1455 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1456 next_pymax = last_pymax;
1458 else {
1459 ++cache_index;
1461 max = waveview->cache->data[cache_index].max;
1462 min = waveview->cache->data[cache_index].min;
1464 if (max >= 1.0) {
1465 max = 1.0;
1466 next_clip_max = 1;
1469 if (min <= -1.0) {
1470 min = -1.0;
1471 next_clip_min = 1;
1474 if (fabs (min) > fabs (max)) {
1475 max = fabs (min);
1478 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1481 /* render */
1482 if (pymax == pymin) {
1483 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1484 } else {
1485 if((prev_pymax < pymax && next_pymax < pymax) ||
1486 (prev_pymax == pymax && next_pymax == pymax)) {
1487 fill_max = pymax + 1;
1488 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1490 else {
1491 fill_max = MAX(prev_pymax, next_pymax);
1492 if(pymax == fill_max) {
1493 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1494 ++fill_max;
1496 else {
1497 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1501 if(fill_max < pymin) {
1502 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
1504 else if(fill_max == pymin) {
1505 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
1509 if (clip_max) {
1510 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1513 if (clip_min) {
1514 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1517 prev_pymax = pymax;
1520 else {
1521 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1523 for (x = begin; x < end; x++) {
1525 double max, min;
1526 int clip_max, clip_min;
1528 clip_max = 0;
1529 clip_min = 0;
1531 max = waveview->cache->data[cache_index].max;
1532 min = waveview->cache->data[cache_index].min;
1534 if (max >= 1.0) {
1535 max = 1.0;
1536 clip_max = 1;
1539 if (min <= -1.0) {
1540 min = -1.0;
1541 clip_min = 1;
1544 if (rectify) {
1546 if (fabs (min) > fabs (max)) {
1547 max = fabs (min);
1550 max = max * waveview->height;
1552 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1553 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1555 } else {
1557 max = max * half_height;
1558 min = min * half_height;
1560 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1561 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1564 /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1565 or, if samples_per_unit == 1, then a dot at each location.
1568 if (pymax == pymin) {
1569 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1570 } else {
1571 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1574 /* show clipped waveforms with small red lines */
1576 if (clip_max) {
1577 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1580 if (clip_min) {
1581 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1584 /* presto, we're done */
1586 cache_index++;
1590 if (!waveview->rectified && waveview->zero_line) {
1591 // Paint zeroline.
1592 //PAINT_HORIZA(buf, waveview->zero_r, waveview->zero_g, waveview->zero_b, waveview->zero_a, begin, endi-1, origin );
1594 unsigned char zero_r, zero_g, zero_b, zero_a;
1595 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a );
1596 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1597 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, end, zeroline_y);
1599 #undef origin
1603 static void
1604 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1605 GdkDrawable *drawable,
1606 int x, int y,
1607 int width, int height)
1609 GnomeCanvasWaveView *waveview;
1610 cairo_t* cr;
1611 gulong s1, s2;
1612 int cache_index;
1613 double zbegin, zend;
1614 gboolean rectify;
1615 double origin;
1616 double clip_length;
1617 double xoff;
1618 double yoff = 0.0;
1619 double ulx;
1620 double uly;
1621 double lrx;
1622 double lry;
1624 waveview = GNOME_CANVAS_WAVEVIEW (item);
1626 /* compute intersection of Drawable area and waveview,
1627 in canvas coordinate space
1630 if (x > waveview->bbox_ulx) {
1631 ulx = x;
1632 zbegin = ulx;
1633 } else {
1634 ulx = waveview->bbox_ulx;
1635 zbegin = ulx + 1;
1638 if (y > waveview->bbox_uly) {
1639 uly = y;
1640 } else {
1641 uly = waveview->bbox_uly;
1644 if (x + width > waveview->bbox_lrx) {
1645 lrx = waveview->bbox_lrx;
1646 zend = lrx - 1;
1647 } else {
1648 lrx = x + width;
1649 zend = lrx;
1652 if (y + height > waveview->bbox_lry) {
1653 lry = waveview->bbox_lry;
1654 } else {
1655 lry = y + height;
1658 /* figure out which samples we need for the resulting intersection */
1660 s1 = floor ((ulx - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1662 if (lrx == waveview->bbox_lrx) {
1663 /* This avoids minor rounding errors when we have the
1664 entire region visible.
1666 s2 = waveview->samples;
1667 } else {
1668 s2 = s1 + floor ((lrx - ulx) * waveview->samples_per_unit);
1671 /* translate back to buffer coordinate space */
1673 ulx -= x;
1674 uly -= y;
1675 lrx -= x;
1676 lry -= y;
1677 zbegin -= x;
1678 zend -= x;
1680 /* don't rectify at single-sample zoom */
1681 if(waveview->rectified && waveview->samples_per_unit > 1.0) {
1682 rectify = TRUE;
1683 } else {
1684 rectify = FALSE;
1687 clip_length = MIN(5,(waveview->height/4));
1689 cr = gdk_cairo_create (drawable);
1690 cairo_set_line_width (cr, 0.5);
1692 origin = waveview->bbox_uly - y + waveview->half_height;
1694 cairo_rectangle (cr, ulx, uly, lrx - ulx, lry - uly);
1695 cairo_clip (cr);
1697 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1698 waveview->cache->start = 0;
1699 waveview->cache->end = 0;
1700 waveview->reload_cache_in_render = FALSE;
1703 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1705 #if 0
1706 printf ("%p r (%d,%d)(%d,%d)[%d x %d] bbox (%d,%d)(%d,%d)[%d x %d]"
1707 " draw (%.1f,%.1f)(%.1f,%.1f)[%.1f x %.1f] s= %lu..%lu\n",
1708 waveview,
1709 x, y,
1710 x + width,
1711 y + height,
1712 width,
1713 height,
1714 waveview->bbox_ulx,
1715 waveview->bbox_uly,
1716 waveview->bbox_lrx,
1717 waveview->bbox_lry,
1718 waveview->bbox_lrx - waveview->bbox_ulx,
1719 waveview->bbox_lry - waveview->bbox_uly,
1720 ulx, uly,
1721 lrx, lry,
1722 lrx - ulx,
1723 lry - uly,
1724 s1, s2);
1725 #endif
1727 /* draw the top half */
1729 for (xoff = ulx; xoff < lrx; xoff++) {
1730 double max, min;
1732 max = waveview->cache->data[cache_index].max;
1733 min = waveview->cache->data[cache_index].min;
1735 if (min <= -1.0) {
1736 min = -1.0;
1739 if (max >= 1.0) {
1740 max = 1.0;
1743 if (rectify) {
1744 if (fabs (min) > fabs (max)) {
1745 max = fabs (min);
1749 yoff = origin - (waveview->half_height * max) + 0.5;
1751 if (xoff == ulx) {
1752 /* first point */
1753 cairo_move_to (cr, xoff+0.5, yoff);
1754 } else {
1755 cairo_line_to (cr, xoff+0.5, yoff);
1758 cache_index++;
1761 /* from the final top point, move out of the clip zone */
1763 cairo_line_to (cr, xoff + 10, yoff);
1765 /* now draw the bottom half */
1767 for (--xoff, --cache_index; xoff >= ulx; --xoff) {
1768 double min;
1770 min = waveview->cache->data[cache_index].min;
1772 if (min <= -1.0) {
1773 min = -1.0;
1776 yoff = origin - (waveview->half_height * min) + 0.5;
1778 cairo_line_to (cr, xoff+0.5, yoff);
1779 cache_index--;
1782 /* from the final lower point, move out of the clip zone */
1784 cairo_line_to (cr, xoff - 10, yoff);
1786 /* close path to fill */
1788 cairo_close_path (cr);
1790 /* fill and stroke */
1792 cairo_set_source_rgba (cr,
1793 (waveview->fill_r/255.0),
1794 (waveview->fill_g/255.0),
1795 (waveview->fill_b/255.0),
1796 (waveview->fill_a/255.0));
1797 cairo_fill_preserve (cr);
1798 cairo_set_source_rgba (cr,
1799 (waveview->wave_r/255.0),
1800 (waveview->wave_g/255.0),
1801 (waveview->wave_b/255.0),
1802 (waveview->wave_a/255.0));
1803 cairo_stroke (cr);
1805 cairo_destroy (cr);
1808 #if 0
1809 if (clip_max || clip_min) {
1810 cairo_set_source_rgba (cr, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a);
1813 if (clip_max) {
1814 cairo_move_to (cr, xoff, yoff1);
1815 cairo_line_to (cr, xoff, yoff1 + clip_length);
1816 cairo_stroke (cr);
1819 if (clip_min) {
1820 cairo_move_to (cr, xoff, yoff2);
1821 cairo_line_to (cr, xoff, yoff2 - clip_length);
1822 cairo_stroke (cr);
1825 #endif
1827 static void
1828 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1830 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1832 *x1 = waveview->x;
1833 *y1 = waveview->y;
1835 *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1836 *y2 = *y1 + waveview->height;
1838 #if 0
1839 x = 0; y = 0;
1840 gnome_canvas_item_i2w (item, &x, &y);
1841 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1842 x = *x2;
1843 y = *y2;
1844 gnome_canvas_item_i2w (item, &x, &y);
1845 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1846 printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1847 #endif
1851 static double
1852 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1854 /* XXX for now, point is never inside the wave
1855 GnomeCanvasWaveView *waveview;
1856 double x1, y1, x2, y2;
1857 double dx, dy;
1860 return DBL_MAX;
1862 #if 0
1863 waveview = GNOME_CANVAS_WAVEVIEW (item);
1865 *actual_item = item;
1867 /* Find the bounds for the rectangle plus its outline width */
1869 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1871 /* Is point inside rectangle */
1873 if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1874 return 0.0;
1877 /* Point is outside rectangle */
1879 if (x < x1)
1880 dx = x1 - x;
1881 else if (x > x2)
1882 dx = x - x2;
1883 else
1884 dx = 0.0;
1886 if (y < y1)
1887 dy = y1 - y;
1888 else if (y > y2)
1889 dy = y - y2;
1890 else
1891 dy = 0.0;
1893 return sqrt (dx * dx + dy * dy);
1894 #endif