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 #define SNACK_DEFAULT_SECTWINTYPE SNACK_WIN_HAMMING
33 #define SNACK_DEFAULT_SECTWINTYPE_NAME "hamming"
34
35 #define SNACK_DEFAULT_LPC_ORDER "20"
36
37 /*
38 * Section item structure
39 */
40
41 typedef struct SectionItem {
42
43 Tk_Item header;
44 Tk_Canvas canvas;
45 double x, y;
46 Tk_Anchor anchor;
47 int nPoints;
48 double *coords;
49 XColor *fg;
50 Pixmap fillStipple;
51 GC gc;
52 char *newSoundName;
53 char *soundName;
54 Sound *sound;
55 SnackItemInfo si;
56 float *xfft;
57 double *ffts;
58 int height;
59 int width;
60 int startSmp;
61 int endSmp;
62 int ssmp;
63 int esmp;
64 int frame;
65 int id;
66 XPoint fpts[5];
67 char *channelstr;
68 int debug;
69 double topFrequency;
70 double maxValue;
71 double minValue;
72 char *windowTypeStr;
73 char *analysisTypeStr;
74 int type;
75 int lpcOrder;
76 Tcl_Interp *interp;
77 double preemph;
78
79 } SectionItem;
80
81 Tk_CustomOption sectTagsOption = { (Tk_OptionParseProc *) NULL,
82 (Tk_OptionPrintProc *) NULL,
83 (ClientData) NULL };
84
85 typedef enum {
86 OPTION_ANCHOR,
87 OPTION_TAGS,
88 OPTION_SOUND,
89 OPTION_HEIGHT,
90 OPTION_WIDTH,
91 OPTION_FFTLEN,
92 OPTION_WINLEN,
93 OPTION_PREEMP,
94 OPTION_START,
95 OPTION_END,
96 OPTION_FILL,
97 OPTION_STIPPLE,
98 OPTION_FRAME,
99 OPTION_TOPFREQUENCY,
100 OPTION_CHANNEL,
101 OPTION_MAXVAL,
102 OPTION_MINVAL,
103 OPTION_SKIP,
104 OPTION_WINTYPE,
105 OPTION_ANALYSISTYPE,
106 OPTION_LPCORDER
107 } ConfigSpec;
108
109 static Tk_ConfigSpec configSpecs[] = {
110
111 {TK_CONFIG_ANCHOR, "-anchor", (char *) NULL, (char *) NULL,
112 "nw", Tk_Offset(SectionItem, anchor), TK_CONFIG_DONT_SET_DEFAULT},
113
114 {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
115 (char *) NULL, 0, TK_CONFIG_NULL_OK, §TagsOption},
116
117 {TK_CONFIG_STRING, "-sound", (char *) NULL, (char *) NULL,
118 "", Tk_Offset(SectionItem, newSoundName), TK_CONFIG_NULL_OK},
119
120 {TK_CONFIG_INT, "-height", (char *) NULL, (char *) NULL,
121 "256", Tk_Offset(SectionItem, height), 0},
122
123 {TK_CONFIG_PIXELS, "-width", (char *) NULL, (char *) NULL,
124 "256", Tk_Offset(SectionItem, width), 0},
125
126 {TK_CONFIG_INT, "-fftlength", (char *) NULL, (char *) NULL,
127 "512", Tk_Offset(SectionItem, si.fftlen), 0},
128
129 {TK_CONFIG_INT, "-winlength", (char *) NULL, (char *) NULL,
130 "256", Tk_Offset(SectionItem, si.winlen), 0},
131
132 {TK_CONFIG_DOUBLE, "-preemphasisfactor", (char *) NULL, (char *) NULL,
133 "0.0", Tk_Offset(SectionItem, preemph), 0},
134
135 {TK_CONFIG_INT, "-start", (char *) NULL, (char *) NULL,
136 "0", Tk_Offset(SectionItem, startSmp), 0},
137
138 {TK_CONFIG_INT, "-end", (char *) NULL, (char *) NULL,
139 "-1", Tk_Offset(SectionItem, endSmp), 0},
140
141 {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL,
142 "black", Tk_Offset(SectionItem, fg), TK_CONFIG_NULL_OK},
143
144 {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL,
145 (char *) NULL, Tk_Offset(SectionItem, fillStipple), TK_CONFIG_NULL_OK},
146
147 {TK_CONFIG_BOOLEAN, "-frame", (char *) NULL, (char *) NULL,
148 "no", Tk_Offset(SectionItem, frame), TK_CONFIG_NULL_OK},
149
150 {TK_CONFIG_DOUBLE, "-topfrequency", (char *) NULL, (char *) NULL,
151 "0.0", Tk_Offset(SectionItem, topFrequency), 0},
152
153 {TK_CONFIG_STRING, "-channel", (char *) NULL, (char *) NULL,
154 "-1", Tk_Offset(SectionItem, channelstr), TK_CONFIG_NULL_OK},
155
156 {TK_CONFIG_DOUBLE, "-maxvalue", (char *) NULL, (char *) NULL,
157 "0.0", Tk_Offset(SectionItem, maxValue), 0},
158
159 {TK_CONFIG_DOUBLE, "-minvalue", (char *) NULL, (char *) NULL,
160 "-80.0", Tk_Offset(SectionItem, minValue), 0},
161
162 {TK_CONFIG_INT, "-skip", (char *) NULL, (char *) NULL,
163 "-1", Tk_Offset(SectionItem, si.skip), 0},
164
165 {TK_CONFIG_STRING, "-windowtype", (char *) NULL, (char *) NULL,
166 SNACK_DEFAULT_SECTWINTYPE_NAME, Tk_Offset(SectionItem, windowTypeStr), 0},
167
168 {TK_CONFIG_STRING, "-analysistype", (char *) NULL, (char *) NULL,
169 "fft", Tk_Offset(SectionItem, analysisTypeStr), 0},
170
171 {TK_CONFIG_INT, "-lpcorder", (char *) NULL, (char *) NULL,
172 SNACK_DEFAULT_LPC_ORDER, Tk_Offset(SectionItem, lpcOrder), 0},
173
174 {TK_CONFIG_INT, "-debug", (char *) NULL, (char *) NULL,
175 "0", Tk_Offset(SectionItem, debug), 0},
176
177 {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
178 (char *) NULL, 0, 0}
179
180 };
181
182 /*
183 * Protos
184 */
185
186 static void ComputeSectionBbox(Tk_Canvas canvas, SectionItem *sectPtr);
187
188 static int ComputeSectionCoords(Tk_Item *itemPtr);
189
190 static int ConfigureSection(Tcl_Interp *interp, Tk_Canvas canvas,
191 Tk_Item *itemPtr, int argc,
192 char **argv, int flags);
193
194 static int CreateSection(Tcl_Interp *interp, Tk_Canvas canvas,
195 struct Tk_Item *itemPtr, int argc, char **argv);
196
197 static void DeleteSection(Tk_Canvas canvas, Tk_Item *itemPtr,
198 Display *display);
199
200 static void DisplaySection(Tk_Canvas canvas, Tk_Item *itemPtr,
201 Display *display, Drawable dst,
202 int x, int y, int width, int height);
203
204 static void ScaleSection(Tk_Canvas canvas, Tk_Item *itemPtr,
205 double originX, double originY,
206 double scaleX, double scaleY);
207
208 static int SectionCoords(Tcl_Interp *interp, Tk_Canvas canvas,
209 Tk_Item *itemPtr, int argc, char **argv);
210
211 static int SectionToArea(Tk_Canvas canvas, Tk_Item *itemPtr,
212 double *rectPtr);
213
214 static double SectionToPoint(Tk_Canvas canvas, Tk_Item *itemPtr,
215 double *coords);
216
217 static int SectionToPS(Tcl_Interp *interp, Tk_Canvas canvas,
218 Tk_Item *itemPtr, int prepass);
219
220 static void TranslateSection(Tk_Canvas canvas, Tk_Item *itemPtr,
221 double deltaX, double deltaY);
222
223 /*
224 * Section item type
225 */
226
227 Tk_ItemType snackSectionType = {
228 "section",
229 sizeof(SectionItem),
230 CreateSection,
231 configSpecs,
232 ConfigureSection,
233 SectionCoords,
234 DeleteSection,
235 DisplaySection,
236 0,
237 SectionToPoint,
238 SectionToArea,
239 SectionToPS,
240 ScaleSection,
241 TranslateSection,
242 (Tk_ItemIndexProc *) NULL,
243 (Tk_ItemCursorProc *) NULL,
244 (Tk_ItemSelectionProc *) NULL,
245 (Tk_ItemInsertProc *) NULL,
246 (Tk_ItemDCharsProc *) NULL,
247 (Tk_ItemType *) NULL
248 };
249
250 static int
CreateSection(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,int argc,char ** argv)251 CreateSection(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr,
252 int argc, char **argv)
253 {
254 SectionItem *sectPtr = (SectionItem *) itemPtr;
255
256 if (argc < 2) {
257 Tcl_AppendResult(interp, "wrong # args: should be \"",
258 Tk_PathName(Tk_CanvasTkwin(canvas)), " create ",
259 itemPtr->typePtr->name, " x y ?opts?\"", (char *) NULL);
260 return TCL_ERROR;
261 }
262
263 sectPtr->canvas = canvas;
264 sectPtr->anchor = TK_ANCHOR_NW;
265 sectPtr->nPoints = 0;
266 sectPtr->coords = NULL;
267 sectPtr->fg = None;
268 sectPtr->fillStipple = None;
269 sectPtr->gc = None;
270 sectPtr->newSoundName = NULL;
271 sectPtr->soundName = NULL;
272 sectPtr->sound = NULL;
273 sectPtr->si.samprate = 16000;
274 sectPtr->si.BufPos = 0;
275 sectPtr->si.fftlen = 512;
276 sectPtr->si.winlen = 256;
277 sectPtr->preemph = 0.0;
278 sectPtr->si.hamwin = (float *) ckalloc(NMAX * sizeof(float));
279 sectPtr->si.abmax = 0.0f;
280 sectPtr->xfft = (float *) ckalloc(NMAX * sizeof(float));
281 sectPtr->ffts = (double *) ckalloc(NMAX / 2 * sizeof(double));
282 sectPtr->height = 256;
283 sectPtr->width = 256;
284 sectPtr->startSmp = 0;
285 sectPtr->endSmp = -1;
286 sectPtr->ssmp = 0;
287 sectPtr->esmp = -1;
288 sectPtr->id = 0;
289 sectPtr->frame = 0;
290 sectPtr->debug = 0;
291 sectPtr->x = 0;
292 sectPtr->y = 0;
293 sectPtr->topFrequency = 0.0;
294 sectPtr->channelstr = NULL;
295 sectPtr->si.channel = -1;
296 sectPtr->si.channelSet = -1;
297 sectPtr->si.nchannels = 1;
298 sectPtr->maxValue = 0.0;
299 sectPtr->minValue = -80.0;
300 sectPtr->si.validStart = 0;
301 sectPtr->si.skip = -1;
302 sectPtr->si.windowType = SNACK_DEFAULT_SECTWINTYPE;
303 sectPtr->si.windowTypeSet = SNACK_DEFAULT_SECTWINTYPE;
304 sectPtr->windowTypeStr = NULL;
305 sectPtr->analysisTypeStr = NULL;
306 sectPtr->type = 0;
307 sectPtr->lpcOrder = atoi(SNACK_DEFAULT_LPC_ORDER);
308 sectPtr->interp = interp;
309
310 if (sectPtr->si.hamwin == NULL) {
311 Tcl_AppendResult(interp, "Couldn't allocate analysis window buffer!",NULL);
312 return TCL_ERROR;
313 }
314
315 if (sectPtr->xfft == NULL) {
316 Tcl_AppendResult(interp, "Couldn't allocate fft buffer!", NULL);
317 ckfree((char *)sectPtr->si.hamwin);
318 return TCL_ERROR;
319 }
320
321 if (sectPtr->ffts == NULL) {
322 Tcl_AppendResult(interp, "Couldn't allocate fft buffer!", NULL);
323 ckfree((char *)sectPtr->si.hamwin);
324 ckfree((char *)sectPtr->xfft);
325 return TCL_ERROR;
326 }
327
328
329 if ((Tk_CanvasGetCoord(interp, canvas, argv[0], §Ptr->x) != TCL_OK) ||
330 (Tk_CanvasGetCoord(interp, canvas, argv[1], §Ptr->y) != TCL_OK))
331 return TCL_ERROR;
332
333 if (ConfigureSection(interp, canvas, itemPtr, argc-2, argv+2, 0) == TCL_OK)
334 return TCL_OK;
335
336 DeleteSection(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
337 return TCL_ERROR;
338 }
339
340 static int
SectionCoords(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,int argc,char ** argv)341 SectionCoords(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr,
342 int argc, char **argv)
343 {
344 SectionItem *wPtr = (SectionItem *) itemPtr;
345 char xc[TCL_DOUBLE_SPACE], yc[TCL_DOUBLE_SPACE];
346
347 if (argc == 0) {
348 Tcl_PrintDouble(interp, wPtr->x, xc);
349 Tcl_PrintDouble(interp, wPtr->y, yc);
350 Tcl_AppendResult(interp, xc, " ", yc, (char *) NULL);
351 } else if (argc == 2) {
352 if ((Tk_CanvasGetCoord(interp, canvas, argv[0], &wPtr->x) != TCL_OK) ||
353 (Tk_CanvasGetCoord(interp, canvas, argv[1], &wPtr->y) != TCL_OK)) {
354 return TCL_ERROR;
355 }
356 ComputeSectionBbox(canvas, wPtr);
357 } else {
358 char buf[80];
359
360 sprintf(buf, "wrong # coordinates: expected 0 or 2, got %d", argc);
361 Tcl_SetResult(interp, buf, TCL_VOLATILE);
362
363 return TCL_ERROR;
364 }
365 return TCL_OK;
366 }
367
368 static int
ComputeSectionCoords(Tk_Item * itemPtr)369 ComputeSectionCoords(Tk_Item *itemPtr)
370 {
371 SectionItem *sectPtr = (SectionItem *) itemPtr;
372 int i;
373 int nPoints = sectPtr->nPoints;
374 float xscale = (float) (sectPtr->width) / nPoints;
375 float yscale = (float) ((float) (sectPtr->height - 1) /
376 (sectPtr->minValue - sectPtr->maxValue));
377 float fscale = (float) (sectPtr->si.topfrequency / (sectPtr->si.samprate / 2.0));
378
379 if (sectPtr->debug > 1) {
380 Snack_WriteLogInt(" Enter ComputeSectionCoords", nPoints);
381 }
382
383 if (sectPtr->coords != NULL) ckfree((char *) sectPtr->coords);
384 sectPtr->coords = (double *) ckalloc((unsigned)
385 (sizeof(double) * (2 * nPoints)));
386
387 for (i = 0; i < nPoints; i++) {
388 double t = (double) (sectPtr->ffts[(int)((float)i*fscale)] -
389 sectPtr->maxValue) * yscale;
390 if (t > sectPtr->height-1) t = (double) (sectPtr->height-1);
391 if (t < 0.0) t = 0.0;
392 sectPtr->coords[i*2] = (double) i * xscale;
393 sectPtr->coords[i*2+1] = t;
394 }
395
396 ComputeSectionBbox(sectPtr->canvas, sectPtr);
397
398 if (sectPtr->debug) Snack_WriteLog("Exit ComputeSectionCoords\n");
399
400 return TCL_OK;
401 }
402
403 void
GetFloatMonoSigSect(SnackItemInfo * siPtr,SnackLinkedFileInfo * info,float * sig,int beg,int len)404 GetFloatMonoSigSect(SnackItemInfo *siPtr,SnackLinkedFileInfo *info,
405 float *sig,int beg, int len) {
406 /* sig buffer must be allocated, file must be open! */
407
408 int i;
409
410 if (siPtr->storeType == SOUND_IN_MEMORY) {
411 if (siPtr->nchannels == 1 || siPtr->channel != -1) {
412 int p = beg * siPtr->nchannels + siPtr->channel;
413
414 for (i = 0; i < len; i++) {
415 sig[i] = (float) (FSAMPLE(siPtr, p));
416 p += siPtr->nchannels;
417 }
418 } else {
419 int c;
420
421 for (i = 0; i < len; i++) {
422 sig[i] = 0.0;
423 }
424 for (c = 0; c < siPtr->nchannels; c++) {
425 int p = beg * siPtr->nchannels + c;
426
427 for (i = 0; i < len; i++) {
428 sig[i] += (float) (FSAMPLE(siPtr, p));
429 p += siPtr->nchannels;
430 }
431 }
432 for (i = 0; i < len; i++) {
433 sig[i] /= siPtr->nchannels;
434 }
435 }
436 } else { /* storeType != SOUND_IN_MEMORY */
437 if (siPtr->nchannels == 1 || siPtr->channel != -1) {
438 int p = beg * siPtr->nchannels + siPtr->channel;
439
440 for (i = 0; i < len; i++) {
441 sig[i] = (float) (GetSample(info, p));
442 p += siPtr->nchannels;
443 }
444 } else {
445 int c;
446
447 for (i = 0; i < len; i++) {
448 sig[i] = 0.0;
449 }
450 for (c = 0; c < siPtr->nchannels; c++) {
451 int p = beg * siPtr->nchannels + c;
452
453 for (i = 0; i < len; i++) {
454 sig[i] += (float) (GetSample(info, p));
455 p += siPtr->nchannels;
456 }
457 }
458 for (i = 0; i < len; i++) {
459 sig[i] /= siPtr->nchannels;
460 }
461 }
462 }
463 }
464
465 extern void Snack_PowerSpectrum(float *z);
466
467 void
ComputeSection(Tk_Item * itemPtr)468 ComputeSection(Tk_Item *itemPtr)
469 {
470 SectionItem *sectPtr = (SectionItem *) itemPtr;
471 SnackItemInfo *siPtr = §Ptr->si;
472 int i, j;
473 int fftlen = siPtr->fftlen;
474 int winlen = siPtr->winlen;
475 float preemph = siPtr->preemph;
476 int RestartPos = siPtr->RestartPos - siPtr->validStart;
477 int storeType = siPtr->storeType;
478 int n, skip = siPtr->skip;
479 SnackLinkedFileInfo info;
480 float *sig_lpc;
481 float presample = 0.0;
482 int siglen;
483 float g_lpc;
484
485 if (sectPtr->debug) Snack_WriteLogInt("Enter ComputeSection", sectPtr->ssmp);
486
487 if (skip < 1) {
488 skip = fftlen;
489 }
490 siglen = sectPtr->esmp - siPtr->RestartPos;
491 n = siglen / skip;
492
493 for (i = 0; i < fftlen/2; i++) {
494 sectPtr->ffts[i] = 0.0;
495 }
496
497 if (n == 0) return;
498
499 Snack_InitFFT(fftlen);
500 Snack_InitWindow(siPtr->hamwin, winlen, fftlen, siPtr->windowType);
501
502 if (storeType != SOUND_IN_MEMORY) {
503 if (OpenLinkedFile(sectPtr->sound, &info) != TCL_OK) {
504 return;
505 }
506 }
507
508 if (sectPtr->type != 0 && n > 0) { /* LPC + FFT */
509
510 sig_lpc = (float *) ckalloc(siglen * sizeof(float));
511
512 GetFloatMonoSigSect(siPtr,&info,sig_lpc,RestartPos,siglen);
513 if (RestartPos > 0)
514 GetFloatMonoSigSect(siPtr,&info,&presample,RestartPos-1,1);
515 PreEmphase(sig_lpc,presample,siglen,preemph);
516
517 /* windowing signal to make lpc look more like the fft spectrum ??? */
518 for (i = 0; i < winlen/2; i++) {
519 sig_lpc[i] = sig_lpc[i] * siPtr->hamwin[i];
520 }
521 for (i = winlen/2; i < winlen; i++) {
522 sig_lpc[i+siglen-winlen] = sig_lpc[i+siglen-winlen] * siPtr->hamwin[i];
523 }
524
525 g_lpc = LpcAnalysis(sig_lpc,siglen,sectPtr->xfft,sectPtr->lpcOrder);
526 ckfree((char *)sig_lpc);
527
528 for (i=0; i<=sectPtr->lpcOrder; i++) {
529 /* the factor is a guess, try looking for analytical value */
530 sectPtr->xfft[i] = sectPtr->xfft[i] * 5000000000.0f;
531 }
532 for (i = sectPtr->lpcOrder + 1; i < fftlen; i++) {
533 sectPtr->xfft[i] = 0.0;
534 }
535
536 Snack_DBPowerSpectrum(sectPtr->xfft);
537
538 for (i = 0; i < fftlen/2; i++) {
539 sectPtr->ffts[i] = -sectPtr->xfft[i];
540 }
541 } else { /* usual FFT */
542
543 for (j = 0; j < n; j++) {
544 if (storeType == SOUND_IN_MEMORY) {
545 if (siPtr->nchannels == 1 || siPtr->channel != -1) {
546 int p = (RestartPos + j * skip) * siPtr->nchannels + siPtr->channel;
547
548 for (i = 0; i < fftlen; i++) {
549 sectPtr->xfft[i] = (float) ((FSAMPLE(siPtr, p + siPtr->nchannels)
550 - preemph * FSAMPLE(siPtr, p))
551 * siPtr->hamwin[i]);
552 p += siPtr->nchannels;
553 }
554 } else {
555 int c;
556
557 for (i = 0; i < fftlen; i++) {
558 sectPtr->xfft[i] = 0.0;
559 }
560 for (c = 0; c < siPtr->nchannels; c++) {
561 int p = (RestartPos + j * skip) * siPtr->nchannels + c;
562
563 for (i = 0; i < fftlen; i++) {
564 sectPtr->xfft[i] += (float)((FSAMPLE(siPtr, p + siPtr->nchannels)
565 - preemph * FSAMPLE(siPtr, p))
566 * siPtr->hamwin[i]);
567 p += siPtr->nchannels;
568 }
569 }
570 for (i = 0; i < fftlen; i++) {
571 sectPtr->xfft[i] /= siPtr->nchannels;
572 }
573 }
574 } else { /* storeType != SOUND_IN_MEMORY */
575 if (siPtr->nchannels == 1 || siPtr->channel != -1) {
576 int p = (RestartPos + j * skip) * siPtr->nchannels + siPtr->channel;
577
578 for (i = 0; i < fftlen; i++) {
579 sectPtr->xfft[i] = (float) ((GetSample(&info, p + siPtr->nchannels)
580 - preemph * GetSample(&info, p))
581 * siPtr->hamwin[i]);
582 p += siPtr->nchannels;
583 }
584 } else {
585 int c;
586
587 for (i = 0; i < fftlen; i++) {
588 sectPtr->xfft[i] = 0.0;
589 }
590 for (c = 0; c < siPtr->nchannels; c++) {
591 int p = (RestartPos + j * skip) * siPtr->nchannels + c;
592
593 for (i = 0; i < fftlen; i++) {
594 sectPtr->xfft[i] += (float)((GetSample(&info, p+siPtr->nchannels)
595 - preemph * GetSample(&info, p))
596 * siPtr->hamwin[i]);
597 p += siPtr->nchannels;
598 }
599 }
600 for (i = 0; i < fftlen; i++) {
601 sectPtr->xfft[i] /= siPtr->nchannels;
602 }
603 }
604 }
605
606 Snack_PowerSpectrum(sectPtr->xfft);
607
608 for (i = 0; i < fftlen/2; i++) {
609 sectPtr->ffts[i] += sectPtr->xfft[i];
610 }
611 }
612
613 for (i = 0; i < fftlen/2; i++) {
614 sectPtr->ffts[i] = sectPtr->ffts[i] / (float) n;
615 }
616
617 for (i = 1; i < fftlen/2; i++) {
618 if (sectPtr->ffts[i] < SNACK_INTLOGARGMIN)
619 sectPtr->ffts[i] = SNACK_INTLOGARGMIN;
620 sectPtr->ffts[i] = (float)(SNACK_DB*log(sectPtr->ffts[i]) - SNACK_CORRN);
621 }
622 if (sectPtr->ffts[0] < SNACK_INTLOGARGMIN)
623 sectPtr->ffts[0] = SNACK_INTLOGARGMIN;
624 sectPtr->ffts[0] = (float)(SNACK_DB*log(sectPtr->ffts[0]) - SNACK_CORR0);
625 }
626 if (storeType != SOUND_IN_MEMORY) {
627 CloseLinkedFile(&info);
628 }
629
630 if (sectPtr->debug) Snack_WriteLog("Exit ComputeSection");
631 }
632
633 static void
UpdateSection(ClientData clientData,int flag)634 UpdateSection(ClientData clientData, int flag)
635 {
636 SectionItem *sectPtr = (SectionItem *) clientData;
637 Sound *s = sectPtr->sound;
638
639 if (sectPtr->debug) Snack_WriteLogInt("Enter UpdateSection", flag);
640
641 if (sectPtr->canvas == NULL) return;
642
643 if (flag == SNACK_DESTROY_SOUND) {
644 sectPtr->sound = NULL;
645 if (sectPtr->id) Snack_RemoveCallback(s, sectPtr->id);
646 sectPtr->id = 0;
647 return;
648 }
649
650 Tk_CanvasEventuallyRedraw(sectPtr->canvas,
651 sectPtr->header.x1, sectPtr->header.y1,
652 sectPtr->header.x2, sectPtr->header.y2);
653
654 sectPtr->si.blocks = s->blocks;
655 sectPtr->si.BufPos = s->length;
656 sectPtr->si.storeType = s->storeType;
657 sectPtr->si.samprate = s->samprate;
658 sectPtr->si.encoding = s->encoding;
659 sectPtr->si.nchannels = s->nchannels;
660
661 if (flag == SNACK_MORE_SOUND) {
662 sectPtr->esmp = sectPtr->si.BufPos - 1;
663 sectPtr->ssmp = sectPtr->esmp - sectPtr->si.fftlen;
664
665 if (sectPtr->ssmp < 0) {
666 sectPtr->ssmp = 0;
667 }
668
669 sectPtr->si.RestartPos = sectPtr->ssmp;
670 }
671
672 if (flag == SNACK_NEW_SOUND) {
673 sectPtr->esmp = sectPtr->endSmp;
674
675 if (sectPtr->endSmp < 0)
676 sectPtr->esmp = sectPtr->si.BufPos - 1;
677
678 if (sectPtr->endSmp > sectPtr->si.BufPos - 1)
679 sectPtr->esmp = sectPtr->si.BufPos - 1;
680
681 if (sectPtr->startSmp > sectPtr->endSmp && sectPtr->endSmp >= 0)
682 sectPtr->startSmp = sectPtr->endSmp;
683
684 if (sectPtr->startSmp < 0)
685 sectPtr->startSmp = 0;
686
687 sectPtr->ssmp = sectPtr->startSmp;
688
689 if (sectPtr->ssmp > sectPtr->esmp)
690 sectPtr->ssmp = sectPtr->esmp;
691
692 if (sectPtr->ssmp > sectPtr->esmp - sectPtr->si.fftlen) {
693 sectPtr->esmp = sectPtr->ssmp + sectPtr->si.fftlen;
694 if (sectPtr->esmp > sectPtr->si.BufPos - 1) {
695 sectPtr->esmp = sectPtr->si.BufPos - 1;
696 sectPtr->ssmp = sectPtr->esmp - sectPtr->si.fftlen;
697 if (sectPtr->ssmp < 0) {
698 sectPtr->ssmp = 0;
699 }
700 }
701 }
702
703 if (sectPtr->topFrequency <= 0.0) {
704 sectPtr->si.topfrequency = sectPtr->si.samprate / 2.0;
705 } else if (sectPtr->topFrequency > sectPtr->si.samprate / 2.0) {
706 sectPtr->si.topfrequency = sectPtr->si.samprate / 2.0;
707 } else {
708 sectPtr->si.topfrequency = sectPtr->topFrequency;
709 }
710 }
711 sectPtr->si.channel = sectPtr->si.channelSet;
712 if (sectPtr->si.nchannels == 1) {
713 sectPtr->si.channel = 0;
714 }
715
716 sectPtr->si.validStart = s->validStart;
717
718 ComputeSection((Tk_Item *)sectPtr);
719
720 if (ComputeSectionCoords((Tk_Item *)sectPtr) != TCL_OK) {
721 return;
722 }
723
724 Tk_CanvasEventuallyRedraw(sectPtr->canvas,
725 sectPtr->header.x1, sectPtr->header.y1,
726 sectPtr->header.x2, sectPtr->header.y2);
727
728 if (sectPtr->debug) Snack_WriteLog("Exit UpdateSection\n");
729 }
730
731 static int
ConfigureSection(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,int argc,char ** argv,int flags)732 ConfigureSection(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr,
733 int argc, char **argv, int flags)
734 {
735 SectionItem *sectPtr = (SectionItem *) itemPtr;
736 Sound *s = sectPtr->sound;
737 Tk_Window tkwin = Tk_CanvasTkwin(canvas);
738 XGCValues gcValues;
739 GC newGC;
740 unsigned long mask;
741 int doCompute = 0;
742 int i, j;
743
744 if (argc == 0) return TCL_OK;
745
746 if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc,
747 (CONST84 char **)argv,
748 (char *) sectPtr, flags) != TCL_OK) return TCL_ERROR;
749
750 if (sectPtr->debug) Snack_WriteLog("Enter ConfigureSection\n");
751
752 for (i = 0; configSpecs[i].type != TK_CONFIG_END; i++) {
753 for (j = 0; j < argc; j += 2) {
754 if (strncmp(argv[j], configSpecs[i].argvName, strlen(argv[j])) == 0) {
755 configSpecs[i].specFlags |= TK_CONFIG_OPTION_SPECIFIED;
756 break;
757 }
758 }
759 }
760
761 #if defined(MAC) || defined(MAC_OSX_TCL)
762 for (i = 0; i < argc; i++) {
763 if (strncmp(argv[i], "-anchor", strlen(argv[i])) == 0) {
764 i++;
765 if (strcmp(argv[i], "ne") == 0) {
766 sectPtr->anchor = 1;
767 } else if (strcmp(argv[i], "nw") == 0) {
768 sectPtr->anchor = 7;
769 } else if (strcmp(argv[i], "n") == 0) {
770 sectPtr->anchor = 0;
771 } else if (strcmp(argv[i], "e") == 0) {
772 sectPtr->anchor = 2;
773 } else if (strcmp(argv[i], "se") == 0) {
774 sectPtr->anchor = 3;
775 } else if (strcmp(argv[i], "sw") == 0) {
776 sectPtr->anchor = 5;
777 } else if (strcmp(argv[i], "s") == 0) {
778 sectPtr->anchor = 4;
779 } else if (strcmp(argv[i], "w") == 0) {
780 sectPtr->anchor = 6;
781 } else if (strncmp(argv[i], "center", strlen(argv[i])) == 0) {
782 sectPtr->anchor = 8;
783 }
784 break;
785 }
786 }
787 #endif
788
789 if (CheckFFTlen(interp, sectPtr->si.fftlen) != TCL_OK) return TCL_ERROR;
790
791 if (CheckWinlen(interp, sectPtr->si.winlen, sectPtr->si.fftlen) != TCL_OK)
792 return TCL_ERROR;
793
794 if (CheckLPCorder(interp, sectPtr->lpcOrder) != TCL_OK) return TCL_ERROR;
795
796 if (OptSpecified(OPTION_SOUND)) {
797 if (sectPtr->newSoundName == NULL) {
798 sectPtr->sound = NULL;
799 if (sectPtr->id) Snack_RemoveCallback(s, sectPtr->id);
800 sectPtr->id = 0;
801 sectPtr->si.BufPos = 0;
802 doCompute = 1;
803 } else {
804 if ((s = Snack_GetSound(interp, sectPtr->newSoundName)) == NULL) {
805 return TCL_ERROR;
806 }
807 if (s->storeType == SOUND_IN_CHANNEL) {
808 Tcl_AppendResult(interp, sectPtr->newSoundName,
809 " can not be linked to a channel", (char *) NULL);
810 return TCL_ERROR;
811 }
812 if (s->storeType == SOUND_IN_FILE) {
813 s->itemRefCnt++;
814 }
815 sectPtr->sound = s;
816 if (sectPtr->soundName == NULL) {
817 sectPtr->soundName = ckalloc(strlen(sectPtr->newSoundName)+1);
818 strcpy(sectPtr->soundName, sectPtr->newSoundName);
819 }
820 if (strcmp(sectPtr->soundName, sectPtr->newSoundName) != 0) {
821 Sound *t = Snack_GetSound(interp, sectPtr->soundName);
822 ckfree(sectPtr->soundName);
823 sectPtr->soundName = ckalloc(strlen(sectPtr->newSoundName)+1);
824 strcpy(sectPtr->soundName, sectPtr->newSoundName);
825 sectPtr->nPoints = 0;
826 sectPtr->ssmp = 0;
827 sectPtr->esmp = -1;
828 Snack_RemoveCallback(t, sectPtr->id);
829 sectPtr->id = 0;
830 }
831 if (!sectPtr->id)
832 sectPtr->id = Snack_AddCallback(s, UpdateSection, (int *)sectPtr);
833
834 sectPtr->si.blocks = s->blocks;
835 sectPtr->si.BufPos = s->length;
836 sectPtr->si.samprate = s->samprate;
837 sectPtr->si.encoding = s->encoding;
838 sectPtr->si.nchannels = s->nchannels;
839 sectPtr->si.storeType = s->storeType;
840 doCompute = 1;
841 }
842 }
843 sectPtr->esmp = sectPtr->endSmp;
844
845 if (sectPtr->endSmp < 0)
846 sectPtr->esmp = sectPtr->si.BufPos - 1;
847
848 if (sectPtr->endSmp > sectPtr->si.BufPos - 1)
849 sectPtr->esmp = sectPtr->si.BufPos - 1;
850
851 if (sectPtr->startSmp > sectPtr->endSmp && sectPtr->endSmp >= 0)
852 sectPtr->startSmp = sectPtr->endSmp;
853
854 if (sectPtr->startSmp < 0)
855 sectPtr->startSmp = 0;
856
857 sectPtr->ssmp = sectPtr->startSmp;
858
859 if (sectPtr->ssmp > sectPtr->esmp)
860 sectPtr->ssmp = sectPtr->esmp;
861
862 if (sectPtr->ssmp > sectPtr->esmp - sectPtr->si.fftlen) {
863 sectPtr->esmp = sectPtr->ssmp + sectPtr->si.fftlen;
864 if (sectPtr->esmp > sectPtr->si.BufPos - 1) {
865 sectPtr->esmp = sectPtr->si.BufPos - 1;
866 sectPtr->ssmp = sectPtr->esmp - sectPtr->si.fftlen;
867 if (sectPtr->ssmp < 0) {
868 sectPtr->ssmp = 0;
869 }
870 }
871 }
872
873 if (OptSpecified(OPTION_WINLEN))
874 doCompute = 1;
875
876 if (OptSpecified(OPTION_FFTLEN)) {
877 doCompute = 1;
878 }
879
880 sectPtr->si.preemph = (float) sectPtr->preemph;
881
882 if (OptSpecified(OPTION_SKIP)) {
883 doCompute = 1;
884 }
885
886 if (OptSpecified(OPTION_START)) {
887 doCompute = 1;
888 }
889
890 if (OptSpecified(OPTION_END)) {
891 doCompute = 1;
892 }
893
894 if (sectPtr->topFrequency <= 0.0) {
895 sectPtr->si.topfrequency = sectPtr->si.samprate / 2.0;
896 } else if (sectPtr->topFrequency > sectPtr->si.samprate / 2.0) {
897 sectPtr->si.topfrequency = sectPtr->si.samprate / 2.0;
898 } else {
899 sectPtr->si.topfrequency = sectPtr->topFrequency;
900 }
901
902 if (OptSpecified(OPTION_CHANNEL)) {
903 if (GetChannel(interp, sectPtr->channelstr, sectPtr->si.nchannels,
904 §Ptr->si.channelSet) != TCL_OK) {
905 return TCL_ERROR;
906 }
907 doCompute = 1;
908 }
909 sectPtr->si.channel = sectPtr->si.channelSet;
910 if (sectPtr->si.nchannels == 1) {
911 sectPtr->si.channel = 0;
912 }
913
914 if (OptSpecified(OPTION_ANALYSISTYPE)) {
915 int len = strlen(sectPtr->analysisTypeStr);
916
917 if (strncasecmp(sectPtr->analysisTypeStr, "lpc", len) == 0) {
918 sectPtr->type = 1;
919 } else if (strncasecmp(sectPtr->analysisTypeStr, "fft", len) == 0) {
920 sectPtr->type = 0;
921 } else {
922 Tcl_AppendResult(interp, "-type should be FFT or LPC", (char *) NULL);
923 return TCL_ERROR;
924 }
925 doCompute = 1;
926 }
927 if (OptSpecified(OPTION_LPCORDER)) {
928 doCompute = 1;
929 }
930 if (OptSpecified(OPTION_WINTYPE)) {
931 if (GetWindowType(interp, sectPtr->windowTypeStr,
932 §Ptr->si.windowTypeSet)
933 != TCL_OK) {
934 return TCL_ERROR;
935 }
936 doCompute = 1;
937 }
938 sectPtr->si.windowType = sectPtr->si.windowTypeSet;
939
940 if (doCompute) {
941 sectPtr->nPoints = sectPtr->si.fftlen / 2;
942 sectPtr->si.RestartPos = sectPtr->ssmp;
943 ComputeSection((Tk_Item *)sectPtr);
944 }
945
946 if (sectPtr->height <= 2) sectPtr->height = 0;
947
948 if (sectPtr->fg == NULL) {
949 newGC = None;
950 } else {
951 gcValues.foreground = sectPtr->fg->pixel;
952 gcValues.line_width = 1;
953 mask = GCForeground|GCLineWidth;
954 if (sectPtr->fillStipple != None) {
955 gcValues.stipple = sectPtr->fillStipple;
956 gcValues.fill_style = FillStippled;
957 mask |= GCStipple|GCFillStyle;
958 }
959 newGC = Tk_GetGC(tkwin, mask, &gcValues);
960 gcValues.line_width = 0;
961 }
962 if (sectPtr->gc != None) {
963 Tk_FreeGC(Tk_Display(tkwin), sectPtr->gc);
964 }
965 sectPtr->gc = newGC;
966
967 ComputeSectionBbox(canvas, sectPtr);
968
969 if (ComputeSectionCoords(itemPtr) != TCL_OK) {
970 return TCL_ERROR;
971 }
972
973 for (i = 0; configSpecs[i].type != TK_CONFIG_END; i++) {
974 configSpecs[i].specFlags &= ~TK_CONFIG_OPTION_SPECIFIED;
975 }
976
977 if (sectPtr->debug) Snack_WriteLog("Exit ConfigureSection\n");
978
979 return TCL_OK;
980 }
981
982 static void
DeleteSection(Tk_Canvas canvas,Tk_Item * itemPtr,Display * display)983 DeleteSection(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display)
984 {
985 SectionItem *sectPtr = (SectionItem *) itemPtr;
986
987 if ((sectPtr->id) &&
988 (Snack_GetSound(sectPtr->interp, sectPtr->soundName) != NULL)) {
989 Snack_RemoveCallback(sectPtr->sound, sectPtr->id);
990 }
991
992 if (sectPtr->soundName != NULL) ckfree(sectPtr->soundName);
993
994 if (sectPtr->coords != NULL) ckfree((char *) sectPtr->coords);
995
996 if (sectPtr->si.hamwin != NULL) ckfree((char *)sectPtr->si.hamwin);
997
998 if (sectPtr->xfft != NULL) ckfree((char *)sectPtr->xfft);
999
1000 if (sectPtr->ffts != NULL) ckfree((char *)sectPtr->ffts);
1001
1002 if (sectPtr->fg != NULL) Tk_FreeColor(sectPtr->fg);
1003
1004 if (sectPtr->fillStipple != None) Tk_FreeBitmap(display, sectPtr->fillStipple);
1005
1006 if (sectPtr->gc != None) Tk_FreeGC(display, sectPtr->gc);
1007
1008 if (sectPtr->sound != NULL) {
1009 if (sectPtr->sound->storeType == SOUND_IN_FILE) {
1010 sectPtr->sound->itemRefCnt--;
1011 }
1012 }
1013 }
1014
1015 static void
ComputeSectionBbox(Tk_Canvas canvas,SectionItem * sectPtr)1016 ComputeSectionBbox(Tk_Canvas canvas, SectionItem *sectPtr)
1017 {
1018 int width = sectPtr->width;
1019 int height = sectPtr->height;
1020 int x = (int) (sectPtr->x + ((sectPtr->x >= 0) ? 0.5 : - 0.5));
1021 int y = (int) (sectPtr->y + ((sectPtr->y >= 0) ? 0.5 : - 0.5));
1022
1023 switch (sectPtr->anchor) {
1024 case TK_ANCHOR_N:
1025 x -= width/2;
1026 break;
1027 case TK_ANCHOR_NE:
1028 x -= width;
1029 break;
1030 case TK_ANCHOR_E:
1031 x -= width;
1032 y -= height/2;
1033 break;
1034 case TK_ANCHOR_SE:
1035 x -= width;
1036 y -= height;
1037 break;
1038 case TK_ANCHOR_S:
1039 x -= width/2;
1040 y -= height;
1041 break;
1042 case TK_ANCHOR_SW:
1043 y -= height;
1044 break;
1045 case TK_ANCHOR_W:
1046 y -= height/2;
1047 break;
1048 case TK_ANCHOR_NW:
1049 break;
1050 case TK_ANCHOR_CENTER:
1051 x -= width/2;
1052 y -= height/2;
1053 break;
1054 }
1055
1056 sectPtr->header.x1 = x;
1057 sectPtr->header.y1 = y;
1058 sectPtr->header.x2 = x + width;
1059 sectPtr->header.y2 = y + height;
1060 }
1061
1062 static void
DisplaySection(Tk_Canvas canvas,Tk_Item * itemPtr,Display * display,Drawable drawable,int x,int y,int width,int height)1063 DisplaySection(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display,
1064 Drawable drawable, int x, int y, int width, int height)
1065 {
1066 SectionItem *sectPtr = (SectionItem *) itemPtr;
1067 double *coords = sectPtr->coords;
1068 int i, nPoints = sectPtr->nPoints;
1069 XPoint *wpts = (XPoint *) ckalloc((unsigned)(nPoints * sizeof(XPoint)));
1070 XPoint *p = wpts;
1071 int xo = sectPtr->header.x1;
1072 int yo = sectPtr->header.y1;
1073
1074 if (sectPtr->debug) Snack_WriteLogInt("Enter DisplaySection", nPoints);
1075
1076 if (sectPtr->gc == None) return;
1077
1078 if (sectPtr->fillStipple != None)
1079 Tk_CanvasSetStippleOrigin(canvas, sectPtr->gc);
1080
1081 for (i = 0; i < sectPtr->nPoints; i++) {
1082 Tk_CanvasDrawableCoords(canvas, xo + coords[0], yo + coords[1],
1083 &p->x, &p->y);
1084 coords += 2;
1085 p++;
1086 }
1087
1088 XDrawLines(display, drawable, sectPtr->gc, wpts, nPoints,
1089 CoordModeOrigin);
1090
1091 if (sectPtr->frame) {
1092 Tk_CanvasDrawableCoords(canvas, (double) xo, (double) yo,
1093 §Ptr->fpts[0].x, §Ptr->fpts[0].y);
1094 Tk_CanvasDrawableCoords(canvas, (double) (xo + sectPtr->width - 1),
1095 (double) yo,
1096 §Ptr->fpts[1].x, §Ptr->fpts[1].y);
1097 Tk_CanvasDrawableCoords(canvas, (double) (xo + sectPtr->width - 1),
1098 (double) (yo + sectPtr->height - 1),
1099 §Ptr->fpts[2].x, §Ptr->fpts[2].y);
1100 Tk_CanvasDrawableCoords(canvas, (double) xo,
1101 (double) (yo + sectPtr->height - 1),
1102 §Ptr->fpts[3].x, §Ptr->fpts[3].y);
1103 Tk_CanvasDrawableCoords(canvas, (double) xo, (double) yo,
1104 §Ptr->fpts[4].x, §Ptr->fpts[4].y);
1105 XDrawLines(display, drawable, sectPtr->gc, sectPtr->fpts, 5, CoordModeOrigin);
1106 }
1107
1108 ckfree((char *) wpts);
1109
1110 if (sectPtr->debug) Snack_WriteLog("Exit DisplaySection\n");
1111 }
1112
1113 static double
SectionToPoint(Tk_Canvas canvas,Tk_Item * itemPtr,double * coords)1114 SectionToPoint(Tk_Canvas canvas, Tk_Item *itemPtr, double *coords)
1115 {
1116 SectionItem *sectPtr = (SectionItem *) itemPtr;
1117 double dx = 0.0, dy = 0.0;
1118 double x1 = sectPtr->header.x1;
1119 double y1 = sectPtr->header.y1;
1120 double x2 = sectPtr->header.x2;
1121 double y2 = sectPtr->header.y2;
1122
1123 if (coords[0] < x1)
1124 dx = x1 - coords[0];
1125 else if (coords[0] > x2)
1126 dx = coords[0] - x2;
1127 else
1128 dx = 0;
1129
1130 if (coords[1] < y1)
1131 dy = y1 - coords[1];
1132 else if (coords[1] > y2)
1133 dy = coords[1] - y2;
1134 else
1135 dy = 0;
1136
1137 return hypot(dx, dy);
1138 }
1139
1140 static int
SectionToArea(Tk_Canvas canvas,Tk_Item * itemPtr,double * rectPtr)1141 SectionToArea(Tk_Canvas canvas, Tk_Item *itemPtr, double *rectPtr)
1142 {
1143 SectionItem *sectPtr = (SectionItem *) itemPtr;
1144
1145 if ((rectPtr[2] <= sectPtr->header.x1) ||
1146 (rectPtr[0] >= sectPtr->header.x2) ||
1147 (rectPtr[3] <= sectPtr->header.y1) ||
1148 (rectPtr[1] >= sectPtr->header.y2))
1149 return -1;
1150
1151 if ((rectPtr[0] <= sectPtr->header.x1) &&
1152 (rectPtr[1] <= sectPtr->header.y1) &&
1153 (rectPtr[2] >= sectPtr->header.x2) &&
1154 (rectPtr[3] >= sectPtr->header.y2))
1155 return 1;
1156
1157 return 0;
1158 }
1159
1160 static void
ScaleSection(Tk_Canvas canvas,Tk_Item * itemPtr,double ox,double oy,double sx,double sy)1161 ScaleSection(Tk_Canvas canvas, Tk_Item *itemPtr, double ox, double oy,
1162 double sx, double sy)
1163 {
1164 SectionItem *sectPtr = (SectionItem *) itemPtr;
1165 double *coords = sectPtr->coords;
1166 int i;
1167
1168 for (i = 0; i < sectPtr->nPoints; i++) {
1169 coords[0] = ox + sx * (coords[0] - ox);
1170 coords[1] = oy + sy * (coords[1] - oy);
1171 coords += 2;
1172 }
1173 sectPtr->width = (int) (sx * sectPtr->width);
1174 sectPtr->height = (int) (sy * sectPtr->height);
1175
1176 ComputeSectionBbox(canvas, sectPtr);
1177 }
1178
1179 static void
TranslateSection(Tk_Canvas canvas,Tk_Item * itemPtr,double dx,double dy)1180 TranslateSection(Tk_Canvas canvas, Tk_Item *itemPtr, double dx, double dy)
1181 {
1182 SectionItem *sectPtr = (SectionItem *) itemPtr;
1183
1184 sectPtr->x += dx;
1185 sectPtr->y += dy;
1186 ComputeSectionBbox(canvas, sectPtr);
1187 }
1188
1189 static int
SectionToPS(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,int prepass)1190 SectionToPS(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr, int prepass)
1191 {
1192 SectionItem *sectPtr = (SectionItem *) itemPtr;
1193 double *coords = sectPtr->coords;
1194 int nPoints = sectPtr->nPoints;
1195 char buffer[100];
1196 int xo = sectPtr->header.x1;
1197 int yo = sectPtr->header.y1;
1198
1199 if (sectPtr->fg == NULL) {
1200 return TCL_OK;
1201 }
1202
1203 Tcl_AppendResult(interp, "%% SECT BEGIN\n", (char *) NULL);
1204
1205 sprintf(buffer, "%.15g %.15g moveto\n", coords[0] + xo,
1206 Tk_CanvasPsY(canvas, (double) (coords[1] + yo)));
1207 Tcl_AppendResult(interp, buffer, (char *) NULL);
1208 coords += 2;
1209 for (nPoints--; nPoints > 0; nPoints--) {
1210 sprintf(buffer, "%.15g %.15g lineto\n", coords[0] + xo,
1211 Tk_CanvasPsY(canvas, (double) (coords[1] + yo)));
1212 Tcl_AppendResult(interp, buffer, (char *) NULL);
1213 coords += 2;
1214 }
1215
1216 if (sectPtr->frame) {
1217 sprintf(buffer, "%.15g %.15g moveto\n", (double) xo, Tk_CanvasPsY(canvas, (double) yo));
1218 Tcl_AppendResult(interp, buffer, (char *) NULL);
1219
1220 sprintf(buffer, "%.15g %.15g lineto\n", (double) xo + sectPtr->width - 1,
1221 Tk_CanvasPsY(canvas, (double) yo));
1222 Tcl_AppendResult(interp, buffer, (char *) NULL);
1223
1224 sprintf(buffer, "%.15g %.15g lineto\n", (double) xo + sectPtr->width - 1,
1225 Tk_CanvasPsY(canvas, (double) (yo + sectPtr->height - 1)));
1226 Tcl_AppendResult(interp, buffer, (char *) NULL);
1227
1228 sprintf(buffer, "%.15g %.15g lineto\n", (double) xo,
1229 Tk_CanvasPsY(canvas, (double) (yo + sectPtr->height - 1)));
1230 Tcl_AppendResult(interp, buffer, (char *) NULL);
1231
1232 sprintf(buffer, "%.15g %.15g lineto\n", (double) xo,
1233 Tk_CanvasPsY(canvas, (double) yo));
1234 Tcl_AppendResult(interp, buffer, (char *) NULL);
1235 }
1236
1237 Tcl_AppendResult(interp, "1 setlinewidth\n", (char *) NULL);
1238 Tcl_AppendResult(interp, "0 setlinecap\n0 setlinejoin\n", (char *) NULL);
1239 if (Tk_CanvasPsColor(interp, canvas, sectPtr->fg) != TCL_OK) {
1240 return TCL_ERROR;
1241 };
1242 if (sectPtr->fillStipple != None) {
1243 Tcl_AppendResult(interp, "StrokeClip ", (char *) NULL);
1244 if (Tk_CanvasPsStipple(interp, canvas, sectPtr->fillStipple) != TCL_OK) {
1245 return TCL_ERROR;
1246 }
1247 } else {
1248 Tcl_AppendResult(interp, "stroke\n", (char *) NULL);
1249 }
1250
1251 Tcl_AppendResult(interp, "%% SECT END\n", (char *) NULL);
1252
1253 return TCL_OK;
1254 }
1255