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 41typedef 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 81Tk_CustomOption sectTagsOption = { (Tk_OptionParseProc *) NULL, 82 (Tk_OptionPrintProc *) NULL, 83 (ClientData) NULL }; 84 85typedef 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 109static 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 186static void ComputeSectionBbox(Tk_Canvas canvas, SectionItem *sectPtr); 187 188static int ComputeSectionCoords(Tk_Item *itemPtr); 189 190static int ConfigureSection(Tcl_Interp *interp, Tk_Canvas canvas, 191 Tk_Item *itemPtr, int argc, 192 char **argv, int flags); 193 194static int CreateSection(Tcl_Interp *interp, Tk_Canvas canvas, 195 struct Tk_Item *itemPtr, int argc, char **argv); 196 197static void DeleteSection(Tk_Canvas canvas, Tk_Item *itemPtr, 198 Display *display); 199 200static void DisplaySection(Tk_Canvas canvas, Tk_Item *itemPtr, 201 Display *display, Drawable dst, 202 int x, int y, int width, int height); 203 204static void ScaleSection(Tk_Canvas canvas, Tk_Item *itemPtr, 205 double originX, double originY, 206 double scaleX, double scaleY); 207 208static int SectionCoords(Tcl_Interp *interp, Tk_Canvas canvas, 209 Tk_Item *itemPtr, int argc, char **argv); 210 211static int SectionToArea(Tk_Canvas canvas, Tk_Item *itemPtr, 212 double *rectPtr); 213 214static double SectionToPoint(Tk_Canvas canvas, Tk_Item *itemPtr, 215 double *coords); 216 217static int SectionToPS(Tcl_Interp *interp, Tk_Canvas canvas, 218 Tk_Item *itemPtr, int prepass); 219 220static void TranslateSection(Tk_Canvas canvas, Tk_Item *itemPtr, 221 double deltaX, double deltaY); 222 223/* 224 * Section item type 225 */ 226 227Tk_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 250static int 251CreateSection(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 340static int 341SectionCoords(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 368static int 369ComputeSectionCoords(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 403void 404GetFloatMonoSigSect(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 465extern void Snack_PowerSpectrum(float *z); 466 467void 468ComputeSection(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 633static void 634UpdateSection(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 731static int 732ConfigureSection(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 982static void 983DeleteSection(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 1015static void 1016ComputeSectionBbox(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 1062static void 1063DisplaySection(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 1113static double 1114SectionToPoint(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 1140static int 1141SectionToArea(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 1160static void 1161ScaleSection(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 1179static void 1180TranslateSection(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 1189static int 1190SectionToPS(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