/* This file is part of the Q programming system. The Q programming system is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. The Q programming system is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #if defined (HAVE_CONFIG_H) # include "config.h" #endif #ifdef _WIN32 #define STDC_HEADERS 1 #define HAVE_STRDUP 1 #define HAVE_MEMCPY 1 #define HAVE_LIMITS_H 1 #include #endif /* system headers */ #include #include #include /* check for standard C headers */ #if STDC_HEADERS # include # include #else # ifndef HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); #endif #ifdef HAVE_MALLOC_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef DMALLOC #include #endif #include #include #include "ggilib.h" MODULE(draw_wave) typedef struct bstr { long size; unsigned char *v; } bstr_t; typedef struct { ggi_visual_t vis; } visual_t; static void shade(ggi_color *c) { if (c->r || c->g || c->b) { c->r -= c->r>>2; c->g -= c->g>>2; c->b -= c->b>>2; } else { c->r = c->g = c->b = 0x4000; } } FUNCTION(draw_wave,draw_wave,argc,argv) { visual_t *v; ggi_visual_t vis; expr *xv; int n; long x, y, w, h, channels, format, chan; bstr_t *m; if (argc == 7 && isobj(argv[0], type(GGIVisual), (void**)&v) && (vis = v->vis) && istuple(argv[1], &n, &xv) && n == 2 && isint(xv[0], &x) && isint(xv[1], &y) && istuple(argv[2], &n, &xv) && n == 2 && isint(xv[0], &w) && isint(xv[1], &h) && w > 0 && h > 0 && isint(argv[3], &chan) && isint(argv[4], &channels) && chan >= 0 && chan < channels && isint(argv[5], &format) && isobj(argv[6], type(ByteStr), (void**)&m)) { int bps, bpf; long samples, frames; switch (format) { case paFloat32: bps = 4; break; case paInt32: bps = 4; break; case paInt16: bps = 2; break; default: return __FAIL; } bpf = bps*channels; frames = m->size/bpf; samples = frames*channels; if (frames <= 0) return mkvoid; else if (frames <= w) { double dx = ((double)w-1)/(frames-1); switch (format) { case paFloat32: { long i, k; float *data = (float*)m->v; int show_dots = (dx>=5.0), xa, ya; for (i = k = 0; i < samples && k < w; i += channels, k++) { int val = (int)((data[i+chan]+1.0f)/2.0f*(h-1)); int xe = (int)x+k*dx, ye = y+h-1-val; if (k > 0 && draw_line(v, xa, ya, xe, ye)) return __FAIL; if (show_dots && draw_box(v, xe-1, ye-1, 3, 3) < 0) return __FAIL; xa = xe; ya = ye; } break; } case paInt32: { long i, k; int *data = (int*)m->v; int show_dots = (dx>=5.0), xa, ya; for (i = k = 0; i < samples && k < w; i += channels, k++) { double samp = data[i+chan]*(1.0/0x7FFFFFFF); int val = (int)((samp+1.0)/2.0*(h-1)); int xe = (int)x+k*dx, ye = y+h-1-val; if (k > 0 && draw_line(v, xa, ya, xe, ye)) return __FAIL; if (show_dots && draw_box(v, xe-1, ye-1, 3, 3) < 0) return __FAIL; xa = xe; ya = ye; } break; } case paInt16: { long i, k; short *data = (short*)m->v; int show_dots = (dx>=5.0), xa, ya; for (i = k = 0; i < samples && k < w; i += channels, k++) { double samp = data[i+chan]*(1.0/0x7FFF); int val = (int)((samp+1.0)/2.0*(h-1)); int xe = (int)x+k*dx, ye = y+h-1-val; if (k > 0 && draw_line(v, xa, ya, xe, ye)) return __FAIL; if (show_dots && draw_box(v, xe-1, ye-1, 3, 3) < 0) return __FAIL; xa = xe; ya = ye; } break; } } #ifdef DEBUG printf("%d frames, 1 sample per pixel\n", frames); #endif } else { int fpp = frames/w+((frames%w==0)?0:1), spp = fpp*channels, vals = frames/fpp+((frames%fpp==0)?0:1); int *min = malloc(vals*sizeof(int)), *max = malloc(vals*sizeof(int)), *rmsh = malloc(vals*sizeof(int)), *rmsl = malloc(vals*sizeof(int)); double dx = ((double)w-1)/(vals-1), sum, mean; long i, j, k, n; ggi_pixel pix, pix2; ggi_color col; if (!min || !max || !rmsh || !rmsl) { if (min) free(min); if (max) free(max); if (rmsh) free(rmsh); if (rmsl) free(rmsl); return __ERROR; } switch (format) { case paFloat32: { float *data = (float*)m->v; for (i = k = 0; k < vals; i += spp, k++) { min[k] = w; max[k] = -1; sum = 0.0; n = 0; for (j = i; j < i+spp && j+chan < samples; j += channels) { double samp = data[j+chan]; int val = (int)((samp+1.0f)/2.0f*(h-1)); if (valmax[k]) max[k] = val; sum += samp*samp; n++; } mean = sqrt(sum/n); rmsh[k] = (int)((mean+1.0f)/2.0f*(h-1)); rmsl[k] = (int)((-mean+1.0f)/2.0f*(h-1)); if (k > 0) { if (max[k] < min[k-1]) max[k] = min[k-1]-1; if (min[k] > max[k-1]) min[k] = max[k-1]+1; } if (min[k] < 0) min[k] = 0; if (min[k] >= w) min[k] = h-1; if (max[k] < 0) max[k] = 0; if (max[k] >= w) max[k] = h-1; if (min[k] > max[k]) min[k] = max[k]; if (rmsh[k] >= max[k]) rmsh[k] = max[k]-1; if (rmsl[k] <= min[k]) rmsl[k] = min[k]+1; } break; } case paInt32: { int *data = (int*)m->v; for (i = k = 0; k < vals; i += spp, k++) { min[k] = w; max[k] = -1; sum = 0.0; n = 0; for (j = i; j < i+spp && j+chan < samples; j += channels) { double samp = data[j+chan]*(1.0/0x7FFFFFFF); int val = (int)((samp+1.0)/2.0*(h-1)); if (valmax[k]) max[k] = val; sum += samp*samp; n++; } mean = sqrt(sum/n); rmsh[k] = (int)((mean+1.0f)/2.0f*(h-1)); rmsl[k] = (int)((-mean+1.0f)/2.0f*(h-1)); if (k > 0) { if (max[k] < min[k-1]) max[k] = min[k-1]-1; if (min[k] > max[k-1]) min[k] = max[k-1]+1; } if (min[k] < 0) min[k] = 0; if (min[k] >= w) min[k] = h-1; if (max[k] < 0) max[k] = 0; if (max[k] >= w) max[k] = h-1; if (min[k] > max[k]) min[k] = max[k]; if (rmsh[k] >= max[k]) rmsh[k] = max[k]-1; if (rmsl[k] <= min[k]) rmsl[k] = min[k]+1; } break; } case paInt16: { short *data = (short*)m->v; for (i = k = 0; k < vals; i += spp, k++) { min[k] = w; max[k] = -1; sum = 0.0; n = 0; for (j = i; j < i+spp && j+chan < samples; j += channels) { double samp = data[j+chan]*(1.0/0x7FFF); int val = (int)((samp+1.0)/2.0*(h-1)); if (valmax[k]) max[k] = val; sum += samp*samp; n++; } mean = sqrt(sum/n); rmsh[k] = (int)((mean+1.0f)/2.0f*(h-1)); rmsl[k] = (int)((-mean+1.0f)/2.0f*(h-1)); if (k > 0) { if (max[k] < min[k-1]) max[k] = min[k-1]-1; if (min[k] > max[k-1]) min[k] = max[k-1]+1; } if (min[k] < 0) min[k] = 0; if (min[k] >= w) min[k] = h-1; if (max[k] < 0) max[k] = 0; if (max[k] >= w) max[k] = h-1; if (min[k] > max[k]) min[k] = max[k]; if (rmsh[k] >= max[k]) rmsh[k] = max[k]-1; if (rmsl[k] <= min[k]) rmsl[k] = min[k]+1; } break; } } ggiGetGCForeground(vis, &pix); if (ggiUnmapPixel(vis, pix, &col)) return __FAIL; shade(&col); pix2 = ggiMapColor(vis, &col); for (k = 0; k < vals; k++) { int xa, xe = (int)x+(k+1)*dx; for (xa = (int)x+k*dx; xa < xe; xa++) if (draw_vline(v, xa, y+h-1-max[k], max[k]-min[k]+1) < 0) return __FAIL; } ggiSetGCForeground(vis, pix2); for (k = 0; k < vals; k++) { int xa, xe = (int)x+(k+1)*dx; if (rmsl[k] <= rmsh[k]) for (xa = (int)x+k*dx; xa < xe; xa++) if (draw_vline(v, xa, y+h-1-rmsh[k], rmsh[k]-rmsl[k]+1) < 0) return __FAIL; } ggiSetGCForeground(vis, pix); free(min); free(max); free(rmsh); free(rmsl); #ifdef DEBUG printf("%d frames, %d samples per pixel\n", frames, fpp); #endif } return mkvoid; } else return __FAIL; }