1/*
2 * Copyright (C) 1997-2005 Kare Sjolander <kare@speech.kth.se>
3 *
4 * This file is part of the Snack Sound Toolkit.
5 * The latest version can be found at http://www.speech.kth.se/snack/
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include "tcl.h"
23#include "snack.h"
24#include <stdio.h>
25#include <stdlib.h>
26#include <math.h>
27#define USE_OLD_CANVAS /* To keep Tk8.3 happy */
28#include "tk.h"
29#include "jkCanvItems.h"
30#include <string.h>
31
32/*
33 * Wave item structure
34 */
35
36typedef struct WaveItem  {
37
38  Tk_Item header;
39  Tk_Canvas canvas;
40  double x, y;
41  Tk_Anchor anchor;
42  double *x0;
43  double *y0;
44  double *x1;
45  double *y1;
46  XColor *fg;
47  Pixmap fillStipple;
48  GC gc;
49  char *newSoundName;
50  char *soundName;
51  Sound *sound;
52  int channel;
53  int channelSet;
54  int nchannels;
55  int samprate;
56  int encoding;
57  float **blocks;
58  int bufPos;
59  double limit;
60  int subSample;
61  double pixpsec;
62  int height;
63  int width;
64  int widthSet;
65  int startSmp;
66  int endSmp;
67  int ssmp;
68  int esmp;
69  int zeroLevel;
70  int frame;
71  int id;
72  int mode;
73  int subSampleInt;
74  char *channelStr;
75  int debug;
76  int storeType;
77  char *preCompFile;
78  struct WaveItem *preWI;
79  Sound *preSound;
80  int preCompInvalid;
81  int validStart;
82  char *progressCmd;
83  Tcl_Obj *cmdPtr;
84  Tcl_Interp *interp;
85  int trimstart;
86  float maxv;
87  float minv;
88  int remove; /* remove for 2.1 */
89
90} WaveItem;
91
92Tk_CustomOption waveTagsOption = { (Tk_OptionParseProc *) NULL,
93				   (Tk_OptionPrintProc *) NULL,
94				   (ClientData) NULL };
95
96typedef enum {
97  OPTION_ANCHOR,
98  OPTION_TAGS,
99  OPTION_SOUND,
100  OPTION_HEIGHT,
101  OPTION_WIDTH,
102  OPTION_PIXPSEC,
103  OPTION_START,
104  OPTION_END,
105  OPTION_FILL,
106  OPTION_STIPPLE,
107  OPTION_ZEROLEVEL,
108  OPTION_FRAME,
109  OPTION_LIMIT,
110  OPTION_SUBSAMPLE,
111  OPTION_CHANNEL,
112  OPTION_PRECOMPWAVE,
113  OPTION_PROGRESS,
114  OPTION_TRIMSTART
115} ConfigSpec;
116
117static Tk_ConfigSpec configSpecs[] = {
118
119  {TK_CONFIG_ANCHOR, "-anchor", (char *) NULL, (char *) NULL,
120   "nw", Tk_Offset(WaveItem, anchor), TK_CONFIG_DONT_SET_DEFAULT},
121
122  {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
123   (char *) NULL, 0, TK_CONFIG_NULL_OK, &waveTagsOption},
124
125  {TK_CONFIG_STRING, "-sound", (char *) NULL, (char *) NULL,
126   "", Tk_Offset(WaveItem, newSoundName), TK_CONFIG_NULL_OK},
127
128  {TK_CONFIG_INT, "-height", (char *) NULL, (char *) NULL,
129   "100", Tk_Offset(WaveItem, height), 0},
130
131  {TK_CONFIG_PIXELS, "-width", (char *) NULL, (char *) NULL,
132   "378", Tk_Offset(WaveItem, widthSet), 0},
133
134  {TK_CONFIG_DOUBLE, "-pixelspersecond", "pps", (char *) NULL,
135   "250.0", Tk_Offset(WaveItem, pixpsec), 0},
136
137  {TK_CONFIG_INT, "-start", (char *) NULL, (char *) NULL,
138   "0", Tk_Offset(WaveItem, startSmp), 0},
139
140  {TK_CONFIG_INT, "-end", (char *) NULL, (char *) NULL,
141   "-1", Tk_Offset(WaveItem, endSmp), 0},
142
143  {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL,
144   "black", Tk_Offset(WaveItem, fg), TK_CONFIG_NULL_OK},
145
146  {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL,
147   (char *) NULL, Tk_Offset(WaveItem, fillStipple), TK_CONFIG_NULL_OK},
148
149  {TK_CONFIG_BOOLEAN, "-zerolevel", "zerolevel", (char *) NULL,
150   "yes", Tk_Offset(WaveItem, zeroLevel), TK_CONFIG_NULL_OK},
151
152  {TK_CONFIG_BOOLEAN, "-frame", (char *) NULL, (char *) NULL,
153   "no", Tk_Offset(WaveItem, frame), TK_CONFIG_NULL_OK},
154
155  {TK_CONFIG_DOUBLE, "-limit", (char *) NULL, (char *) NULL,
156   "-1.0", Tk_Offset(WaveItem, limit), 0},
157
158  {TK_CONFIG_INT, "-subsample", (char *) NULL, (char *) NULL,
159   "1", Tk_Offset(WaveItem, subSampleInt), TK_CONFIG_NULL_OK},
160
161  {TK_CONFIG_STRING, "-channel", (char *) NULL, (char *) NULL,
162   "-1", Tk_Offset(WaveItem, channelStr), TK_CONFIG_NULL_OK},
163
164  {TK_CONFIG_STRING, "-shapefile", (char *) NULL, (char *) NULL,
165   "", Tk_Offset(WaveItem, preCompFile), TK_CONFIG_NULL_OK},
166
167  {TK_CONFIG_STRING, "-progress", (char *) NULL, (char *) NULL,
168   "", Tk_Offset(WaveItem, progressCmd), TK_CONFIG_NULL_OK},
169
170  {TK_CONFIG_INT, "-trimstart", (char *) NULL, (char *) NULL,
171   "0", Tk_Offset(WaveItem, trimstart), 0},
172
173  /* To be removed for 2.1 */
174  {TK_CONFIG_INT, "-tround", (char *) NULL, (char *) NULL,
175   "0", Tk_Offset(WaveItem, remove), 0},
176
177  {TK_CONFIG_INT, "-debug", (char *) NULL, (char *) NULL,
178   "0", Tk_Offset(WaveItem, debug), 0},
179
180  {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
181   (char *) NULL, 0, 0}
182
183};
184
185/*
186 * Protos
187 */
188
189  static void   ComputeWaveBbox(Tk_Canvas canvas, WaveItem *wavePtr);
190
191  static int    ComputeWaveCoords(Tk_Item *itemPtr);
192
193  static int    ConfigureWave(Tcl_Interp *interp, Tk_Canvas canvas,
194			      Tk_Item *itemPtr, int argc,
195			      char **argv, int flags);
196
197  static int    CreateWave(Tcl_Interp *interp, Tk_Canvas canvas,
198			   struct Tk_Item *itemPtr,
199			   int argc, char **argv);
200
201  static void   DeleteWave(Tk_Canvas canvas, Tk_Item *itemPtr,
202			   Display *display);
203
204  static void   DisplayWave(Tk_Canvas canvas, Tk_Item *itemPtr,
205			    Display *display, Drawable dst,
206			    int x, int y, int width, int height);
207
208  static void   ScaleWave(Tk_Canvas canvas, Tk_Item *itemPtr,
209			  double originX, double originY,
210			  double scaleX, double scaleY);
211
212  static void   TranslateWave(Tk_Canvas canvas, Tk_Item *itemPtr,
213			      double deltaX, double deltaY);
214
215  static int    WaveCoords(Tcl_Interp *interp, Tk_Canvas canvas,
216			   Tk_Item *itemPtr, int argc, char **argv);
217
218  static int    WaveToArea(Tk_Canvas canvas, Tk_Item *itemPtr,
219			   double *rectPtr);
220
221  static double WaveToPoint(Tk_Canvas canvas, Tk_Item *itemPtr,
222			    double *coords);
223
224  static int    WaveToPS(Tcl_Interp *interp, Tk_Canvas canvas,
225			 Tk_Item *itemPtr, int prepass);
226
227/*
228 * Wave item type
229 */
230
231Tk_ItemType snackWaveType = {
232  "waveform",
233  sizeof(WaveItem),
234  CreateWave,
235  configSpecs,
236  ConfigureWave,
237  WaveCoords,
238  DeleteWave,
239  DisplayWave,
240  0,
241  WaveToPoint,
242  WaveToArea,
243  WaveToPS,
244  ScaleWave,
245  TranslateWave,
246  (Tk_ItemIndexProc *) NULL,
247  (Tk_ItemCursorProc *) NULL,
248  (Tk_ItemSelectionProc *) NULL,
249  (Tk_ItemInsertProc *) NULL,
250  (Tk_ItemDCharsProc *) NULL,
251  (Tk_ItemType *) NULL
252};
253
254static int
255CreateWave(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr,
256	   int argc, char **argv)
257{
258  WaveItem *wavePtr = (WaveItem *) itemPtr;
259
260  if (argc < 2) {
261    Tcl_AppendResult(interp, "wrong # args: should be \"",
262		     Tk_PathName(Tk_CanvasTkwin(canvas)), " create ",
263		     itemPtr->typePtr->name, " x y ?opts?\"", (char *) NULL);
264    return TCL_ERROR;
265  }
266
267  wavePtr->canvas = canvas;
268  wavePtr->anchor = TK_ANCHOR_NW;
269  wavePtr->x0 = NULL;
270  wavePtr->y0 = NULL;
271  wavePtr->x1 = NULL;
272  wavePtr->y1 = NULL;
273  wavePtr->fg = None;
274  wavePtr->fillStipple = None;
275  wavePtr->gc = None;
276  wavePtr->newSoundName = NULL;
277  wavePtr->soundName = NULL;
278  wavePtr->sound = NULL;
279  wavePtr->pixpsec = 250.0;
280  wavePtr->height = 100;
281  wavePtr->width = -1;
282  wavePtr->widthSet = 378;
283  wavePtr->startSmp = 0;
284  wavePtr->endSmp = -1;
285  wavePtr->ssmp = 0;
286  wavePtr->esmp = -1;
287  wavePtr->id = 0;
288  wavePtr->mode = CONF_WIDTH;
289  wavePtr->zeroLevel = 1;
290  wavePtr->frame = 0;
291  wavePtr->channelStr = NULL;
292  wavePtr->channel = -1;
293  wavePtr->channelSet = -1;
294  wavePtr->nchannels = 1;
295  wavePtr->samprate = 16000;
296  wavePtr->encoding = LIN16;
297  wavePtr->bufPos = 0;
298  wavePtr->limit = -1.0;
299  wavePtr->subSampleInt = 1;
300  wavePtr->subSample = 1;
301  wavePtr->preCompFile = NULL;
302  wavePtr->preSound = NULL;
303  wavePtr->preWI = NULL;
304  wavePtr->preCompInvalid = 0;
305  wavePtr->validStart = 0;
306  wavePtr->progressCmd = NULL;
307  wavePtr->cmdPtr = NULL;
308  wavePtr->interp = interp;
309  wavePtr->trimstart = 0;
310  wavePtr->maxv = 0.0f;
311  wavePtr->minv = 0.0f;
312  wavePtr->debug = 0;
313  wavePtr->x = 0;
314  wavePtr->y = 0;
315
316  if ((Tk_CanvasGetCoord(interp, canvas, argv[0], &wavePtr->x) != TCL_OK) ||
317      (Tk_CanvasGetCoord(interp, canvas, argv[1], &wavePtr->y) != TCL_OK))
318    return TCL_ERROR;
319
320  if (ConfigureWave(interp, canvas, itemPtr, argc-2, argv+2, 0) == TCL_OK)
321    return TCL_OK;
322
323  DeleteWave(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
324  return TCL_ERROR;
325}
326
327static void
328WaveMaxMin(WaveItem *wavePtr, SnackLinkedFileInfo *info, int start, int stop,
329	   float *maxi, float *mini)
330{
331  int i, j, allFlag = 0;
332  float maxval = -8388608.0, minval = 8388607.0, val;
333  int nchan = wavePtr->nchannels, chan = wavePtr->channel;
334  int inc = nchan * wavePtr->subSample;
335
336  if (start < 0 || stop > wavePtr->bufPos - 1 || stop == 0 ||
337      (wavePtr->blocks[0] == NULL && wavePtr->storeType == SOUND_IN_MEMORY)) {
338    if (wavePtr->encoding == LIN8OFFSET) {
339      *maxi = 128.0;
340      *mini = 128.0;
341    } else {
342      *maxi = 0.0;
343      *mini = 0.0;
344    }
345    return;
346  }
347  if (chan == -1) {
348    allFlag = 1;
349    chan = 0;
350  }
351
352  start = start * wavePtr->nchannels + chan;
353  stop  = stop  * wavePtr->nchannels + chan + wavePtr->nchannels - 1;
354
355  for (i = start; i <= stop; i += inc) {
356    if (wavePtr->storeType == SOUND_IN_MEMORY) {
357      val = FSAMPLE(wavePtr, i);
358      if (allFlag) {
359	for (j = 1; j < nchan; j++) {
360	  val += FSAMPLE(wavePtr, i + j);
361	}
362	val = val / nchan;
363      }
364    } else {
365      val = GetSample(info, i);
366      if (allFlag) {
367	for (j = 1; j < nchan; j++) {
368	  val += GetSample(info, i + j);
369	}
370	val = val / nchan;
371      }
372    }
373    if (val > maxval) {
374      maxval = val;
375    }
376    if (val < minval) {
377      minval = val;
378    }
379  }
380  if (wavePtr->limit > 0.0) {
381    if (maxval > wavePtr->limit) {
382      maxval = (float) wavePtr->limit;
383    }
384    if (minval < -wavePtr->limit) {
385      minval = (float) -wavePtr->limit;
386    }
387  }
388  *maxi = maxval;
389  *mini = minval;
390}
391
392static int
393WaveCoords(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr, int argc,
394	   char **argv)
395{
396  WaveItem *wPtr = (WaveItem *) itemPtr;
397  char xc[TCL_DOUBLE_SPACE], yc[TCL_DOUBLE_SPACE];
398
399  if (argc == 0) {
400    Tcl_PrintDouble(interp, wPtr->x, xc);
401    Tcl_PrintDouble(interp, wPtr->y, yc);
402    Tcl_AppendResult(interp, xc, " ", yc, (char *) NULL);
403  } else if (argc == 2) {
404    if ((Tk_CanvasGetCoord(interp, canvas, argv[0], &wPtr->x) != TCL_OK) ||
405	(Tk_CanvasGetCoord(interp, canvas, argv[1], &wPtr->y) != TCL_OK)) {
406      return TCL_ERROR;
407    }
408    ComputeWaveBbox(canvas, wPtr);
409  } else {
410    char buf[80];
411
412    sprintf(buf, "wrong # coordinates: expected 0 or 2, got %d", argc);
413    Tcl_SetResult(interp, buf, TCL_VOLATILE);
414
415    return TCL_ERROR;
416  }
417
418  return TCL_OK;
419}
420
421/*#define WIDEWAVE 100000*/
422
423static int
424ComputeWaveCoords(Tk_Item *itemPtr)
425{
426  WaveItem *wavePtr = (WaveItem *) itemPtr;
427  int i = 0;
428  float maxv, minv, abmax;
429  int yh = wavePtr->height / 2;
430  int nPoints = wavePtr->width;
431  SnackLinkedFileInfo info;
432  Tcl_Interp *interp;
433  int usePre = 0;
434
435  if (wavePtr->debug > 1) Snack_WriteLog("  Enter ComputeWaveCoords\n");
436
437  if (wavePtr->x0 != NULL) {
438    ckfree((char *) wavePtr->x0);
439  }
440  if (wavePtr->y0 != NULL) {
441    ckfree((char *) wavePtr->y0);
442  }
443  if (wavePtr->x1 != NULL) {
444    ckfree((char *) wavePtr->x1);
445  }
446  if (wavePtr->y1 != NULL) {
447    ckfree((char *) wavePtr->y1);
448  }
449  wavePtr->x0 = (double *) ckalloc(sizeof(double) * nPoints);
450  wavePtr->y0 = (double *) ckalloc(sizeof(double) * nPoints);
451  wavePtr->x1 = (double *) ckalloc(sizeof(double) * nPoints);
452  wavePtr->y1 = (double *) ckalloc(sizeof(double) * nPoints);
453
454  if (wavePtr->sound == NULL) {
455    for (i = 0; i < nPoints; i++) {
456      wavePtr->x0[i] = (double) i;
457      wavePtr->y0[i] = (double) yh;
458      wavePtr->x1[i] = (double) i;
459      wavePtr->y1[i] = (double) yh;
460    }
461    return TCL_OK;
462  }
463
464  maxv = wavePtr->sound->maxsamp;
465  minv = wavePtr->sound->minsamp;
466  abmax = wavePtr->sound->abmax;
467  interp = wavePtr->sound->interp;
468
469  if (wavePtr->preCompFile != NULL && wavePtr->sound->readStatus != READ) {
470    char *type = NULL;
471
472    if (wavePtr->preSound != NULL) {
473      wavePtr->preSound->fcname = NULL;
474      Snack_DeleteSound(wavePtr->preSound);
475    }
476    wavePtr->preSound = Snack_NewSound(200, LIN8, wavePtr->sound->nchannels);
477    if (wavePtr->preSound != NULL) {
478      wavePtr->preSound->fcname = wavePtr->preCompFile;
479      type = LoadSound(wavePtr->preSound, interp, NULL, 0, -1);
480      if (wavePtr->preWI != NULL) ckfree((char *)wavePtr->preWI);
481      wavePtr->preWI = (WaveItem *) ckalloc(sizeof(WaveItem));
482      if (wavePtr->preWI != NULL) {
483	wavePtr->preWI->nchannels = wavePtr->preSound->nchannels;
484	wavePtr->preWI->channel = 0;
485	wavePtr->preWI->subSample = 1;
486	wavePtr->preWI->bufPos = wavePtr->preSound->length;
487	wavePtr->preWI->blocks = wavePtr->preSound->blocks;
488	wavePtr->preWI->storeType = SOUND_IN_MEMORY;
489	wavePtr->preWI->encoding = LIN8;
490	wavePtr->preWI->limit = wavePtr->limit;
491      }
492    }
493
494    if ((type == NULL || wavePtr->preCompInvalid) && wavePtr->preSound!=NULL) {
495
496      /* Compute and store wave */
497
498      int nStore = (int) (200.0 * wavePtr->sound->length
499			  / wavePtr->sound->samprate);
500      int j;
501      int tmp = wavePtr->channel;
502
503      maxv = 0.0f;
504      minv = 0.0f;
505      if (wavePtr->debug > 2) Snack_WriteLog("    Saving computed waveform\n");
506      wavePtr->preCompInvalid = 0;
507      Snack_ResizeSoundStorage(wavePtr->preSound, nStore);
508      wavePtr->preSound->length = nStore;
509      if (wavePtr->cmdPtr != NULL) {
510	Snack_ProgressCallback(wavePtr->cmdPtr, interp,
511			       "Computing waveform", 0.0);
512      }
513
514      if (wavePtr->storeType != SOUND_IN_MEMORY) {
515	if (OpenLinkedFile(wavePtr->sound, &info) != TCL_OK) {
516	  for (i = 0; i < nPoints; i++) {
517	    wavePtr->x0[i] = (double) i;
518	    wavePtr->y0[i] = (double) yh;
519	    wavePtr->x1[i] = (double) i;
520	    wavePtr->y1[i] = (double) yh;
521	  }
522	  if (wavePtr->cmdPtr != NULL) {
523	    Snack_ProgressCallback(wavePtr->cmdPtr, interp,
524				   "Computing waveform", 1.0);
525	  }
526	  return TCL_OK;
527	}
528      }
529      for (i = 0; i < nStore / 2; i++) {
530	for (j = 0; j < wavePtr->sound->nchannels; j++) {
531	  float fraq = (float) wavePtr->sound->length / (nStore / 2);
532	  int start = (int) (i     * fraq);
533	  int stop  = (int) ((i+1) * fraq);
534	  float wtop, wbot;
535
536	  wavePtr->channel = j;
537	  WaveMaxMin(wavePtr, &info, start, stop, &wtop, &wbot);
538
539	  if (maxv < wtop) maxv = wtop;
540	  if (minv > wbot) minv = wbot;
541
542	  switch (wavePtr->encoding) {
543	  case LIN16:
544	  case MULAW:
545	  case ALAW:
546	    wtop = wtop / 256.0f;
547	    wbot = wbot / 256.0f;
548	    break;
549	  case LIN24:
550	    wtop = wtop / 65536.0f;
551	    wbot = wbot / 65536.0f;
552	    break;
553	  case LIN32:
554	    wtop = wtop / 16777216.0f;
555	    wbot = wbot / 16777216.0f;
556	    break;
557	  case SNACK_FLOAT:
558	    wtop = (wtop / abmax) * 128.0f;
559	    wbot = (wbot / abmax) * 128.0f;
560	    break;
561	  case LIN8OFFSET:
562	    wtop -= 128.0f;
563	    wbot -= 128.0f;
564	    break;
565	  case LIN8:
566	    break;
567	  }
568	  Snack_SetSample(wavePtr->preSound, j, i*2,   (char) wtop);
569	  Snack_SetSample(wavePtr->preSound, j, i*2+1, (char) wbot);
570	  if (j == 0 && (wavePtr->cmdPtr != NULL) && ((i % 1000) == 999)) {
571	    int res = Snack_ProgressCallback(wavePtr->cmdPtr, interp,
572			     "Computing waveform", (double) i/(nStore/2));
573	    if (res != TCL_OK) {
574	      if (wavePtr->debug > 2) {
575		Snack_WriteLog("    Aborting ComputeWaveCoords\n");
576	      }
577	      for (;i < nStore / 2; i++) {
578		for (j = 0; j < wavePtr->sound->nchannels; j++) {
579		  Snack_SetSample(wavePtr->preSound, j, i*2,   (char) 0);
580		  Snack_SetSample(wavePtr->preSound, j, i*2+1, (char) 0);
581		}
582	      }
583	      break;
584	    }
585	  }
586	}
587      }
588      if (wavePtr->cmdPtr != NULL) {
589	Snack_ProgressCallback(wavePtr->cmdPtr, interp,
590			       "Computing waveform", 1.0);
591      }
592      if (SaveSound(wavePtr->preSound, interp, wavePtr->preCompFile, NULL,
593		    0, NULL, 0, wavePtr->preSound->length, AIFF_STRING) ==
594	  TCL_ERROR) {
595	if (wavePtr->debug > 2) Snack_WriteLog("    Failed saving waveform\n");
596	wavePtr->preCompFile = NULL;
597      }
598      wavePtr->preWI->bufPos = wavePtr->preSound->length;
599      wavePtr->preWI->blocks = wavePtr->preSound->blocks;
600      if (wavePtr->storeType != SOUND_IN_MEMORY) {
601	CloseLinkedFile(&info);
602      }
603      wavePtr->channel = tmp;
604    }
605
606    if (wavePtr->preSound != NULL && wavePtr->preWI != NULL) {
607
608      /* Use precomputed wave */
609
610      float left  = ((float) wavePtr->ssmp / wavePtr->sound->length) *
611	wavePtr->preSound->length;
612      float right = ((float) wavePtr->esmp / wavePtr->sound->length) *
613	wavePtr->preSound->length;
614      float fraq  = (right - left) / (nPoints * 2);
615
616      if (fraq > 1.0) {
617	usePre = 1;
618	switch (wavePtr->encoding) {
619	case LIN16:
620	case MULAW:
621	case ALAW:
622	  maxv = maxv / 256.0f;
623	  minv = minv / 256.0f;
624	  break;
625	case LIN24:
626	  maxv = maxv / 65536.0f;
627	  minv = minv / 65536.0f;
628	  break;
629	case LIN32:
630	  maxv = maxv / 16777216.0f;
631	  minv = minv / 16777216.0f;
632	  break;
633	case SNACK_FLOAT:
634	  maxv = (maxv / abmax) * 128.0f;
635	  minv = (minv / abmax) * 128.0f;
636	  break;
637	case LIN8OFFSET:
638	  maxv -= 128.0f;
639	  minv -= 128.0f;
640	  break;
641	case LIN8:
642	  break;
643	}
644
645	if (wavePtr->debug > 2) {
646	  Snack_WriteLog("    Using precomputed waveform\n");
647	}
648
649	wavePtr->preWI->channel = wavePtr->channel;
650	for (i = 0; i < nPoints; i++) {
651	  int start = (int) (left + 2*(i * fraq));
652	  int stop  = (int) (left + 2*(i+1)*fraq);
653	  float wtop, wbot;
654
655	  WaveMaxMin(wavePtr->preWI, NULL, start, stop, &wtop, &wbot);
656
657	  if (maxv < wtop) maxv = wtop;
658	  if (minv > wbot) minv = wbot;
659
660	  wavePtr->x0[i] = i;
661	  wavePtr->x1[i] = i;
662	  if (i > 0 && wavePtr->y1[i-1] <= wtop) {
663	    wavePtr->y0[i] = wtop;
664	    wavePtr->y1[i] = wbot;
665	  } else {
666	    wavePtr->y0[i] = wbot;
667	    wavePtr->y1[i] = wtop;
668	  }
669	}
670
671	if (wavePtr->encoding == LIN8OFFSET) {
672	  maxv += 128.0f;
673	  minv += 128.0f;
674	}
675      } else {
676	usePre = 0;
677      }
678    }
679  }
680
681  if (!usePre) {
682
683    if (wavePtr->debug > 2) {
684      Snack_WriteLog("    Default waveform computation\n");
685    }
686
687    if (wavePtr->storeType != SOUND_IN_MEMORY) {
688      if (OpenLinkedFile(wavePtr->sound, &info) != TCL_OK) {
689	for (i = 0; i < nPoints; i++) {
690	  wavePtr->x0[i] = (double) i;
691	  wavePtr->y0[i] = (double) yh;
692	  wavePtr->x1[i] = (double) i;
693	  wavePtr->y1[i] = (double) yh;
694	}
695	  return TCL_OK;
696      }
697    }
698
699    for (i = 0; i < nPoints; i++) {
700      float fraq = (float) (wavePtr->esmp - wavePtr->ssmp) / nPoints;
701      int start = wavePtr->ssmp + (int) (i     * fraq) - wavePtr->validStart;
702      int stop  = wavePtr->ssmp + (int) ((i+1) * fraq) - wavePtr->validStart;
703      float wtop, wbot;
704
705      if (wavePtr->trimstart == 1) {
706	start = (int)(wavePtr->subSample*ceil((float)start/wavePtr->subSample));
707      }
708
709      WaveMaxMin(wavePtr, &info, start, stop, &wtop, &wbot);
710
711      if (maxv < wtop) maxv = wtop;
712      if (minv > wbot) minv = wbot;
713
714      if (wavePtr->encoding == LIN8OFFSET) {
715	wtop -= 128.0f;
716	wbot -= 128.0f;
717      }
718
719      wavePtr->x0[i] = i;
720      wavePtr->x1[i] = i;
721      if (i > 0 && wavePtr->y1[i-1] <= wtop) {
722	wavePtr->y0[i] = wtop;
723	wavePtr->y1[i] = wbot;
724      } else {
725	wavePtr->y0[i] = wbot;
726	wavePtr->y1[i] = wtop;
727      }
728    }
729    if (wavePtr->storeType != SOUND_IN_MEMORY) {
730      CloseLinkedFile(&info);
731    }
732  }
733
734  if (maxv > wavePtr->sound->maxsamp) {
735    wavePtr->sound->maxsamp = maxv;
736  }
737  if (minv < wavePtr->sound->minsamp) {
738    wavePtr->sound->minsamp = minv;
739  }
740
741  if (wavePtr->limit > 0) {
742    maxv = (float) wavePtr->limit;
743    minv = (float) -wavePtr->limit;
744  }
745  if (wavePtr->encoding == LIN8OFFSET) {
746    maxv -= 128.0f;
747    minv -= 128.0f;
748  }
749
750  wavePtr->maxv = maxv;
751  wavePtr->minv = minv;
752
753  ComputeWaveBbox(wavePtr->canvas, wavePtr);
754
755  if (usePre) {
756    switch (wavePtr->encoding) {
757    case LIN16:
758    case MULAW:
759    case ALAW:
760      maxv = maxv * 256.0f;
761      minv = minv * 256.0f;
762      break;
763    case LIN24:
764      maxv = maxv * 65536.0f;
765      minv = minv * 65536.0f;
766      break;
767    case LIN32:
768      maxv = maxv / 16777216.0f;
769      minv = minv / 16777216.0f;
770      break;
771    case SNACK_FLOAT:
772      maxv = maxv / 128.0f;
773      minv = minv / 128.0f;
774      break;
775    case LIN8OFFSET:
776      maxv += 128.0f;
777      minv += 128.0f;
778      break;
779    case LIN8:
780      break;
781    }
782    if (maxv > wavePtr->sound->maxsamp) {
783      wavePtr->sound->maxsamp = maxv;
784    }
785    if (minv < wavePtr->sound->minsamp) {
786      wavePtr->sound->minsamp = minv;
787    }
788  }
789
790  if (wavePtr->debug > 1) Snack_WriteLog("  Exit ComputeWaveCoords\n");
791
792  return TCL_OK;
793}
794
795#define NSAMPLES 100000
796
797static void
798UpdateWave(ClientData clientData, int flag)
799{
800  WaveItem *wavePtr = (WaveItem *) clientData;
801  Sound *s = wavePtr->sound;
802
803  if (wavePtr->debug > 1) Snack_WriteLogInt("  Enter UpdateWave", flag);
804
805  if (wavePtr->canvas == NULL || wavePtr->sound == NULL) return;
806
807  if (flag == SNACK_DESTROY_SOUND) {
808    wavePtr->sound = NULL;
809    if (wavePtr->id) Snack_RemoveCallback(s, wavePtr->id);
810    wavePtr->id = 0;
811    return;
812  }
813
814  Tk_CanvasEventuallyRedraw(wavePtr->canvas,
815			    wavePtr->header.x1, wavePtr->header.y1,
816			    wavePtr->header.x2, wavePtr->header.y2);
817
818  wavePtr->blocks = s->blocks;
819  wavePtr->bufPos = s->length;
820  wavePtr->storeType = s->storeType;
821
822  if ((flag == SNACK_MORE_SOUND) || (wavePtr->endSmp < 0)) {
823    wavePtr->esmp = wavePtr->bufPos - 1;
824  }
825
826  if (wavePtr->esmp > wavePtr->bufPos - 1)
827    wavePtr->esmp = wavePtr->bufPos - 1;
828
829  if (wavePtr->endSmp > 0)
830    wavePtr->esmp = wavePtr->endSmp;
831
832  if (wavePtr->endSmp > wavePtr->bufPos - 1)
833    wavePtr->esmp = wavePtr->bufPos - 1;
834
835  wavePtr->ssmp = wavePtr->startSmp;
836
837  if (wavePtr->ssmp > wavePtr->esmp)
838    wavePtr->ssmp = wavePtr->esmp;
839
840  wavePtr->samprate = s->samprate;
841  wavePtr->encoding = s->encoding;
842  wavePtr->nchannels = s->nchannels;
843
844  wavePtr->channel = wavePtr->channelSet;
845  if (wavePtr->nchannels == 1) {
846    wavePtr->channel = 0;
847  }
848
849  if (wavePtr->mode == CONF_WIDTH) {
850    if (wavePtr->esmp != wavePtr->ssmp) {
851      wavePtr->pixpsec = (double) wavePtr->width * wavePtr->samprate /
852	(wavePtr->esmp - wavePtr->ssmp);
853    }
854  }
855  else if (wavePtr->mode == CONF_PPS) {
856
857    wavePtr->width = (int)((wavePtr->esmp - wavePtr->ssmp) *
858			   wavePtr->pixpsec / wavePtr->samprate/* + 0.5*/);
859  }
860  else if (wavePtr->mode == CONF_WIDTH_PPS) {
861    wavePtr->ssmp = (int) (wavePtr->esmp - wavePtr->width *
862			   wavePtr->samprate / wavePtr->pixpsec);
863  }
864
865  if (wavePtr->subSampleInt == 0) {
866    if (wavePtr->esmp - wavePtr->ssmp > NSAMPLES) {
867      wavePtr->subSample = (wavePtr->esmp - wavePtr->ssmp) / NSAMPLES;
868    } else {
869      wavePtr->subSample = 1;
870    }
871  } else {
872    wavePtr->subSample = wavePtr->subSampleInt;
873  }
874
875  wavePtr->preCompInvalid = 1;
876  wavePtr->validStart = s->validStart;
877
878  if (ComputeWaveCoords((Tk_Item *)wavePtr) != TCL_OK) {
879    return;
880  }
881  Tk_CanvasEventuallyRedraw(wavePtr->canvas,
882			    wavePtr->header.x1, wavePtr->header.y1,
883			    wavePtr->header.x2, wavePtr->header.y2);
884
885  if (wavePtr->debug > 1) {
886    Snack_WriteLogInt("  Exit UpdateWave", wavePtr->width);
887  }
888}
889
890static int
891ConfigureWave(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr,
892	      int argc, char **argv, int flags)
893{
894  WaveItem *wavePtr = (WaveItem *) itemPtr;
895  Sound *s = wavePtr->sound;
896  Tk_Window tkwin = Tk_CanvasTkwin(canvas);
897  XGCValues gcValues;
898  GC newGC;
899  unsigned long mask;
900  int doCompute = 0, oldMode;
901  int i, j;
902
903  if (argc == 0) return TCL_OK;
904
905  if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc,
906			 (CONST84 char **)argv,
907			 (char *) wavePtr, flags) != TCL_OK) return TCL_ERROR;
908
909  if (wavePtr->debug > 1) Snack_WriteLog("  Enter ConfigureWave\n");
910
911  for (i = 0; configSpecs[i].type != TK_CONFIG_END; i++) {
912    for (j = 0; j < argc; j += 2) {
913      if (strncmp(argv[j], configSpecs[i].argvName, strlen(argv[j])) == 0) {
914	configSpecs[i].specFlags |= TK_CONFIG_OPTION_SPECIFIED;
915	break;
916      }
917    }
918  }
919
920#if defined(MAC)
921  for (i = 0; i < argc; i++) {
922    if (strncmp(argv[i], "-anchor", strlen(argv[i])) == 0) {
923      i++;
924      if (strcmp(argv[i], "ne") == 0) {
925	wavePtr->anchor = 1;
926      } else if (strcmp(argv[i], "nw") == 0) {
927	wavePtr->anchor = 7;
928      } else if (strcmp(argv[i], "n") == 0) {
929	wavePtr->anchor = 0;
930      } else if (strcmp(argv[i], "e") == 0) {
931	wavePtr->anchor = 2;
932      } else if (strcmp(argv[i], "se") == 0) {
933	wavePtr->anchor = 3;
934      } else if (strcmp(argv[i], "sw") == 0) {
935	wavePtr->anchor = 5;
936      } else if (strcmp(argv[i], "s") == 0) {
937	wavePtr->anchor = 4;
938      } else if (strcmp(argv[i], "w") == 0) {
939	wavePtr->anchor = 6;
940      } else if (strncmp(argv[i], "center", strlen(argv[i])) == 0) {
941	wavePtr->anchor = 8;
942      }
943      break;
944    }
945  }
946#endif
947
948  if (OptSpecified(OPTION_SOUND)) {
949    if (wavePtr->newSoundName == NULL) {
950      wavePtr->sound = NULL;
951      if (wavePtr->id) Snack_RemoveCallback(s, wavePtr->id);
952      wavePtr->id = 0;
953    } else {
954      if ((s = Snack_GetSound(interp, wavePtr->newSoundName)) == NULL) {
955	return TCL_ERROR;
956      }
957      if (s->storeType == SOUND_IN_CHANNEL) {
958	Tcl_AppendResult(interp, wavePtr->newSoundName,
959			 " can not be linked to a channel", (char *) NULL);
960	return TCL_ERROR;
961      }
962      if (s->storeType == SOUND_IN_FILE) {
963	s->itemRefCnt++;
964      }
965      wavePtr->sound = s;
966      if (wavePtr->soundName == NULL) {
967	wavePtr->soundName = ckalloc(strlen(wavePtr->newSoundName)+1);
968	strcpy(wavePtr->soundName, wavePtr->newSoundName);
969      }
970      if (strcmp(wavePtr->soundName, wavePtr->newSoundName) != 0) {
971	Sound *t = Snack_GetSound(interp, wavePtr->soundName);
972	ckfree(wavePtr->soundName);
973	wavePtr->soundName = ckalloc(strlen(wavePtr->newSoundName)+1);
974	strcpy(wavePtr->soundName, wavePtr->newSoundName);
975	wavePtr->width = 0;
976	wavePtr->ssmp    = 0;
977	wavePtr->esmp    = -1;
978	Snack_RemoveCallback(t, wavePtr->id);
979	wavePtr->id = 0;
980      }
981
982      if (!wavePtr->id)
983	wavePtr->id = Snack_AddCallback(s, UpdateWave, (int *)wavePtr);
984
985      wavePtr->blocks = s->blocks;
986      wavePtr->bufPos = s->length;
987      wavePtr->samprate = s->samprate;
988      wavePtr->encoding = s->encoding;
989      wavePtr->nchannels = s->nchannels;
990      wavePtr->storeType = s->storeType;
991    }
992    doCompute = 1;
993  }
994  wavePtr->esmp = wavePtr->endSmp;
995
996  if (wavePtr->endSmp < 0)
997    wavePtr->esmp = wavePtr->bufPos - 1;
998
999  if (wavePtr->endSmp > wavePtr->bufPos - 1)
1000    wavePtr->esmp = wavePtr->bufPos - 1;
1001
1002  if (wavePtr->startSmp > wavePtr->endSmp && wavePtr->endSmp >= 0)
1003    wavePtr->startSmp = wavePtr->endSmp;
1004
1005  if (wavePtr->startSmp < 0)
1006    wavePtr->startSmp = 0;
1007
1008  wavePtr->ssmp = wavePtr->startSmp;
1009
1010  if (wavePtr->ssmp > wavePtr->esmp)
1011    wavePtr->ssmp = wavePtr->esmp;
1012
1013  if (OptSpecified(OPTION_START)) {
1014    doCompute = 1;
1015  }
1016
1017  if (OptSpecified(OPTION_END)) {
1018    doCompute = 1;
1019  }
1020
1021  if (OptSpecified(OPTION_LIMIT)) {
1022    doCompute = 1;
1023  }
1024
1025  if (OptSpecified(OPTION_SUBSAMPLE)) {
1026    doCompute = 1;
1027  }
1028
1029  oldMode = wavePtr->mode;
1030  if (OptSpecified(OPTION_PIXPSEC) && OptSpecified(OPTION_WIDTH)) {
1031    wavePtr->mode = CONF_WIDTH_PPS;
1032    doCompute = 1;
1033  }
1034  else if (OptSpecified(OPTION_PIXPSEC)) {
1035    wavePtr->mode = CONF_PPS;
1036    doCompute = 1;
1037  }
1038  else if (OptSpecified(OPTION_WIDTH)) {
1039    wavePtr->mode = CONF_WIDTH;
1040  }
1041
1042  if (oldMode != wavePtr->mode) {
1043    doCompute = 1;
1044  }
1045
1046  if (wavePtr->width != wavePtr->widthSet) {
1047    wavePtr->width = wavePtr->widthSet;
1048    doCompute = 1;
1049  }
1050
1051  if (wavePtr->mode == CONF_WIDTH_PPS) {
1052    if (OptSpecified(OPTION_END) && !OptSpecified(OPTION_START)) {
1053      wavePtr->ssmp = (int) (wavePtr->esmp - wavePtr->width *
1054			     wavePtr->samprate / wavePtr->pixpsec);
1055    } else {
1056      wavePtr->esmp = (int) (wavePtr->ssmp + wavePtr->width *
1057			     wavePtr->samprate / wavePtr->pixpsec);
1058    }
1059  }
1060  else if (wavePtr->mode == CONF_PPS) {
1061    wavePtr->width = (int)((wavePtr->esmp - wavePtr->ssmp) *
1062			   wavePtr->pixpsec / wavePtr->samprate/* + 0.5*/);
1063  }
1064  else if (wavePtr->mode == CONF_WIDTH) {
1065    if (wavePtr->esmp != wavePtr->ssmp) {
1066      wavePtr->pixpsec = (double) wavePtr->width * wavePtr->samprate /
1067	(wavePtr->esmp - wavePtr->ssmp);
1068    }
1069  }
1070
1071  if (OptSpecified(OPTION_PRECOMPWAVE)) {
1072    wavePtr->preCompInvalid = 0;
1073    doCompute = 1;
1074  }
1075
1076  if (OptSpecified(OPTION_CHANNEL)) {
1077    if (GetChannel(interp, wavePtr->channelStr, wavePtr->nchannels,
1078		   &wavePtr->channelSet) != TCL_OK) {
1079      return TCL_ERROR;
1080    }
1081    doCompute = 1;
1082  }
1083  wavePtr->channel = wavePtr->channelSet;
1084  if (wavePtr->nchannels == 1) {
1085    wavePtr->channel = 0;
1086  }
1087
1088  if (OptSpecified(OPTION_PROGRESS)) {
1089    if (wavePtr->progressCmd != NULL) {
1090      wavePtr->cmdPtr = Tcl_NewStringObj(wavePtr->progressCmd, -1);
1091      Tcl_IncrRefCount(wavePtr->cmdPtr);
1092    } else {
1093      if (wavePtr->cmdPtr != NULL) {
1094	Tcl_DecrRefCount(wavePtr->cmdPtr);
1095	wavePtr->cmdPtr = NULL;
1096      }
1097    }
1098  }
1099
1100  if (wavePtr->subSampleInt == 0) {
1101    if (wavePtr->esmp - wavePtr->ssmp > NSAMPLES) {
1102      wavePtr->subSample = (wavePtr->esmp - wavePtr->ssmp) / NSAMPLES;
1103    } else {
1104      wavePtr->subSample = 1;
1105    }
1106  } else {
1107    wavePtr->subSample = wavePtr->subSampleInt;
1108  }
1109
1110  if (wavePtr->trimstart == 1 && wavePtr->width > 0) {
1111    int len = wavePtr->esmp - wavePtr->ssmp;
1112    double fraq = (double) len / wavePtr->width;
1113
1114    if (fraq > 0.0) {
1115      wavePtr->ssmp = (int) (fraq * floor(wavePtr->ssmp/fraq));
1116      wavePtr->esmp = wavePtr->ssmp + len;
1117    }
1118    if (wavePtr->esmp > wavePtr->bufPos - 1)
1119      wavePtr->esmp = wavePtr->bufPos - 1;
1120  }
1121
1122
1123  if (wavePtr->fg == NULL) {
1124    newGC = None;
1125  } else {
1126    gcValues.foreground = wavePtr->fg->pixel;
1127    gcValues.line_width = 1;
1128    mask = GCForeground|GCLineWidth;
1129    if (wavePtr->fillStipple != None) {
1130      gcValues.stipple = wavePtr->fillStipple;
1131      gcValues.fill_style = FillStippled;
1132      mask |= GCStipple|GCFillStyle;
1133    }
1134    newGC = Tk_GetGC(tkwin, mask, &gcValues);
1135    gcValues.line_width = 0;
1136  }
1137  if (wavePtr->gc != None) {
1138    Tk_FreeGC(Tk_Display(tkwin), wavePtr->gc);
1139  }
1140  wavePtr->gc = newGC;
1141
1142  ComputeWaveBbox(canvas, wavePtr);
1143
1144  if (doCompute) {
1145    if (ComputeWaveCoords(itemPtr) != TCL_OK) {
1146      return TCL_ERROR;
1147    }
1148  }
1149
1150  for (i = 0; configSpecs[i].type != TK_CONFIG_END; i++) {
1151    configSpecs[i].specFlags &= ~TK_CONFIG_OPTION_SPECIFIED;
1152  }
1153
1154  if (wavePtr->debug > 1)
1155    Snack_WriteLogInt("  Exit ConfigureWave", wavePtr->width);
1156
1157  return TCL_OK;
1158}
1159
1160static void
1161DeleteWave(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display)
1162{
1163  WaveItem *wavePtr = (WaveItem *) itemPtr;
1164
1165  if ((wavePtr->id) &&
1166      (Snack_GetSound(wavePtr->interp, wavePtr->soundName) != NULL)) {
1167    Snack_RemoveCallback(wavePtr->sound, wavePtr->id);
1168  }
1169
1170  if (wavePtr->soundName != NULL) ckfree(wavePtr->soundName);
1171
1172  if (wavePtr->x0 != NULL) ckfree((char *) wavePtr->x0);
1173  if (wavePtr->y0 != NULL) ckfree((char *) wavePtr->y0);
1174  if (wavePtr->x1 != NULL) ckfree((char *) wavePtr->x1);
1175  if (wavePtr->y1 != NULL) ckfree((char *) wavePtr->y1);
1176
1177  if (wavePtr->fg != NULL) Tk_FreeColor(wavePtr->fg);
1178
1179  if (wavePtr->fillStipple != None) Tk_FreeBitmap(display, wavePtr->fillStipple);
1180
1181  if (wavePtr->gc != None) Tk_FreeGC(display, wavePtr->gc);
1182
1183  if (wavePtr->preWI != NULL) ckfree((char *)wavePtr->preWI);
1184
1185  if (wavePtr->preSound != NULL) Snack_DeleteSound(wavePtr->preSound);
1186
1187  if (wavePtr->sound != NULL) {
1188    if (wavePtr->sound->storeType == SOUND_IN_FILE) {
1189      wavePtr->sound->itemRefCnt--;
1190    }
1191  }
1192
1193  if (wavePtr->cmdPtr != NULL) Tcl_DecrRefCount(wavePtr->cmdPtr);
1194}
1195
1196static void
1197ComputeWaveBbox(Tk_Canvas canvas, WaveItem *wavePtr)
1198{
1199  int width = wavePtr->width;
1200  int height = wavePtr->height;
1201  int x = (int) (wavePtr->x + ((wavePtr->x >= 0) ? 0.5 : - 0.5));
1202  int y = (int) (wavePtr->y + ((wavePtr->y >= 0) ? 0.5 : - 0.5));
1203
1204  switch (wavePtr->anchor) {
1205  case TK_ANCHOR_N:
1206    x -= width/2;
1207    break;
1208  case TK_ANCHOR_NE:
1209    x -= width;
1210    break;
1211  case TK_ANCHOR_E:
1212    x -= width;
1213    y -= height/2;
1214    break;
1215  case TK_ANCHOR_SE:
1216    x -= width;
1217    y -= height;
1218    break;
1219  case TK_ANCHOR_S:
1220    x -= width/2;
1221    y -= height;
1222    break;
1223  case TK_ANCHOR_SW:
1224    y -= height;
1225    break;
1226  case TK_ANCHOR_W:
1227    y -= height/2;
1228    break;
1229  case TK_ANCHOR_NW:
1230    break;
1231  case TK_ANCHOR_CENTER:
1232    x -= width/2;
1233    y -= height/2;
1234    break;
1235  }
1236
1237  wavePtr->header.x1 = x;
1238  wavePtr->header.y1 = y;
1239  wavePtr->header.x2 = x + width;
1240  wavePtr->header.y2 = y + height;
1241}
1242
1243static void
1244DisplayWave(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display,
1245	    Drawable drawable, int x, int y, int width, int height)
1246{
1247  WaveItem *wavePtr = (WaveItem *) itemPtr;
1248  int i;
1249  int xo = wavePtr->header.x1;
1250  int yo = wavePtr->header.y1;
1251  int ym = wavePtr->height / 2;
1252  int dx = max(x - xo, 0);
1253  float scale = 1000000.0f;
1254  XPoint fpts[5];
1255
1256  if (wavePtr->debug > 1) Snack_WriteLogInt("  Enter DisplayWave", width);
1257
1258  if (wavePtr->height == 0) return;
1259
1260  if (wavePtr->gc == None) return;
1261
1262  if (wavePtr->fillStipple != None)
1263    Tk_CanvasSetStippleOrigin(canvas, wavePtr->gc);
1264
1265  if (wavePtr->height > 2) {
1266    scale = 2 * ((wavePtr->maxv > -wavePtr->minv)
1267		 ? wavePtr->maxv :
1268		 -wavePtr->minv) / (float)(wavePtr->height - 2);
1269  }
1270  if (scale < 0.00001f) {
1271    scale = 0.00001f;
1272  }
1273
1274  if (dx + width > wavePtr->width) {
1275    width = wavePtr->width - dx;
1276  }
1277  if (dx > 0) {
1278    dx--;
1279    if (width < wavePtr->width - dx) width++;
1280    if (width < wavePtr->width - dx) width++;
1281  }
1282  for (i = dx; i < dx + width; i++) {
1283    Tk_CanvasDrawableCoords(canvas, xo + wavePtr->x0[i],
1284			    yo + ym - wavePtr->y0[i] / scale,
1285			    &fpts[0].x, &fpts[0].y);
1286    Tk_CanvasDrawableCoords(canvas, xo + wavePtr->x1[i],
1287			    yo + ym - wavePtr->y1[i] / scale,
1288			    &fpts[1].x, &fpts[1].y);
1289    Tk_CanvasDrawableCoords(canvas, xo + wavePtr->x1[i]+1,
1290			    yo + ym - wavePtr->y1[i] / scale,
1291			    &fpts[2].x, &fpts[2].y);
1292    XDrawLines(display, drawable, wavePtr->gc, fpts, 3, CoordModeOrigin);
1293  }
1294
1295  if (wavePtr->zeroLevel) {
1296    Tk_CanvasDrawableCoords(canvas, (double) xo,
1297			    (double) (yo + wavePtr->height / 2),
1298			    &fpts[0].x, &fpts[0].y);
1299    Tk_CanvasDrawableCoords(canvas, (double) (xo + wavePtr->width - 1),
1300			    (double) (yo + wavePtr->height / 2),
1301			    &fpts[1].x, &fpts[1].y);
1302    XDrawLines(display, drawable, wavePtr->gc, fpts, 2, CoordModeOrigin);
1303  }
1304
1305  if (wavePtr->frame) {
1306    Tk_CanvasDrawableCoords(canvas, (double) xo, (double) yo,
1307			    &fpts[0].x, &fpts[0].y);
1308    Tk_CanvasDrawableCoords(canvas, (double) (xo + wavePtr->width - 1),
1309			    (double) yo,
1310			    &fpts[1].x, &fpts[1].y);
1311    Tk_CanvasDrawableCoords(canvas, (double) (xo + wavePtr->width - 1),
1312			    (double) (yo + wavePtr->height - 1),
1313			    &fpts[2].x, &fpts[2].y);
1314    Tk_CanvasDrawableCoords(canvas, (double) xo,
1315			    (double) (yo + wavePtr->height - 1),
1316			    &fpts[3].x, &fpts[3].y);
1317    Tk_CanvasDrawableCoords(canvas, (double) xo, (double) yo,
1318			    &fpts[4].x, &fpts[4].y);
1319    XDrawLines(display, drawable, wavePtr->gc, fpts, 5, CoordModeOrigin);
1320  }
1321
1322  if (wavePtr->debug > 1) Snack_WriteLog("  Exit DisplayWave\n");
1323}
1324
1325static double
1326WaveToPoint(Tk_Canvas canvas, Tk_Item *itemPtr, double *coords)
1327{
1328  WaveItem *wavePtr = (WaveItem *) itemPtr;
1329  double dx = 0.0, dy = 0.0;
1330  double x1 = wavePtr->header.x1;
1331  double y1 = wavePtr->header.y1;
1332  double x2 = wavePtr->header.x2;
1333  double y2 = wavePtr->header.y2;
1334
1335  if (coords[0] < x1)
1336    dx = x1 - coords[0];
1337  else if (coords[0] > x2)
1338    dx = coords[0] - x2;
1339  else
1340    dx = 0;
1341
1342  if (coords[1] < y1)
1343    dy = y1 - coords[1];
1344  else if (coords[1] > y2)
1345    dy = coords[1] - y2;
1346  else
1347    dy = 0;
1348
1349  return hypot(dx, dy);
1350}
1351
1352static int
1353WaveToArea(Tk_Canvas canvas, Tk_Item *itemPtr, double *rectPtr)
1354{
1355  WaveItem *wavePtr = (WaveItem *) itemPtr;
1356
1357  if ((rectPtr[2] <= wavePtr->header.x1) ||
1358      (rectPtr[0] >= wavePtr->header.x2) ||
1359      (rectPtr[3] <= wavePtr->header.y1) ||
1360      (rectPtr[1] >= wavePtr->header.y2))
1361    return -1;
1362
1363  if ((rectPtr[0] <= wavePtr->header.x1) &&
1364      (rectPtr[1] <= wavePtr->header.y1) &&
1365      (rectPtr[2] >= wavePtr->header.x2) &&
1366      (rectPtr[3] >= wavePtr->header.y2))
1367    return 1;
1368
1369  return 0;
1370}
1371
1372static void
1373ScaleWave(Tk_Canvas canvas, Tk_Item *itemPtr, double ox, double oy,
1374	  double sx, double sy)
1375{
1376  WaveItem *wavePtr = (WaveItem *) itemPtr;
1377  double *x0 = wavePtr->x0;
1378  double *y0 = wavePtr->y0;
1379  double *x1 = wavePtr->x1;
1380  double *y1 = wavePtr->y1;
1381  int i;
1382
1383  for (i = 0; i < wavePtr->width; i++) {
1384    x0[i] = ox + sx * (x0[i] - ox);
1385    y0[i] = oy + sy * (y0[i] - oy);
1386    x1[i] = ox + sx * (x1[i] - ox);
1387    y1[i] = oy + sy * (y1[i] - oy);
1388  }
1389  wavePtr->width  = (int) (sx * wavePtr->width) + 1;
1390  wavePtr->height = (int) (sy * wavePtr->height);
1391  if (wavePtr->bufPos > 0)
1392    wavePtr->pixpsec = (double) wavePtr->width * wavePtr->samprate /
1393      wavePtr->bufPos;
1394
1395  ComputeWaveBbox(canvas, wavePtr);
1396}
1397
1398static void
1399TranslateWave(Tk_Canvas canvas, Tk_Item *itemPtr, double dx, double dy)
1400{
1401  WaveItem *wavePtr = (WaveItem *) itemPtr;
1402
1403  wavePtr->x += dx;
1404  wavePtr->y += dy;
1405  ComputeWaveBbox(canvas, wavePtr);
1406}
1407
1408static int
1409WaveToPS(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr, int prepass)
1410{
1411  WaveItem *wavePtr = (WaveItem *) itemPtr;
1412  double  *x0 = wavePtr->x0;
1413  double  *y0 = wavePtr->y0;
1414  double  *x1 = wavePtr->x1;
1415  double  *y1 = wavePtr->y1;
1416  int i;
1417  char buffer[100];
1418  int xo = wavePtr->header.x1;
1419  int yo = wavePtr->header.y1;
1420  float scale = 1000000.0f;
1421
1422  if (wavePtr->fg == NULL) {
1423    return TCL_OK;
1424  }
1425  if (wavePtr->height > 2) {
1426    scale = 2 * ((wavePtr->maxv > -wavePtr->minv)
1427		 ? wavePtr->maxv :
1428		 -wavePtr->minv) / (float)(wavePtr->height - 2);
1429  }
1430  if (scale < 0.00001f) {
1431    scale = 0.00001f;
1432  }
1433
1434  Tcl_AppendResult(interp, "%% WAVE BEGIN\n", (char *) NULL);
1435
1436  for (i = 0; i < wavePtr->width; i++) {
1437    sprintf(buffer,
1438	    "%.1f %.1f moveto\n%.1f %.1f lineto\n",
1439	    x0[i] + xo, Tk_CanvasPsY(canvas, (double)
1440				     (-y0[i]/scale + yo+ wavePtr->height / 2)),
1441	    x1[i] + xo, Tk_CanvasPsY(canvas, (double)
1442				     (-y1[i]/scale + yo+ wavePtr->height / 2)));
1443    Tcl_AppendResult(interp, buffer, (char *) NULL);
1444    if ((double)(wavePtr->esmp - wavePtr->ssmp)/wavePtr->width < 1.0) {
1445      sprintf(buffer, "%.1f %.1f lineto\n",
1446	      x1[i] + xo + 1, Tk_CanvasPsY(canvas, (double)
1447		   (-y1[i]/scale + yo+ wavePtr->height / 2)));
1448      Tcl_AppendResult(interp, buffer, (char *) NULL);
1449    }
1450  }
1451
1452  if (wavePtr->zeroLevel) {
1453    sprintf(buffer, "%.1f %.1f moveto\n", (double) xo,
1454	    Tk_CanvasPsY(canvas, (double) (yo + wavePtr->height / 2)));
1455    Tcl_AppendResult(interp, buffer, (char *) NULL);
1456
1457    sprintf(buffer, "%.1f %.1f lineto\n", (double) xo + wavePtr->width - 1,
1458	    Tk_CanvasPsY(canvas, (double) (yo + wavePtr->height / 2)));
1459    Tcl_AppendResult(interp, buffer, (char *) NULL);
1460  }
1461
1462  if (wavePtr->frame) {
1463    sprintf(buffer, "%.1f %.1f moveto\n", (double) xo, Tk_CanvasPsY(canvas, (double) yo));
1464    Tcl_AppendResult(interp, buffer, (char *) NULL);
1465
1466    sprintf(buffer, "%.1f %.1f lineto\n", (double) xo + wavePtr->width - 1,
1467	    Tk_CanvasPsY(canvas, (double) yo));
1468    Tcl_AppendResult(interp, buffer, (char *) NULL);
1469
1470    sprintf(buffer, "%.1f %.1f lineto\n", (double) xo + wavePtr->width - 1,
1471	    Tk_CanvasPsY(canvas, (double) (yo + wavePtr->height - 1)));
1472    Tcl_AppendResult(interp, buffer, (char *) NULL);
1473
1474    sprintf(buffer, "%.1f %.1f lineto\n", (double) xo,
1475	    Tk_CanvasPsY(canvas, (double) (yo + wavePtr->height - 1)));
1476    Tcl_AppendResult(interp, buffer, (char *) NULL);
1477
1478    sprintf(buffer, "%.1f %.1f lineto\n", (double) xo,
1479	    Tk_CanvasPsY(canvas, (double) yo));
1480    Tcl_AppendResult(interp, buffer, (char *) NULL);
1481  }
1482
1483  Tcl_AppendResult(interp, "1 setlinewidth\n", (char *) NULL);
1484  Tcl_AppendResult(interp, "0 setlinecap\n0 setlinejoin\n", (char *) NULL);
1485  if (Tk_CanvasPsColor(interp, canvas, wavePtr->fg) != TCL_OK) {
1486    return TCL_ERROR;
1487  };
1488  if (wavePtr->fillStipple != None) {
1489    Tcl_AppendResult(interp, "StrokeClip ", (char *) NULL);
1490    if (Tk_CanvasPsStipple(interp, canvas, wavePtr->fillStipple) != TCL_OK) {
1491      return TCL_ERROR;
1492    }
1493  } else {
1494    Tcl_AppendResult(interp, "stroke\n", (char *) NULL);
1495  }
1496
1497  Tcl_AppendResult(interp, "%% WAVE END\n", (char *) NULL);
1498
1499  return TCL_OK;
1500}
1501