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#define USE_OLD_CANVAS /* To keep Tk8.3 happy */
27#include "tk.h"
28#include "jkCanvItems.h"
29#include <string.h>
30#include <math.h>
31#ifdef MAC
32#include <Xlib.h>
33#include <Xutil.h>
34#else
35#include <X11/Xlib.h>
36#include <X11/Xutil.h>
37#endif
38
39#ifndef Solaris
40#  ifndef TkPutImage
41EXTERN void TkPutImage _ANSI_ARGS_((unsigned long *colors,
42				    int ncolors, Display* display, Drawable d,
43				    GC gc, XImage* image, int src_x, int src_y,
44				    int dest_x, int dest_y, unsigned int width,
45				    unsigned int height));
46#  endif
47#endif
48
49#define SNACK_DEFAULT_SPEGWINTYPE      SNACK_WIN_HAMMING
50#define SNACK_DEFAULT_SPEGWINTYPE_NAME "hamming"
51
52/*
53 * Spectrogram item structure
54 */
55
56typedef struct SpectrogramItem  {
57
58  Tk_Item header;
59  Tk_Canvas canvas;
60  double x, y;
61  Tk_Anchor anchor;
62  char *newSoundName;
63  char *soundName;
64  Sound *sound;
65  SnackItemInfo si;
66  int height;
67  int width;
68  int oldheight;
69  int oldwidth;
70  int startSmp;
71  int endSmp;
72  int ssmp;
73  int esmp;
74  int id;
75  int mode;
76  GC copyGC;
77  double bright;
78  double contrast;
79  char *channelstr;
80  double topFrequency;
81  int infft;
82  char *progressCmd;
83  char *windowTypeStr;
84  Tcl_Interp *interp;
85  double preemph;
86
87} SpectrogramItem;
88
89float xfft[NMAX];
90
91static int ParseColorMap(ClientData clientData, Tcl_Interp *interp,
92			 Tk_Window tkwin, CONST84 char *value, char *recordPtr,
93			 int offset);
94
95static char *PrintColorMap(ClientData clientData, Tk_Window tkwin,
96			   char *recordPtr, int offset,
97			   Tcl_FreeProc **freeProcPtr);
98
99Tk_CustomOption spegTagsOption = { (Tk_OptionParseProc *) NULL,
100				   (Tk_OptionPrintProc *) NULL,
101				   (ClientData) NULL };
102
103static Tk_CustomOption colorMapOption = { ParseColorMap,
104					  PrintColorMap,
105					  (ClientData) NULL};
106
107typedef enum {
108  OPTION_ANCHOR,
109  OPTION_TAGS,
110  OPTION_SOUND,
111  OPTION_HEIGHT,
112  OPTION_WIDTH,
113  OPTION_FFTLEN,
114  OPTION_WINLEN,
115  OPTION_PREEMP,
116  OPTION_PIXPSEC,
117  OPTION_START,
118  OPTION_END,
119  OPTION_BRIGHTNESS,
120  OPTION_CONTRAST,
121  OPTION_TOPFREQUENCY,
122  OPTION_GRIDTSPACING,
123  OPTION_GRIDFSPACING,
124  OPTION_CHANNEL,
125  OPTION_COLORMAP,
126  OPTION_PROGRESS,
127  OPTION_GRIDCOLOR,
128  OPTION_WINTYPE
129} ConfigSpec;
130
131static Tk_ConfigSpec configSpecs[] = {
132
133  {TK_CONFIG_ANCHOR, "-anchor", (char *) NULL, (char *) NULL,
134   "nw", Tk_Offset(SpectrogramItem, anchor), TK_CONFIG_DONT_SET_DEFAULT},
135
136  {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
137   (char *) NULL, 0, TK_CONFIG_NULL_OK, &spegTagsOption},
138
139  {TK_CONFIG_STRING, "-sound", (char *) NULL, (char *) NULL,
140   "", Tk_Offset(SpectrogramItem, newSoundName), TK_CONFIG_NULL_OK},
141
142  {TK_CONFIG_INT, "-height", (char *) NULL, (char *) NULL,
143   "128", Tk_Offset(SpectrogramItem, height), 0},
144
145  {TK_CONFIG_INT, "-width", (char *) NULL, (char *) NULL,
146   "378", Tk_Offset(SpectrogramItem, width), 0},
147
148  {TK_CONFIG_INT, "-fftlength", (char *) NULL, (char *) NULL,
149   "256", Tk_Offset(SpectrogramItem, si.fftlen), 0},
150
151  {TK_CONFIG_INT, "-winlength", (char *) NULL, (char *) NULL,
152   "128", Tk_Offset(SpectrogramItem, si.winlen), 0},
153
154  {TK_CONFIG_DOUBLE, "-preemphasisfactor", (char *) NULL, (char *) NULL,
155   "0.97", Tk_Offset(SpectrogramItem, preemph), 0},
156
157  {TK_CONFIG_DOUBLE, "-pixelspersecond", "pps", (char *) NULL,
158   "250.0", Tk_Offset(SpectrogramItem, si.pixpsec), 0},
159
160  {TK_CONFIG_INT, "-start", (char *) NULL, (char *) NULL,
161   "0", Tk_Offset(SpectrogramItem, startSmp), 0},
162
163  {TK_CONFIG_INT, "-end", (char *) NULL, (char *) NULL,
164   "-1", Tk_Offset(SpectrogramItem, endSmp), 0},
165
166  {TK_CONFIG_DOUBLE, "-brightness", (char *) NULL, (char *) NULL,
167   "0.0", Tk_Offset(SpectrogramItem, bright), 0},
168
169  {TK_CONFIG_DOUBLE, "-contrast", (char *) NULL, (char *) NULL,
170   "0.0", Tk_Offset(SpectrogramItem, contrast), 0},
171
172  {TK_CONFIG_DOUBLE, "-topfrequency", (char *) NULL, (char *) NULL,
173   "0.0", Tk_Offset(SpectrogramItem, topFrequency), 0},
174
175  {TK_CONFIG_DOUBLE, "-gridtspacing", (char *) NULL, (char *) NULL,
176   "0.0", Tk_Offset(SpectrogramItem, si.gridTspacing), 0},
177
178  {TK_CONFIG_INT, "-gridfspacing", (char *) NULL, (char *) NULL,
179   "0", Tk_Offset(SpectrogramItem, si.gridFspacing), 0},
180
181  {TK_CONFIG_STRING, "-channel", (char *) NULL, (char *) NULL,
182   "-1", Tk_Offset(SpectrogramItem, channelstr), TK_CONFIG_NULL_OK},
183
184  {TK_CONFIG_CUSTOM, "-colormap", (char *) NULL, (char *) NULL,
185   "", Tk_Offset(SpectrogramItem, si.xcolor), TK_CONFIG_NULL_OK,
186   &colorMapOption},
187
188  {TK_CONFIG_STRING, "-progress", (char *) NULL, (char *) NULL,
189   "", Tk_Offset(SpectrogramItem, progressCmd), TK_CONFIG_NULL_OK},
190
191  {TK_CONFIG_COLOR, "-gridcolor", (char *) NULL, (char *) NULL,
192   "red", Tk_Offset(SpectrogramItem, si.gridcolor), TK_CONFIG_NULL_OK},
193
194  {TK_CONFIG_STRING, "-windowtype", (char *) NULL, (char *) NULL,
195   SNACK_DEFAULT_SPEGWINTYPE_NAME, Tk_Offset(SpectrogramItem, windowTypeStr),
196   0},
197
198  {TK_CONFIG_INT, "-debug", (char *) NULL, (char *) NULL,
199   "0", Tk_Offset(SpectrogramItem, si.debug), 0},
200
201  {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
202   (char *) NULL, 0, 0}
203
204};
205
206/*
207 * Protos
208 */
209
210static void   ComputeSpectrogramBbox(Tk_Canvas canvas,
211				     SpectrogramItem *spegPtr);
212
213static int    ConfigureSpectrogram(Tcl_Interp *interp, Tk_Canvas canvas,
214				   Tk_Item *itemPtr, int argc,
215				   char **argv, int flags);
216
217static int    CreateSpectrogram(Tcl_Interp *interp, Tk_Canvas canvas,
218				struct Tk_Item *itemPtr,
219				int argc, char **argv);
220
221static void   DeleteSpectrogram(Tk_Canvas canvas, Tk_Item *itemPtr,
222				Display *display);
223
224static void   DisplaySpectrogram(Tk_Canvas canvas, Tk_Item *itemPtr,
225				 Display *display, Drawable dst,
226				 int x, int y, int width, int height);
227
228static void   ScaleSpectrogram(Tk_Canvas canvas, Tk_Item *itemPtr,
229			       double originX, double originY,
230			       double scaleX, double scaleY);
231
232static int    SpectrogramCoords(Tcl_Interp *interp, Tk_Canvas canvas,
233				Tk_Item *itemPtr, int argc, char **argv);
234
235static int    SpectrogramToArea(Tk_Canvas canvas, Tk_Item *itemPtr,
236				double *rectPtr);
237
238static double SpectrogramToPoint(Tk_Canvas canvas, Tk_Item *itemPtr,
239				 double *coordPtr);
240
241static int    SpectrogramToPS(Tcl_Interp *interp, Tk_Canvas canvas,
242			      Tk_Item *itemPtr, int prepass);
243
244static void   TranslateSpectrogram(Tk_Canvas canvas, Tk_Item *itemPtr,
245				   double deltaX, double deltaY);
246
247static int    ComputeSpeg(SnackItemInfo *siPtr, int nfft);
248
249static void   DrawSpeg(SnackItemInfo *siPtr, Display* display,
250		       GC gc, int width, int height, int x, int w, int pos);
251
252/*
253 * Spectrogram item type
254 */
255
256Tk_ItemType snackSpectrogramType = {
257  "spectrogram",
258  sizeof(SpectrogramItem),
259  CreateSpectrogram,
260  configSpecs,
261  ConfigureSpectrogram,
262  SpectrogramCoords,
263  DeleteSpectrogram,
264  DisplaySpectrogram,
265  0,
266  SpectrogramToPoint,
267  SpectrogramToArea,
268  SpectrogramToPS,
269  ScaleSpectrogram,
270  TranslateSpectrogram,
271  (Tk_ItemIndexProc *) NULL,
272  (Tk_ItemCursorProc *) NULL,
273  (Tk_ItemSelectionProc *) NULL,
274  (Tk_ItemInsertProc *) NULL,
275  (Tk_ItemDCharsProc *) NULL,
276  (Tk_ItemType *) NULL
277};
278
279static int
280CreateSpectrogram(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr,
281		  int argc, char **argv)
282{
283  SpectrogramItem *spegPtr = (SpectrogramItem *) itemPtr;
284  Tk_Window tkwin = Tk_CanvasTkwin(canvas);
285
286  if (argc < 2) {
287    Tcl_AppendResult(interp, "wrong # args: should be \"",
288		     Tk_PathName(Tk_CanvasTkwin(canvas)), " create ",
289		     itemPtr->typePtr->name, " x y ?opts?\"", (char *) NULL);
290    return TCL_ERROR;
291  }
292
293  spegPtr->canvas = canvas;
294  spegPtr->anchor = TK_ANCHOR_NW;
295  spegPtr->newSoundName = NULL;
296  spegPtr->soundName = NULL;
297  spegPtr->sound = NULL;
298  spegPtr->height = 128;
299  spegPtr->width = 378;
300  spegPtr->startSmp = 0;
301  spegPtr->endSmp = -1;
302  spegPtr->ssmp = 0;
303  spegPtr->esmp = -1;
304  spegPtr->id = 0;
305  spegPtr->mode = CONF_WIDTH;
306  spegPtr->bright = 0.0;
307  spegPtr->contrast = 0.0;
308  spegPtr->si.fftlen = 256;
309  spegPtr->si.winlen = 128;
310  spegPtr->si.spacing = 64.0;
311  spegPtr->preemph = 0.97;
312  spegPtr->si.frame[0] = (short *) ckalloc(2*FRAMESIZE);
313  spegPtr->si.nfrms = 1;
314  spegPtr->si.frlen = FRAMESIZE;
315  spegPtr->si.RestartPos = 0;
316  spegPtr->si.fftmax = -10000;
317  spegPtr->si.fftmin = 10000;
318  spegPtr->si.debug = 1;
319  spegPtr->si.hamwin = (float *) ckalloc(NMAX * sizeof(float));
320  spegPtr->si.BufPos = 0;
321  spegPtr->si.abmax = 0.0f;
322  spegPtr->si.bright = 60.0;
323  spegPtr->si.contrast = 2.3;
324  spegPtr->si.pixpsec = 250.0;
325  spegPtr->si.gridTspacing = 0.0;
326  spegPtr->si.gridFspacing = 0;
327  spegPtr->channelstr = NULL;
328  spegPtr->si.channel = -1;
329  spegPtr->si.channelSet = -1;
330  spegPtr->si.nchannels = 1;
331  spegPtr->si.ncolors = 0;
332  spegPtr->si.xcolor = NULL;
333  spegPtr->si.pixmap = None;
334  spegPtr->si.gridcolor = None;
335  spegPtr->si.depth = Tk_Depth(tkwin);
336  spegPtr->si.visual = Tk_Visual(tkwin);
337  spegPtr->si.pixelmap = NULL;
338  spegPtr->si.display = Tk_Display(tkwin);
339  spegPtr->topFrequency = 0.0;
340  spegPtr->si.xUnderSamp = 1.0;
341  spegPtr->si.xTot = 0;
342  spegPtr->infft = 0;
343  spegPtr->si.doneSpeg = 0;
344  spegPtr->si.validStart = 0;
345  spegPtr->progressCmd = NULL;
346  spegPtr->si.cmdPtr = NULL;
347  spegPtr->si.windowType = SNACK_DEFAULT_SPEGWINTYPE;
348  spegPtr->si.windowTypeSet = SNACK_DEFAULT_SPEGWINTYPE;
349  spegPtr->windowTypeStr = NULL;
350  spegPtr->interp = interp;
351
352  /*  spegPtr->si.computing = 0;*/
353
354  if (spegPtr->si.frame[0] == NULL) {
355    Tcl_AppendResult(interp, "Couldn't allocate fft buffer!", NULL);
356    return TCL_ERROR;
357  }
358
359  if (spegPtr->si.hamwin == NULL) {
360    Tcl_AppendResult(interp, "Couldn't allocate analysis window buffer!",
361		     NULL);
362    ckfree((char *) spegPtr->si.frame[0]);
363    return TCL_ERROR;
364  }
365
366  if ((Tk_CanvasGetCoord(interp, canvas, argv[0], &spegPtr->x) != TCL_OK)
367      || (Tk_CanvasGetCoord(interp, canvas, argv[1], &spegPtr->y) != TCL_OK))
368    return TCL_ERROR;
369
370  if (ConfigureSpectrogram(interp, canvas, itemPtr, argc-2, argv+2, 0) != TCL_OK) {
371    DeleteSpectrogram(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
372    return TCL_ERROR;
373  }
374  return TCL_OK;
375}
376
377static int
378SpectrogramCoords(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr,
379		  int argc, char **argv)
380{
381  SpectrogramItem *spegPtr = (SpectrogramItem *) itemPtr;
382  char xc[TCL_DOUBLE_SPACE], yc[TCL_DOUBLE_SPACE];
383
384  if (argc == 0) {
385    Tcl_PrintDouble(interp, spegPtr->x, xc);
386    Tcl_PrintDouble(interp, spegPtr->y, yc);
387    Tcl_AppendResult(interp, xc, " ", yc, (char *) NULL);
388  } else if (argc == 2) {
389    if ((Tk_CanvasGetCoord(interp, canvas, argv[0], &spegPtr->x) != TCL_OK) ||
390	(Tk_CanvasGetCoord(interp, canvas, argv[1], &spegPtr->y) != TCL_OK)) {
391      return TCL_ERROR;
392    }
393    ComputeSpectrogramBbox(canvas, spegPtr);
394  } else {
395    char buf[80];
396
397    sprintf(buf, "wrong # coordinates: expected 0 or 2, got %d", argc);
398    Tcl_SetResult(interp, buf, TCL_VOLATILE);
399
400    return TCL_ERROR;
401  }
402  return TCL_OK;
403}
404
405static void
406UpdateSpeg(ClientData clientData, int flag)
407{
408  SpectrogramItem *spegPtr = (SpectrogramItem *) clientData;
409  Sound *s = spegPtr->sound;
410  int nfft = 0;
411
412  if (spegPtr->si.debug > 1) Snack_WriteLogInt("  Enter UpdateSpeg", flag);
413
414  if (spegPtr->canvas == NULL) return;
415
416  if (flag == SNACK_DESTROY_SOUND) {
417    spegPtr->sound = NULL;
418    if (spegPtr->id) Snack_RemoveCallback(s, spegPtr->id);
419    spegPtr->id = 0;
420    return;
421  }
422
423  Tk_CanvasEventuallyRedraw(spegPtr->canvas,
424			    spegPtr->header.x1, spegPtr->header.y1,
425			    spegPtr->header.x2, spegPtr->header.y2);
426
427  spegPtr->si.blocks = s->blocks;
428  spegPtr->si.BufPos = s->length;
429  spegPtr->si.storeType = s->storeType;
430
431  if ((flag == SNACK_MORE_SOUND) || (spegPtr->endSmp < 0)) {
432    spegPtr->esmp = spegPtr->si.BufPos - 1;
433  }
434
435  if (spegPtr->endSmp > 0)
436    spegPtr->esmp = spegPtr->endSmp;
437  /*
438  if (spegPtr->endSmp < 0)
439    spegPtr->esmp = spegPtr->si.BufPos - 1;
440    */
441  if (spegPtr->esmp > spegPtr->si.BufPos - 1)
442    spegPtr->esmp = spegPtr->si.BufPos - 1;
443
444  if (spegPtr->endSmp > spegPtr->si.BufPos - 1)
445    spegPtr->esmp = spegPtr->si.BufPos - 1;
446
447  spegPtr->ssmp = spegPtr->startSmp;
448
449  if (spegPtr->ssmp > spegPtr->esmp)
450    spegPtr->ssmp = spegPtr->esmp;
451
452  spegPtr->si.channel = spegPtr->si.channelSet;
453  if (spegPtr->si.nchannels == 1) {
454    spegPtr->si.channel = 0;
455  }
456
457  if (flag == SNACK_NEW_SOUND) {
458    spegPtr->si.samprate = s->samprate;
459    spegPtr->si.encoding = s->encoding;
460    spegPtr->si.nchannels = s->nchannels;
461    spegPtr->si.abmax = s->abmax;
462    spegPtr->si.fftmax = -10000;
463    spegPtr->si.fftmin = 10000;
464    spegPtr->si.RestartPos = spegPtr->ssmp;
465    spegPtr->si.nfft = 0;
466    spegPtr->si.xTot = 0;
467  }
468
469  if (spegPtr->topFrequency <= 0.0) {
470    spegPtr->si.topfrequency = spegPtr->si.samprate / 2.0;
471  } else if (spegPtr->topFrequency > spegPtr->si.samprate / 2.0) {
472    spegPtr->si.topfrequency = spegPtr->si.samprate / 2.0;
473  } else {
474    spegPtr->si.topfrequency = spegPtr->topFrequency;
475  }
476
477  if (flag == SNACK_NEW_SOUND && spegPtr->mode == CONF_WIDTH) {
478    spegPtr->infft = (int)((spegPtr->esmp - spegPtr->ssmp) /spegPtr->si.spacing);
479    nfft = (int)(((spegPtr->esmp - spegPtr->ssmp)
480		  - spegPtr->si.fftlen / 2) / spegPtr->si.spacing);
481  } else if (flag == SNACK_NEW_SOUND && spegPtr->mode == CONF_PPS){
482    spegPtr->infft = (int)((spegPtr->esmp - spegPtr->ssmp) / spegPtr->si.spacing);
483    nfft = (int)(((spegPtr->esmp - spegPtr->ssmp)
484		  - spegPtr->si.fftlen / 2) / spegPtr->si.spacing);
485  } else if (flag == SNACK_NEW_SOUND && spegPtr->mode == CONF_WIDTH_PPS) {
486    spegPtr->ssmp = (int) (spegPtr->esmp - spegPtr->width *
487			   spegPtr->si.samprate / spegPtr->si.pixpsec);
488    spegPtr->si.RestartPos = spegPtr->ssmp;
489    spegPtr->infft = (int)((spegPtr->esmp - spegPtr->ssmp) /
490			   spegPtr->si.spacing);
491    nfft = (int)(((spegPtr->esmp - spegPtr->ssmp)
492		  - spegPtr->si.fftlen / 2) / spegPtr->si.spacing);
493  } else {
494    spegPtr->infft = (int)(s->length / spegPtr->si.spacing);
495    nfft = (int)((s->length  - spegPtr->si.RestartPos
496		  - spegPtr->si.fftlen / 2) / spegPtr->si.spacing);
497  }
498
499  spegPtr->si.validStart = s->validStart;
500
501  if (nfft > 0) {
502    int n = ComputeSpeg(&spegPtr->si, nfft);
503    if (n < 0) return;
504    spegPtr->si.RestartPos += (int)(n * spegPtr->si.spacing);
505
506    if (spegPtr->mode == CONF_WIDTH) {
507      if (spegPtr->esmp != spegPtr->ssmp) {
508	spegPtr->si.pixpsec = (float) spegPtr->width * spegPtr->si.samprate /
509	  (spegPtr->esmp - spegPtr->ssmp);
510      }
511      spegPtr->si.xUnderSamp = (float) spegPtr->infft / spegPtr->width;
512      DrawSpeg(&spegPtr->si, spegPtr->si.display, spegPtr->copyGC,
513	       spegPtr->width, spegPtr->height, 0, spegPtr->width, 0);
514    }
515    else if (spegPtr->mode == CONF_PPS) {
516      spegPtr->width = (int)((spegPtr->esmp - spegPtr->ssmp) *
517			     spegPtr->si.pixpsec / spegPtr->si.samprate);
518      if (spegPtr->width > 32767) {
519	spegPtr->width = 32767;
520      }
521      if (spegPtr->si.pixmap != None) {
522	Tk_FreePixmap(spegPtr->si.display, spegPtr->si.pixmap);
523	spegPtr->si.pixmap = None;
524      }
525      if (spegPtr->si.pixmap == None) {
526	if (spegPtr->width > 0) {
527	  Tk_Window w = Tk_CanvasTkwin(spegPtr->canvas);
528
529	  spegPtr->oldwidth = spegPtr->width;
530	  spegPtr->oldheight = spegPtr->height;
531	  spegPtr->si.pixmap = Tk_GetPixmap(Tk_Display(w),
532					    RootWindow(Tk_Display(w), Tk_ScreenNumber(w)),
533					    spegPtr->width, spegPtr->height, Tk_Depth(w));
534	}
535      }
536      DrawSpeg(&spegPtr->si, spegPtr->si.display, spegPtr->copyGC,
537	       spegPtr->width, spegPtr->height, 0, spegPtr->width, 0);
538    }
539    else if (spegPtr->mode == CONF_WIDTH_PPS) {
540      int virtx = (int) (spegPtr->si.nfft / spegPtr->si.xUnderSamp);
541      int dx = virtx - (int)((spegPtr->si.nfft - n) / spegPtr->si.xUnderSamp);
542      spegPtr->ssmp = (int) (spegPtr->esmp - spegPtr->width *
543			     spegPtr->si.samprate / spegPtr->si.pixpsec);
544
545      XCopyArea(spegPtr->si.display, spegPtr->si.pixmap, spegPtr->si.pixmap,
546		spegPtr->copyGC, dx, 0, spegPtr->width - dx, spegPtr->height,
547		0, 0);
548      DrawSpeg(&spegPtr->si, spegPtr->si.display, spegPtr->copyGC,
549	       spegPtr->width, spegPtr->height, spegPtr->width - dx, dx,
550	       spegPtr->si.nfft - n - 1);
551    }
552  }
553
554  ComputeSpectrogramBbox(spegPtr->canvas, spegPtr);
555
556  Tk_CanvasEventuallyRedraw(spegPtr->canvas,
557			    spegPtr->header.x1, spegPtr->header.y1,
558			    spegPtr->header.x2, spegPtr->header.y2);
559
560  if (spegPtr->si.debug > 1) {
561    Snack_WriteLogInt("  Exit UpdateSpeg", spegPtr->width);
562  }
563}
564
565static int
566ConfigureSpectrogram(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr,
567		     int argc, char **argv, int flags)
568{
569  SpectrogramItem *spegPtr = (SpectrogramItem *) itemPtr;
570  Sound *s = spegPtr->sound;
571  Tk_Window tkwin = Tk_CanvasTkwin(canvas);
572  XGCValues gcValues;
573  int doCompute = 0;
574  int i, j;
575
576  if (argc == 0) return TCL_OK;
577
578  /*  if (spegPtr->si.computing) return TCL_OK;*/
579
580  if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc,
581			 (CONST84 char **)argv,
582			 (char *) spegPtr, flags) != TCL_OK) return TCL_ERROR;
583
584  if (spegPtr->si.debug > 1) Snack_WriteLog("  Enter ConfigureSpeg\n");
585
586  for (i = 0; configSpecs[i].type != TK_CONFIG_END; i++) {
587    for (j = 0; j < argc; j += 2) {
588      if (strncmp(argv[j], configSpecs[i].argvName, strlen(argv[j])) == 0) {
589	configSpecs[i].specFlags |= TK_CONFIG_OPTION_SPECIFIED;
590	break;
591      }
592    }
593  }
594
595#if defined(MAC) || defined(MAC_OSX_TCL)
596  for (i = 0; i < argc; i++) {
597    int l = strlen(argv[i]);
598    if (l && strncmp(argv[i], "-anchor", l) == 0) {
599      i++;
600      if (strcmp(argv[i], "ne") == 0) {
601	spegPtr->anchor = 1;
602      } else if (strcmp(argv[i], "nw") == 0) {
603	spegPtr->anchor = 7;
604      } else if (strcmp(argv[i], "n") == 0) {
605	spegPtr->anchor = 0;
606      } else if (strcmp(argv[i], "e") == 0) {
607	spegPtr->anchor = 2;
608      } else if (strcmp(argv[i], "se") == 0) {
609	spegPtr->anchor = 3;
610      } else if (strcmp(argv[i], "sw") == 0) {
611	spegPtr->anchor = 5;
612      } else if (strcmp(argv[i], "s") == 0) {
613	spegPtr->anchor = 4;
614      } else if (strcmp(argv[i], "w") == 0) {
615	spegPtr->anchor = 6;
616      } else if (strncmp(argv[i], "center", strlen(argv[i])) == 0) {
617	spegPtr->anchor = 8;
618      }
619      break;
620    }
621  }
622#endif
623
624  if (CheckFFTlen(interp, spegPtr->si.fftlen) != TCL_OK) return TCL_ERROR;
625
626  if (CheckWinlen(interp, spegPtr->si.winlen, spegPtr->si.fftlen) != TCL_OK)
627    return TCL_ERROR;
628
629  if (OptSpecified(OPTION_SOUND)) {
630    if (spegPtr->newSoundName == NULL) {
631      spegPtr->sound = NULL;
632      if (spegPtr->id) Snack_RemoveCallback(s, spegPtr->id);
633      spegPtr->id = 0;
634      spegPtr->si.BufPos = 0;
635      doCompute = 1;
636    } else {
637      if ((s = Snack_GetSound(interp, spegPtr->newSoundName)) == NULL) {
638	return TCL_ERROR;
639      }
640      if (s->storeType == SOUND_IN_CHANNEL) {
641	Tcl_AppendResult(interp, spegPtr->newSoundName,
642			 " can not be linked to a channel", (char *) NULL);
643	return TCL_ERROR;
644      }
645      if (s->storeType == SOUND_IN_FILE) {
646	s->itemRefCnt++;
647      }
648      spegPtr->sound = s;
649      if (spegPtr->soundName == NULL) {
650	spegPtr->soundName = ckalloc(strlen(spegPtr->newSoundName)+1);
651	strcpy(spegPtr->soundName, spegPtr->newSoundName);
652      }
653      if (strcmp(spegPtr->soundName, spegPtr->newSoundName) != 0) {
654	Sound *t = Snack_GetSound(interp, spegPtr->soundName);
655	ckfree(spegPtr->soundName);
656	spegPtr->soundName = ckalloc(strlen(spegPtr->newSoundName)+1);
657	strcpy(spegPtr->soundName, spegPtr->newSoundName);
658	spegPtr->ssmp = 0;
659	spegPtr->esmp = -1;
660	Snack_RemoveCallback(t, spegPtr->id);
661	spegPtr->id = 0;
662      }
663      if (!spegPtr->id)
664	spegPtr->id = Snack_AddCallback(s, UpdateSpeg, (int *)spegPtr);
665      spegPtr->si.blocks = s->blocks;
666      spegPtr->si.BufPos = s->length;
667      spegPtr->si.samprate = s->samprate;
668      spegPtr->si.encoding = s->encoding;
669      spegPtr->si.nchannels = s->nchannels;
670      spegPtr->si.abmax = s->abmax;
671      spegPtr->si.storeType = s->storeType;
672      spegPtr->si.sound = spegPtr->sound;
673      doCompute = 1;
674    }
675  }
676
677  spegPtr->copyGC = Tk_GetGC(tkwin, 0, &gcValues);
678
679  spegPtr->esmp = spegPtr->endSmp;
680
681  if (spegPtr->endSmp < 0)
682    spegPtr->esmp = spegPtr->si.BufPos - 1;
683
684  if (spegPtr->endSmp > spegPtr->si.BufPos - 1)
685    spegPtr->esmp = spegPtr->si.BufPos - 1;
686
687  if (spegPtr->startSmp > spegPtr->endSmp && spegPtr->endSmp >= 0)
688    spegPtr->startSmp = spegPtr->endSmp;
689
690  if (spegPtr->startSmp < 0)
691    spegPtr->startSmp = 0;
692
693  spegPtr->ssmp = spegPtr->startSmp;
694
695  if (spegPtr->ssmp > spegPtr->esmp)
696    spegPtr->ssmp = spegPtr->esmp;
697
698  spegPtr->si.preemph = (float) spegPtr->preemph;
699
700  if (OptSpecified(OPTION_START)) {
701    doCompute = 1;
702  }
703
704  if (OptSpecified(OPTION_END)) {
705    doCompute = 1;
706  }
707
708  if (OptSpecified(OPTION_WINLEN)) {
709    doCompute = 1;
710  }
711
712  if (OptSpecified(OPTION_FFTLEN)) {
713    doCompute = 1;
714  }
715
716  if (OptSpecified(OPTION_PIXPSEC) && OptSpecified(OPTION_WIDTH)) {
717    spegPtr->mode = CONF_WIDTH_PPS;
718  }
719  else if (OptSpecified(OPTION_PIXPSEC)) {
720    spegPtr->mode = CONF_PPS;
721  }
722  else if (OptSpecified(OPTION_WIDTH)) {
723    spegPtr->mode = CONF_WIDTH;
724  }
725
726  if (spegPtr->mode == CONF_WIDTH_PPS) {
727    if (OptSpecified(OPTION_END) && !OptSpecified(OPTION_START)) {
728      spegPtr->ssmp = (int) (spegPtr->esmp - spegPtr->width *
729			     spegPtr->si.samprate / spegPtr->si.pixpsec);
730    } else {
731      spegPtr->esmp = (int) (spegPtr->ssmp + spegPtr->width *
732			     spegPtr->si.samprate / spegPtr->si.pixpsec);
733      if (spegPtr->esmp > spegPtr->si.BufPos - 1) {
734	spegPtr->esmp = spegPtr->si.BufPos - 1;
735      }
736    }
737    doCompute = 1;
738  }
739  else if (spegPtr->mode == CONF_PPS) {
740    spegPtr->width = (int)((spegPtr->esmp - spegPtr->ssmp) *
741			   spegPtr->si.pixpsec / spegPtr->si.samprate);
742  }
743  else if (spegPtr->mode == CONF_WIDTH) {
744    if (spegPtr->esmp != spegPtr->ssmp) {
745      spegPtr->si.pixpsec = (float) spegPtr->width * spegPtr->si.samprate /
746	(spegPtr->esmp - spegPtr->ssmp);
747    }
748  }
749
750  if (spegPtr->width > 32767) {
751    spegPtr->width = 32767;
752    spegPtr->esmp = (int) (spegPtr->ssmp + spegPtr->width *
753			   spegPtr->si.samprate / spegPtr->si.pixpsec);
754  }
755
756  if (OptSpecified(OPTION_HEIGHT)) {
757  }
758
759  if (OptSpecified(OPTION_BRIGHTNESS)) {
760    if (spegPtr->bright > 100.0) {
761      spegPtr->bright = 100.0;
762    } else if (spegPtr->bright < -100.0) {
763      spegPtr->bright = -100.0;
764    }
765    spegPtr->si.bright = spegPtr->bright * 0.3 + 60.0;
766  }
767
768  if (OptSpecified(OPTION_CONTRAST)) {
769    if (spegPtr->contrast > 100.0) {
770      spegPtr->contrast = 100.0;
771    } else if (spegPtr->contrast < -100.0) {
772      spegPtr->contrast = -100.0;
773    }
774    if (spegPtr->contrast >= 0.0) {
775      spegPtr->si.contrast = 2.3 + spegPtr->contrast * 0.04;
776    } else {
777      spegPtr->si.contrast = 2.3 + spegPtr->contrast * 0.023;
778    }
779  }
780
781  if (spegPtr->topFrequency <= 0.0) {
782    spegPtr->si.topfrequency = spegPtr->si.samprate / 2.0;
783  } else if (spegPtr->topFrequency > spegPtr->si.samprate / 2.0) {
784    spegPtr->si.topfrequency = spegPtr->si.samprate / 2.0;
785  } else {
786    spegPtr->si.topfrequency = spegPtr->topFrequency;
787  }
788
789  if (OptSpecified(OPTION_CHANNEL)) {
790    if (GetChannel(interp, spegPtr->channelstr, spegPtr->si.nchannels,
791		   &spegPtr->si.channelSet) != TCL_OK) {
792      return TCL_ERROR;
793    }
794    doCompute = 1;
795  }
796  spegPtr->si.channel = spegPtr->si.channelSet;
797  if (spegPtr->si.nchannels == 1) {
798    spegPtr->si.channel = 0;
799  }
800
801  /*  if (OptSpecified(OPTION_PROGRESS)) {
802    spegPtr->si.cmdPtr = Tcl_NewStringObj(spegPtr->progressCmd, -1);
803    Tcl_IncrRefCount(spegPtr->si.cmdPtr);
804  }*/
805
806  if (OptSpecified(OPTION_WINTYPE)) {
807    if (GetWindowType(interp, spegPtr->windowTypeStr,
808		      &spegPtr->si.windowTypeSet)
809	!= TCL_OK) {
810      return TCL_ERROR;
811    }
812    doCompute = 1;
813  }
814  spegPtr->si.windowType = spegPtr->si.windowTypeSet;
815
816  if (doCompute) {
817    int nfft, n;
818
819    spegPtr->si.nfft = 0;
820    spegPtr->si.spacing = (float)(spegPtr->si.samprate / spegPtr->si.pixpsec);
821    nfft = (int)((spegPtr->esmp - spegPtr->ssmp) / spegPtr->si.spacing);
822    spegPtr->si.xUnderSamp = 1.0;
823    spegPtr->si.RestartPos = spegPtr->ssmp;
824    spegPtr->si.fftmax = -10000;
825    spegPtr->si.fftmin = 10000;
826    spegPtr->si.ssmp = spegPtr->ssmp;
827
828    n = ComputeSpeg(&spegPtr->si, nfft);
829
830    if (n < 0) return TCL_OK;
831    spegPtr->infft = nfft;
832  }
833
834  if (spegPtr->si.pixmap != None &&
835      (spegPtr->width != spegPtr->oldwidth ||
836       spegPtr->height != spegPtr->oldheight)) {
837    Tk_FreePixmap(spegPtr->si.display, spegPtr->si.pixmap);
838    spegPtr->si.pixmap = None;
839  }
840  if (spegPtr->si.pixmap == None) {
841    if (spegPtr->width > 0 && spegPtr->height > 0) {
842      spegPtr->oldwidth = spegPtr->width;
843      spegPtr->oldheight = spegPtr->height;
844      spegPtr->si.pixmap = Tk_GetPixmap(Tk_Display(tkwin),
845			      RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin)),
846			      spegPtr->width, spegPtr->height, Tk_Depth(tkwin));
847    }
848  }
849
850  if (spegPtr->mode == CONF_WIDTH_PPS) {
851    if (spegPtr->esmp != spegPtr->ssmp) {
852      spegPtr->si.xUnderSamp = spegPtr->infft /
853	               ((spegPtr->esmp - spegPtr->ssmp)
854		       * (float) spegPtr->si.pixpsec / spegPtr->si.samprate);
855    }
856  }
857  else if (spegPtr->mode == CONF_PPS) {
858    if (spegPtr->width > 0) {
859      spegPtr->si.xUnderSamp = (float) spegPtr->infft / spegPtr->width;
860    }
861  }
862  else if (spegPtr->mode == CONF_WIDTH) {
863    if (spegPtr->width > 0) {
864      spegPtr->si.xUnderSamp = (float) spegPtr->infft / spegPtr->width;
865    }
866  }
867
868  spegPtr->si.xTot = 0;
869  DrawSpeg(&spegPtr->si, Tk_Display(tkwin), spegPtr->copyGC,
870	   spegPtr->width, spegPtr->height, 0, spegPtr->width, 0);
871  ComputeSpectrogramBbox(canvas, spegPtr);
872
873  for (i = 0; configSpecs[i].type != TK_CONFIG_END; i++) {
874    configSpecs[i].specFlags &= ~TK_CONFIG_OPTION_SPECIFIED;
875  }
876
877  if (spegPtr->si.debug > 1) Snack_WriteLog("  Exit ConfigureSpeg\n");
878
879  return TCL_OK;
880}
881
882static void
883DeleteSpectrogram(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display)
884{
885  SpectrogramItem *spegPtr = (SpectrogramItem *) itemPtr;
886  int i;
887
888  if ((spegPtr->id) &&
889      (Snack_GetSound(spegPtr->interp, spegPtr->soundName) != NULL)) {
890    Snack_RemoveCallback(spegPtr->sound, spegPtr->id);
891  }
892
893  if (spegPtr->soundName != NULL) ckfree(spegPtr->soundName);
894
895  if (spegPtr->si.hamwin != NULL) ckfree((char *) spegPtr->si.hamwin);
896
897  for (i = 0; i < spegPtr->si.nfrms; i++) {
898    ckfree((char *)spegPtr->si.frame[i]);
899  }
900
901  for (i = 0; i < spegPtr->si.ncolors; i++) {
902    Tk_FreeColor(spegPtr->si.xcolor[i]);
903  }
904
905  if (spegPtr->si.gridcolor != NULL) Tk_FreeColor(spegPtr->si.gridcolor);
906
907  if (spegPtr->si.pixmap != None) {
908    Tk_FreePixmap(spegPtr->si.display, spegPtr->si.pixmap);
909  }
910
911  if (spegPtr->sound != NULL) {
912    if (spegPtr->sound->storeType == SOUND_IN_FILE) {
913      spegPtr->sound->itemRefCnt--;
914    }
915  }
916}
917
918static void
919ComputeSpectrogramBbox(Tk_Canvas canvas, SpectrogramItem *spegPtr)
920{
921  int width = spegPtr->width;
922  int height = spegPtr->height;
923  int x = (int) (spegPtr->x + ((spegPtr->x >= 0) ? 0.5 : - 0.5));
924  int y = (int) (spegPtr->y + ((spegPtr->y >= 0) ? 0.5 : - 0.5));
925
926  switch (spegPtr->anchor) {
927  case TK_ANCHOR_N:
928    x -= width/2;
929    break;
930  case TK_ANCHOR_NE:
931    x -= width;
932    break;
933  case TK_ANCHOR_E:
934    x -= width;
935    y -= height/2;
936    break;
937  case TK_ANCHOR_SE:
938    x -= width;
939    y -= height;
940    break;
941  case TK_ANCHOR_S:
942    x -= width/2;
943    y -= height;
944    break;
945  case TK_ANCHOR_SW:
946    y -= height;
947    break;
948  case TK_ANCHOR_W:
949    y -= height/2;
950    break;
951  case TK_ANCHOR_NW:
952    break;
953  case TK_ANCHOR_CENTER:
954    x -= width/2;
955    y -= height/2;
956    break;
957  }
958
959  spegPtr->header.x1 = x;
960  spegPtr->header.y1 = y;
961  spegPtr->header.x2 = x + width;
962  spegPtr->header.y2 = y + height;
963}
964
965static void
966DisplaySpectrogram(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display,
967		   Drawable drawable, int x, int y, int width, int height)
968{
969  SpectrogramItem *spegPtr = (SpectrogramItem *) itemPtr;
970  short drawableX, drawableY;
971  int xcoord = 0;
972
973  if (spegPtr->si.debug > 1) Snack_WriteLogInt("  Enter DisplaySpeg", width);
974
975  if (spegPtr->width == 0 || spegPtr->height == 0) return;
976  /*  if (spegPtr->si.computing) return;*/
977
978  Tk_CanvasDrawableCoords(canvas, (double) spegPtr->header.x1,
979			  (double) spegPtr->header.y1, &drawableX, &drawableY);
980
981  if (x < spegPtr->header.x1) {
982    xcoord = 0;
983  } else {
984    xcoord =  x - spegPtr->header.x1;
985  }
986  if (width > spegPtr->width) {
987    width = spegPtr->width;
988  }
989
990  XCopyArea(display, spegPtr->si.pixmap, drawable, spegPtr->copyGC, xcoord, 0,
991	    width, spegPtr->height, drawableX+xcoord, drawableY);
992
993  if (spegPtr->si.debug > 1) Snack_WriteLog("  Exit DisplaySpeg\n");
994}
995
996static double
997SpectrogramToPoint(Tk_Canvas canvas, Tk_Item *itemPtr, double *coordPtr)
998{
999  SpectrogramItem *spegPtr = (SpectrogramItem *) itemPtr;
1000  double dx = 0.0, dy = 0.0;
1001  double x1 = spegPtr->header.x1;
1002  double y1 = spegPtr->header.y1;
1003  double x2 = spegPtr->header.x2;
1004  double y2 = spegPtr->header.y2;
1005
1006  if (coordPtr[0] < x1)
1007    dx = x1 - coordPtr[0];
1008  else if (coordPtr[0] > x2)
1009    dx = coordPtr[0] - x2;
1010
1011  if (coordPtr[1] < y1)
1012    dy = y1 - coordPtr[1];
1013  else if (coordPtr[1] > y2)
1014    dy = coordPtr[1] - y2;
1015
1016  return hypot(dx, dy);
1017}
1018
1019static int
1020SpectrogramToArea(Tk_Canvas canvas, Tk_Item *itemPtr, double *rectPtr)
1021{
1022  SpectrogramItem *spegPtr = (SpectrogramItem *) itemPtr;
1023
1024  if ((rectPtr[2] <= spegPtr->header.x1) ||
1025      (rectPtr[0] >= spegPtr->header.x2) ||
1026      (rectPtr[3] <= spegPtr->header.y1) ||
1027      (rectPtr[1] >= spegPtr->header.y2))
1028    return -1;
1029
1030  if ((rectPtr[0] <= spegPtr->header.x1) &&
1031      (rectPtr[1] <= spegPtr->header.y1) &&
1032      (rectPtr[2] >= spegPtr->header.x2) &&
1033      (rectPtr[3] >= spegPtr->header.y2))
1034    return 1;
1035
1036  return 0;
1037}
1038
1039static void
1040ScaleSpectrogram(Tk_Canvas canvas, Tk_Item *itemPtr, double ox, double oy,
1041		 double sx, double sy)
1042{
1043  SpectrogramItem *spegPtr = (SpectrogramItem *) itemPtr;
1044
1045  spegPtr->x = ox + sx * (spegPtr->x - ox);
1046  spegPtr->y = oy + sy * (spegPtr->y - oy);
1047
1048  spegPtr->width  = (int) (sx * spegPtr->width);
1049  spegPtr->height = (int) (sy * spegPtr->height);
1050
1051  if (spegPtr->si.BufPos > 0)
1052    spegPtr->si.pixpsec = spegPtr->width * spegPtr->si.samprate /
1053                       (spegPtr->esmp - spegPtr->ssmp);
1054
1055  ComputeSpectrogramBbox(canvas, spegPtr);
1056}
1057
1058static void
1059TranslateSpectrogram(Tk_Canvas canvas, Tk_Item *itemPtr, double dx, double dy)
1060{
1061  SpectrogramItem *spegPtr = (SpectrogramItem *) itemPtr;
1062
1063  spegPtr->x += dx;
1064  spegPtr->y += dy;
1065  ComputeSpectrogramBbox(canvas, spegPtr);
1066}
1067
1068#define FFTBUF2(i) *(spegPtr->si.frame[(i)>>18] + ((i)&(FRAMESIZE-1)))
1069
1070static int
1071SpectrogramToPS(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr,
1072		int prepass)
1073{
1074  SpectrogramItem *spegPtr = (SpectrogramItem *) itemPtr;
1075  char buffer[100];
1076  int x, y, i, j, noColor = 1;
1077  int nbins = spegPtr->si.fftlen / 2;
1078  int height = 2 * spegPtr->height;
1079  int width = 2 * spegPtr->width;
1080  int nfft = (int)(((spegPtr->esmp - spegPtr->ssmp)) / spegPtr->si.spacing);
1081  short v[NMAX + 1];
1082  unsigned char *imageR, *imageG, *imageB;
1083
1084  /*  if (width == 0 || height == 0) return TCL_OK;*/
1085
1086  for (i = 0; i < spegPtr->si.ncolors; i++) {
1087    if ((spegPtr->si.xcolor[i]->red != spegPtr->si.xcolor[i]->green) ||
1088	(spegPtr->si.xcolor[i]->red != spegPtr->si.xcolor[i]->blue)) {
1089      noColor = 0;
1090    }
1091  }
1092  if ((spegPtr->si.gridcolor->red != spegPtr->si.gridcolor->green) ||
1093      (spegPtr->si.gridcolor->red != spegPtr->si.gridcolor->blue)) {
1094    noColor = 0;
1095  }
1096
1097  if ((imageR = (unsigned char *) ckalloc(width * height)) == NULL)
1098    return TCL_ERROR;
1099  if ((imageG = (unsigned char *) ckalloc(width * height)) == NULL)
1100    return TCL_ERROR;
1101  if ((imageB = (unsigned char *) ckalloc(width * height)) == NULL)
1102    return TCL_ERROR;
1103
1104  for (i = 0; i < width; i++) {
1105    int top = (int) ((1.0 - (spegPtr->si.topfrequency /
1106			     (spegPtr->si.samprate/2))) * nbins);
1107    float yscale = (float) (nbins - top)/ height;
1108    float xscale = (float) (nfft - 1) / width;
1109    float k = (float) (spegPtr->si.contrast * spegPtr->si.ncolors /
1110		       (spegPtr->si.fftmax - spegPtr->si.fftmin));
1111    float fx = xscale * i;
1112    int ix  = (int) fx;
1113    float deltax = fx - ix;
1114    int p = ix     * nbins;
1115    int q = (ix + 1) * nbins;
1116
1117    for (j = 0; j < nbins; j++) {
1118
1119      if (nfft >= width) { /* subsample in x direction */
1120	v[j] = (short) (k * (FFTBUF2(p) - spegPtr->si.fftmin - spegPtr->si.bright));
1121	p++;
1122      } else {             /* interpolate in x direction */
1123	v[j] = (short) (k * ((FFTBUF2(p) - spegPtr->si.fftmin - spegPtr->si.bright) +
1124			     deltax * (FFTBUF2(q) - FFTBUF2(p))));
1125	p++; q++;
1126      }
1127    }
1128    v[nbins] = v[nbins - 1];
1129    for (j = 0; j < height; j++) {
1130      int c;
1131      float fy = yscale * j;
1132      int iy  = (int) fy;
1133
1134      if (height <= nbins) /* subsample in y direction */
1135	c = v[iy];
1136      else                /* interpolate in y direction */
1137	c = (int) (v[iy] + (fy - iy) * (v[iy + 1] - v[iy]));
1138
1139      if (c >= spegPtr->si.ncolors) {
1140	c = spegPtr->si.ncolors - 1;
1141      }
1142      if (c < 0) {
1143	c = 0;
1144      }
1145
1146      imageR[i + width * (height - j - 1)] = spegPtr->si.xcolor[c]->red >> 8;
1147      imageG[i + width * (height - j - 1)] = spegPtr->si.xcolor[c]->green >> 8;
1148      imageB[i + width * (height - j - 1)] = spegPtr->si.xcolor[c]->blue >> 8;
1149    }
1150  }
1151
1152  if ((spegPtr->si.gridFspacing > 0) && (spegPtr->si.gridTspacing > 0.0)) {
1153    float i, j;
1154    float di = (float) (spegPtr->si.pixpsec * spegPtr->si.gridTspacing);
1155    float dj = (float) ((float) height / (spegPtr->si.topfrequency /
1156					  (float) spegPtr->si.gridFspacing));
1157    int k = 0;
1158
1159    for (j = (float) height - dj; j > 0.0; j -= dj) {
1160      for (i = di; i < (float) width; i += di) {
1161	for (k = -5; k <= 5; k++) {
1162	  imageR[(int) (i+k) + width * (int) j] = spegPtr->si.gridcolor->red >> 8;
1163	  imageG[(int) (i+k) + width * (int) j] = spegPtr->si.gridcolor->green >> 8;
1164	  imageB[(int) (i+k) + width * (int) j] = spegPtr->si.gridcolor->blue >> 8;
1165	  imageR[(int) i + width * (int) (j+k)] = spegPtr->si.gridcolor->red >> 8;
1166	  imageG[(int) i + width * (int) (j+k)] = spegPtr->si.gridcolor->green >> 8;
1167	  imageB[(int) i + width * (int) (j+k)] = spegPtr->si.gridcolor->blue >> 8;
1168	}
1169      }
1170    }
1171
1172  } else if (spegPtr->si.gridFspacing > 0) {
1173    float i, j;
1174    float dj = (float) ((float) height / (spegPtr->si.topfrequency /
1175					  (float) spegPtr->si.gridFspacing));
1176
1177    for (i = 0.0; i < (float) width; i++) {
1178      for (j = (float) height - dj; j > 0.0; j -= dj) {
1179	imageR[(int) i + width * (int) j] = spegPtr->si.gridcolor->red >> 8;
1180	imageG[(int) i + width * (int) j] = spegPtr->si.gridcolor->green >> 8;
1181	imageB[(int) i + width * (int) j] = spegPtr->si.gridcolor->blue >> 8;
1182      }
1183    }
1184  } else if (spegPtr->si.gridTspacing > 0.0) {
1185    float i, j;
1186    float di = (float) (spegPtr->si.pixpsec * spegPtr->si.gridTspacing);
1187
1188    for (i = di; i < (float) width; i += di) {
1189      for (j = 0.0; j < (float) height; j++) {
1190	imageR[(int) i + width * (int) j] = spegPtr->si.gridcolor->red >> 8;
1191	imageG[(int) i + width * (int) j] = spegPtr->si.gridcolor->green >> 8;
1192	imageB[(int) i + width * (int) j] = spegPtr->si.gridcolor->blue >> 8;
1193      }
1194    }
1195  }
1196
1197  Tcl_AppendResult(interp, "%% SPEG BEGIN\n", (char *) NULL);
1198
1199  sprintf(buffer, "/pix %d string def\n%d %f translate\n", width,
1200	  spegPtr->header.x1, Tk_CanvasPsY(canvas,(double)spegPtr->header.y2));
1201  Tcl_AppendResult(interp, buffer, (char *) NULL);
1202
1203  sprintf(buffer, "%d %d scale\n", width/2, height/2);
1204  Tcl_AppendResult(interp, buffer, (char *) NULL);
1205
1206  sprintf(buffer, "%d %d 8\n", width, height);
1207  Tcl_AppendResult(interp, buffer, (char *) NULL);
1208
1209  sprintf(buffer, "[%d 0 0 %d 0 %d]\n", width, -height, height);
1210  Tcl_AppendResult(interp, buffer, (char *) NULL);
1211
1212  if (noColor) {
1213    Tcl_AppendResult(interp, "{currentfile pix readhexstring pop}\nimage\n",
1214		     (char *) NULL);
1215
1216    for (y = 0; y < height; y++) {
1217      for (x = 0; x < width; x++) {
1218	sprintf(buffer, "%.2x", imageR[x + width * y]);
1219	Tcl_AppendResult(interp, buffer, (char *) NULL);
1220      }
1221      Tcl_AppendResult(interp, "\n", (char *) NULL);
1222    }
1223  } else {
1224    Tcl_AppendResult(interp, "{currentfile pix readhexstring pop}\n",
1225		     "false 3 colorimage\n", (char *) NULL);
1226
1227    for (y = 0; y < height; y++) {
1228      for (x = 0; x < width; x++) {
1229	sprintf(buffer, "%.2x%.2x%.2x", imageR[x + width * y],
1230		imageG[x + width * y], imageB[x + width * y]);
1231	Tcl_AppendResult(interp, buffer, (char *) NULL);
1232      }
1233      Tcl_AppendResult(interp, "\n", (char *) NULL);
1234    }
1235  }
1236
1237  Tcl_AppendResult(interp, "%% SPEG END\n", (char *) NULL);
1238
1239  ckfree((char *) imageR);
1240  ckfree((char *) imageG);
1241  ckfree((char *) imageB);
1242
1243  return TCL_OK;
1244}
1245
1246#define FFTBUF(i) *(siPtr->frame[(i)>>18] + ((i)&(FRAMESIZE-1)))
1247#define LAGOM 16384.0f
1248
1249static int
1250ComputeSpeg(SnackItemInfo *siPtr, int nfft)
1251{
1252  int i = 0, j;
1253  float spacing  = siPtr->spacing;
1254  int   fftlen   = siPtr->fftlen;
1255  int   winlen   = siPtr->winlen;
1256  int   fftmax   = siPtr->fftmax;
1257  int   fftmin   = siPtr->fftmin;
1258  float preemph  = siPtr->preemph;
1259  int RestartPos = siPtr->RestartPos - siPtr->validStart;
1260  int encoding   = siPtr->encoding;
1261  int storeType  = siPtr->storeType;
1262  int        ret = nfft;
1263  int       flag = 0;
1264  float g = 1.0;
1265  SnackLinkedFileInfo info;
1266
1267  if (siPtr->debug > 2) Snack_WriteLogInt("    Enter ComputeSpeg", nfft);
1268
1269  if (storeType != SOUND_IN_MEMORY) {
1270    if (OpenLinkedFile(siPtr->sound, &info) != TCL_OK) {
1271      return(0);
1272    }
1273  }
1274
1275  if (winlen > fftlen)  /* should not happen */
1276    winlen = fftlen;
1277
1278  Snack_InitFFT(fftlen);
1279  Snack_InitWindow(siPtr->hamwin, winlen, fftlen, siPtr->windowType);
1280
1281  siPtr->doneSpeg = 0;
1282  /*  siPtr->computing = 1;*/
1283
1284  while (siPtr->frlen <= ((nfft + siPtr->nfft) * fftlen / 2)) {
1285    if ((siPtr->frame[siPtr->nfrms] = (short *) ckalloc(2*FRAMESIZE)) == NULL)
1286      return 0;
1287    siPtr->frlen += FRAMESIZE;
1288    if (siPtr->debug > 3) {
1289      Snack_WriteLogInt("      Alloced frame", siPtr->nfrms);
1290    }
1291    siPtr->nfrms++;
1292  }
1293
1294  if (siPtr->abmax > 0.0 && siPtr->abmax < LAGOM) {
1295    g = LAGOM / siPtr->abmax;
1296  }
1297  if (encoding == LIN8OFFSET || encoding == LIN8) {
1298    if (g == 1.0 && storeType != SOUND_IN_MEMORY) {
1299      g = 256.0;
1300    }
1301  }
1302
1303  /*  if (siPtr->cmdPtr != NULL) {
1304    Snack_ProgressCallback(siPtr->cmdPtr, siPtr->sound->interp,
1305			   "Computing spectrogram", 0.0);
1306  }*/
1307
1308  for (j = 0; j < nfft; j++) {
1309    if ((RestartPos + (int)(j * spacing) - fftlen / 2) >= 0 &&
1310	(RestartPos + (int)(j * spacing) + fftlen - winlen / 2 +
1311	 siPtr->nchannels) < siPtr->BufPos) {
1312
1313      if (storeType == SOUND_IN_MEMORY) {
1314	if (siPtr->nchannels == 1 || siPtr->channel != -1) {
1315	  int p = (RestartPos + (int)(j * spacing) - winlen / 2) *
1316	    siPtr->nchannels + siPtr->channel;
1317
1318	  for (i = 0; i < fftlen; i++) {
1319	    xfft[i] = (float) ((FSAMPLE(siPtr, p + siPtr->nchannels)
1320				- preemph * FSAMPLE(siPtr, p)) *
1321			       siPtr->hamwin[i]) * g;
1322	    p += siPtr->nchannels;
1323	  }
1324	  flag = 1;
1325	} else {
1326	  int c;
1327
1328	  for (i = 0; i < fftlen; i++) {
1329	    xfft[i] = 0.0;
1330	  }
1331	  for (c = 0; c < siPtr->nchannels; c++) {
1332	    int p = (RestartPos + (int)(j * spacing) - winlen / 2) *
1333	      siPtr->nchannels + c;
1334
1335	    for (i = 0; i < fftlen; i++) {
1336	      xfft[i] += (float) ((FSAMPLE(siPtr, p + siPtr->nchannels)
1337				   - preemph * FSAMPLE(siPtr, p))
1338				  * siPtr->hamwin[i]) * g;
1339	      p += siPtr->nchannels;
1340	    }
1341	    flag = 1;
1342	  }
1343	  for (i = 0; i < fftlen; i++) {
1344	    xfft[i] /= siPtr->nchannels;
1345	  }
1346	}
1347      } else { /* storeType != SOUND_IN_MEMORY */
1348	if (siPtr->nchannels == 1 || siPtr->channel != -1) {
1349	  int p = (RestartPos + (int)(j * spacing) - winlen / 2) *
1350	    siPtr->nchannels + siPtr->channel;
1351
1352	  for (i = 0; i < fftlen; i++) {
1353	    xfft[i] = (float) ((GetSample(&info, p + siPtr->nchannels)
1354				- preemph * GetSample(&info, p)) *
1355			       siPtr->hamwin[i]) * g;
1356	    p += siPtr->nchannels;
1357	  }
1358	  flag = 1;
1359	} else {
1360	  int c;
1361
1362	  for (i = 0; i < fftlen; i++) {
1363	    xfft[i] = 0.0;
1364	  }
1365	  for (c = 0; c < siPtr->nchannels; c++) {
1366	    int p = (RestartPos + (int)(j * spacing) - winlen / 2) *
1367	      siPtr->nchannels + c;
1368
1369	    for (i = 0; i < fftlen; i++) {
1370	      xfft[i] += (float) ((GetSample(&info, p + siPtr->nchannels)
1371				   - preemph * GetSample(&info, p))
1372				  * siPtr->hamwin[i]) * g;
1373	      p += siPtr->nchannels;
1374	    }
1375	    flag = 1;
1376	  }
1377	  for (i = 0; i < fftlen; i++) {
1378	    xfft[i] /= siPtr->nchannels;
1379	  }
1380	}
1381      }
1382    } else {
1383      if (flag) ret--;
1384      for (i = 0; i < fftlen; i++) xfft[i] = (float) 0.0;
1385    }
1386
1387    Snack_DBPowerSpectrum(xfft);
1388
1389    for (i = 0; i < fftlen / 2; i++) {
1390      short tmp = (short) (xfft[i] +.5);
1391      int ind = (j + siPtr->nfft) * fftlen / 2 + i;
1392      /*
1393      if (siPtr->debug > 2 && i==0) Snack_WriteLogInt("  in", tmp);
1394      if (siPtr->debug > 2 && i==0) Snack_WriteLogInt("  hm", (int)xfft[i]);
1395      */
1396#if !defined(WIN)
1397      if (tmp == 0 && (int)xfft[i] < -200) {
1398	tmp = fftmin;
1399      }
1400#endif
1401      FFTBUF(ind) = tmp;
1402      if (tmp < fftmin) fftmin = tmp;
1403      if (tmp > fftmax) fftmax = tmp;
1404    }
1405    /*    Tcl_DoOneEvent(TCL_DONT_WAIT);*/
1406    if (siPtr->doneSpeg) return(-1);
1407    /*    if ((siPtr->cmdPtr != NULL) && ((j % 100) == (100-1))) {
1408      Snack_ProgressCallback(siPtr->cmdPtr, siPtr->sound->interp,
1409			     "Computing spectrogram", (double) j/nfft);
1410    }*/
1411  } /* for...*/
1412  siPtr->doneSpeg = 1;
1413  siPtr->fftmax = fftmax;
1414  siPtr->fftmin = fftmin;
1415  siPtr->nfft += ret;
1416
1417  if (storeType != SOUND_IN_MEMORY) {
1418    CloseLinkedFile(&info);
1419  }
1420
1421  /*  if (siPtr->cmdPtr != NULL) {
1422    Snack_ProgressCallback(siPtr->cmdPtr, siPtr->sound->interp,
1423			   "Computing spectrogram", 1.0);
1424  }
1425  siPtr->computing = 0;*/
1426
1427  if (siPtr->debug > 2) {
1428    Snack_WriteLogInt("    Exit ComputeSpeg", siPtr->fftmin);
1429  }
1430
1431  return ret;
1432}
1433
1434#if defined(MAC) || defined(MAC_OSX_TCL)
1435int MacPutPixel(XImage *image, int x, int y, unsigned long pixel)
1436{
1437  /*
1438   * Define "NBBY" (number of bits per byte) if it's not already defined.
1439   */
1440
1441#ifndef NBBY
1442#   define NBBY 8
1443#endif
1444
1445  unsigned char *destPtr = (unsigned char *)&(image->data[(y *
1446	       image->bytes_per_line) + ((x * image->bits_per_pixel) / NBBY)]);
1447
1448  if (image->bits_per_pixel == 32) {
1449    destPtr[0] = 0;
1450    destPtr[1] = (unsigned char) ((pixel >> 16) & 0xff);
1451    destPtr[2] = (unsigned char) ((pixel >> 8) & 0xff);
1452    destPtr[3] = (unsigned char) (pixel & 0xff);
1453  }
1454  return 0;
1455}
1456#endif
1457
1458#define MAX_PIXELS 65536
1459
1460static void
1461DrawSpeg(SnackItemInfo *siPtr, Display* disp, GC gc, int width, int height,
1462	 int drawX, int drawW, int fpos)
1463{
1464  int i, j, len, nCols, xStart = drawX, xEnd, doWidth, bytesPerLine;
1465  short v[NMAX + 1];
1466  int nbins = siPtr->fftlen / 2;
1467  XImage *ximage;
1468  unsigned char *linePtr, *bytePtr;
1469  unsigned long *pixelmap = siPtr->pixelmap;
1470  unsigned long gridpixel = siPtr->gridcolor->pixel;
1471  int ncolors = siPtr->ncolors;
1472  int depth = siPtr->depth;
1473
1474  if (siPtr->debug > 2) Snack_WriteLogInt("    Enter DrawSpeg", drawW);
1475
1476  if (height == 0) return;
1477
1478  if (siPtr->pixelmap != NULL && siPtr->gridcolor != None) {
1479    siPtr->pixelmap[siPtr->ncolors] = siPtr->gridcolor->pixel;
1480  }
1481
1482  if (siPtr->fftmax == siPtr->fftmin) siPtr->fftmax++;
1483
1484  if (siPtr->nfft >= 0) {
1485    nCols = (MAX_PIXELS + height - 1) / height;
1486    if (nCols < 1) {
1487      nCols = 1;
1488    }
1489    if (nCols > drawW) {
1490      nCols = drawW;
1491    }
1492
1493    ximage = XCreateImage(disp, siPtr->visual, depth, ZPixmap, 0,
1494			  (char *) NULL, nCols, height, 32, 0);
1495    if (ximage == NULL) return;
1496#if defined(MAC) || defined(MAC_OSX_TCL)
1497    ximage->f.put_pixel = MacPutPixel;
1498#endif
1499
1500    if (depth >= 24) {
1501      len = (nCols + 3) * height * depth / 6;
1502    } else {
1503      len = (nCols + 3) * height * depth / 8;
1504    }
1505
1506    ximage->data = ckalloc(len);
1507
1508    if (ximage->data == NULL) {
1509      XFree((char *) ximage);
1510      return;
1511    }
1512    bytesPerLine = ((ximage->bits_per_pixel * nCols + 31) >> 3) & ~3;
1513    doWidth = drawW;
1514
1515    for (; doWidth > 0; doWidth -= nCols) {
1516      float xscale = siPtr->xUnderSamp;
1517      int fftmin = siPtr->fftmin;
1518      double offset = siPtr->bright + fftmin;
1519      float k = (float) (siPtr->contrast * siPtr->ncolors /
1520			 (siPtr->fftmax - fftmin));
1521      if (nCols > doWidth) {
1522	nCols = doWidth;
1523      }
1524      xEnd = xStart + nCols;
1525      for (i = xStart; i < xEnd; i++) {
1526	float yscale = ((float)siPtr->topfrequency * nbins /
1527			(siPtr->samprate / 2)) / height;
1528	float fx = xscale * i;
1529	int ix  = (int) fx;
1530	float deltax = fx - ix;
1531	int p = (ix + fpos) * nbins;
1532	int q = p + nbins;
1533
1534	if (drawX > 0) {
1535	  p = (ix - (int)(xscale * xStart) + fpos) * nbins;
1536	  q = p + nbins;
1537	}
1538
1539	if ((p / nbins) < 0 || (p / nbins) >= siPtr->nfft) {
1540	  for (j = 0; j < height; j++) {
1541#if !defined(WIN) && !defined(MAC) && !defined(MAC_OSX_TCL)
1542	    XPutPixel(ximage, (int) i - xStart, (int) j, pixelmap[0]);
1543#else
1544	    if (depth == 8) {
1545	      XPutPixel(ximage, (int) i - xStart, (int) j, 0);
1546	    } else {
1547	      XPutPixel(ximage, (int) i - xStart, (int) j, pixelmap[0]);
1548	    }
1549#endif
1550	  }
1551	  continue;
1552	}
1553
1554	linePtr = (unsigned char *) (ximage->data + i - xStart + bytesPerLine * (height-1));
1555
1556	bytePtr = linePtr;
1557
1558	if (siPtr->nfft >= width) { /* subsample in x direction */
1559	  for (j = 0; j < nbins; j++) {
1560	    v[j] = (short) (k * (FFTBUF(p) - offset));
1561	    p++;
1562	  }
1563	} else {             /* interpolate in x direction */
1564	  for (j = 0; j < nbins; j++) {
1565	    short fftp = FFTBUF(p);
1566	    v[j] = (short) (k * ((fftp - offset) +
1567				 deltax * (FFTBUF(q) - fftp)));
1568	    p++; q++;
1569	  }
1570	}
1571	v[nbins] = v[nbins - 1];
1572	for (j = 0; j < height; j++) {
1573	  int c;
1574	  float fy = yscale * j;
1575	  int iy  = (int) fy;
1576
1577	  if (height <= nbins) /* subsample in y direction */
1578	    c = v[iy];
1579	  else                /* interpolate in y direction */
1580	    c = (int) (v[iy] + (fy - iy) * (v[iy + 1] - v[iy]));
1581
1582	  if (c >= ncolors) {
1583	    c = ncolors - 1;
1584	  }
1585	  if (c < 0) {
1586	    c = 0;
1587	  }
1588
1589	  switch (depth) {
1590	  case 8:
1591#if !defined(WIN) && !defined(MAC) && !defined(MAC_OSX_TCL)
1592	    *bytePtr = (unsigned char) pixelmap[c];
1593#else
1594	    *bytePtr = (unsigned char) c;
1595#endif
1596	    break;
1597
1598	  default:
1599	    XPutPixel(ximage, i - xStart, height - j - 1, pixelmap[c]);
1600	  }
1601	  bytePtr -= bytesPerLine;
1602	}
1603      }
1604
1605      if ((siPtr->gridFspacing > 0) && (siPtr->gridTspacing > 0.0)) {
1606	float i, j;
1607	float di = (float) siPtr->pixpsec * (float) siPtr->gridTspacing;
1608	float dj = (height / ((float)siPtr->topfrequency / siPtr->gridFspacing));
1609	int k = 0;
1610	int xleft = width - siPtr->xTot - drawW;
1611	int xcoord;
1612
1613	for (i = xleft + di; i < (float) width; i += di) {
1614	  for (k = -5; k <= 5; k++) {
1615	    if ((int)(i+k) >= xStart && (int)(i+k) < xEnd) {
1616
1617              xcoord = (int) (i+k) - xStart;
1618	      for (j = (float) height - dj; j > 0.0; j -= dj) {
1619#if !defined(WIN) && !defined(MAC) && !defined(MAC_OSX_TCL)
1620		XPutPixel(ximage, xcoord, (int) j, gridpixel);
1621#else
1622		if (depth == 8) {
1623		  XPutPixel(ximage, xcoord, (int) j, ncolors);
1624		} else {
1625		  XPutPixel(ximage, xcoord, (int) j, gridpixel);
1626		}
1627#endif
1628	      }
1629	    }
1630	  }
1631	  if ((int) i >= xStart && (int) i < xEnd) {
1632	    for (j = (float) height - dj; j > 0.0; j -= dj) {
1633	      for (k = -5; k <= 5; k++) {
1634		if ((int)(j+k) >= 0 && (int)(j+k) < height) {
1635#if !defined(WIN) && !defined(MAC) && !defined(MAC_OSX_TCL)
1636		  XPutPixel(ximage, (int) i - xStart, (int) (j+k), gridpixel);
1637#else
1638		  if (depth == 8) {
1639		    XPutPixel(ximage, (int) i - xStart, (int) (j+k), ncolors);
1640		  } else {
1641		    XPutPixel(ximage, (int) i - xStart, (int) (j+k),gridpixel);
1642		  }
1643#endif
1644		}
1645	      }
1646	    }
1647	  }
1648	}
1649      } else if (siPtr->gridFspacing > 0) {
1650	float i, j;
1651	float dj = (height / ((float)siPtr->topfrequency/siPtr->gridFspacing));
1652
1653	for (i = 0.0; i < (float) width; i++) {
1654	  if (i >= xStart && i < xEnd) {
1655	    for (j = (float) height - dj; j > 0.0; j -= dj) {
1656#if !defined(WIN) && !defined(MAC) && !defined(MAC_OSX_TCL)
1657	      XPutPixel(ximage, (int) i - xStart, (int) j, gridpixel);
1658#else
1659	      if (depth == 8) {
1660		XPutPixel(ximage, (int) i - xStart, (int) j, ncolors);
1661	      } else {
1662		XPutPixel(ximage, (int) i - xStart, (int) j, gridpixel);
1663	      }
1664#endif
1665	    }
1666	  }
1667	}
1668      } else if (siPtr->gridTspacing > 0.0) {
1669	float i, j;
1670	float di = (float) siPtr->pixpsec * (float) siPtr->gridTspacing;
1671	int xleft = width - siPtr->xTot - drawW;
1672
1673	for (i = xleft + di; i < (float) width; i += di) {
1674	  if (i >= xStart && i < xEnd) {
1675	    for (j = 0.0; j < (float) height; j++) {
1676#if !defined(WIN) && !defined(MAC) && !defined(MAC_OSX_TCL)
1677	      XPutPixel(ximage, (int) i - xStart, (int) j, gridpixel);
1678#else
1679	      if (depth == 8) {
1680		XPutPixel(ximage, (int) i - xStart, (int) j, ncolors);
1681	      } else {
1682		XPutPixel(ximage, (int) i - xStart, (int) j, gridpixel);
1683	      }
1684#endif
1685	    }
1686	  }
1687	}
1688      }
1689      TkPutImage(siPtr->pixelmap, siPtr->ncolors + 1, disp, siPtr->pixmap,
1690		 gc, ximage, 0, 0, xStart, 0, nCols, height);
1691      xStart = xEnd;
1692    }
1693    ckfree(ximage->data);
1694    XFree((char *) ximage);
1695  }
1696
1697  if (drawX == 0) {
1698    siPtr->xTot = 0;
1699  } else {
1700    siPtr->xTot += drawW;
1701  }
1702
1703  if (siPtr->debug > 2) Snack_WriteLog("    Exit Drawspeg\n");
1704}
1705
1706static int
1707ParseColorMap(ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin,
1708	      CONST84 char *value, char *recordPtr, int offset)
1709{
1710  SpectrogramItem *spegPtr = (SpectrogramItem *) recordPtr;
1711  int argc, i;
1712  CONST84 char **argv = NULL;
1713
1714  if (Tcl_SplitList(interp, value, &argc, &argv) != TCL_OK) {
1715    Tcl_ResetResult(interp);
1716    Tcl_AppendResult(interp, "bad color map \"", value,
1717		     "\": must be list with at least two colors",
1718		     (char *) NULL);
1719    return TCL_ERROR;
1720  }
1721
1722  if (argc == 1) {
1723    Tcl_ResetResult(interp);
1724    Tcl_AppendResult(interp, "bad color map \"", value,
1725		     "\": must be list with at least two colors",
1726		     (char *) NULL);
1727    if (argv != NULL) {
1728      ckfree((char *) argv);
1729    }
1730    return TCL_ERROR;
1731  }
1732
1733  for (i = 0; i < spegPtr->si.ncolors; i++) {
1734    Tk_FreeColor(spegPtr->si.xcolor[i]);
1735  }
1736
1737  if (argc == 0) {
1738    spegPtr->si.ncolors = NDEFCOLS;
1739  } else {
1740    spegPtr->si.ncolors = argc;
1741  }
1742
1743  spegPtr->si.xcolor = (XColor **) ckalloc(spegPtr->si.ncolors * sizeof(XColor*));
1744
1745  if (spegPtr->si.xcolor == NULL) {
1746    Tcl_ResetResult(interp);
1747    Tcl_AppendResult(interp, "Not enough memory to allocate colormap", NULL);
1748    if (argv != NULL) {
1749      ckfree((char *) argv);
1750    }
1751    return TCL_ERROR;
1752  }
1753
1754  spegPtr->si.pixelmap = (unsigned long *) ckalloc((spegPtr->si.ncolors + 1) * sizeof(unsigned long));
1755
1756  if (spegPtr->si.pixelmap == NULL) {
1757    ckfree((char *) spegPtr->si.xcolor);
1758    Tcl_ResetResult(interp);
1759    Tcl_AppendResult(interp, "Not enough memory to allocate pixelmap", NULL);
1760    if (argv != NULL) {
1761      ckfree((char *) argv);
1762    }
1763    return TCL_ERROR;
1764  }
1765
1766  if (argc == 0) {
1767    for (i = 0; i < spegPtr->si.ncolors; i++) {
1768      XColor xcol;
1769
1770      xcol.flags = DoRed | DoGreen | DoBlue;
1771      xcol.red   = 65535 - (i * 65535 / (spegPtr->si.ncolors - 1));
1772      xcol.green = 65535 - (i * 65535 / (spegPtr->si.ncolors - 1));
1773      xcol.blue  = 65535 - (i * 65535 / (spegPtr->si.ncolors - 1));
1774      spegPtr->si.xcolor[i] = Tk_GetColorByValue(Tk_MainWindow(interp), &xcol);
1775      spegPtr->si.pixelmap[i] = spegPtr->si.xcolor[i]->pixel;
1776    }
1777  } else {
1778    for (i = 0; i < spegPtr->si.ncolors; i++) {
1779      spegPtr->si.xcolor[i] = Tk_GetColor(interp, Tk_MainWindow(interp), argv[i]);
1780      if (spegPtr->si.xcolor[i] == NULL) {
1781	ckfree((char *) spegPtr->si.xcolor);
1782	ckfree((char *) spegPtr->si.pixelmap);
1783	Tcl_ResetResult(interp);
1784	Tcl_AppendResult(interp, "unknown color name \"", argv[i],
1785			 "\"", (char *) NULL);
1786	if (argv != NULL) {
1787	  ckfree((char *) argv);
1788	}
1789	return TCL_ERROR;
1790      }
1791      spegPtr->si.pixelmap[i] = spegPtr->si.xcolor[i]->pixel;
1792    }
1793  }
1794
1795  ckfree((char *) argv);
1796
1797  return TCL_OK;
1798}
1799
1800static char*
1801PrintColorMap(ClientData clientData, Tk_Window tkwin, char *recordPtr,
1802	      int offset, Tcl_FreeProc **freeProcPtr)
1803{
1804  SpectrogramItem *spegPtr = (SpectrogramItem *) recordPtr;
1805  char *buffer;
1806  int i, j = 0;
1807
1808  *freeProcPtr = TCL_DYNAMIC;
1809  buffer = (char *) ckalloc(spegPtr->si.ncolors * 20);
1810  for (i = 0; i < spegPtr->si.ncolors; i++) {
1811    j += (int) sprintf(&buffer[j], "%s ", Tk_NameOfColor(spegPtr->si.xcolor[i]));
1812  }
1813  sprintf(&buffer[j], "\n");
1814  return buffer;
1815}
1816