1/* 2 * Copyright (C) 2001-2002 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 <stdlib.h> 23#include <stdio.h> 24#include <signal.h> 25#include <math.h> 26#include <string.h> 27#include "tcl.h" 28#include "snack.h" 29 30Snack_FilterType *snackFilterTypes = NULL; 31 32Tcl_HashTable *filterHashTable; 33 34extern float floatBuffer[]; 35 36int 37filterSndCmd(ClientData clientData, Tcl_Interp *interp, int objc, 38 Tcl_Obj *CONST objv[]) 39{ 40 register Sound *s = (Sound *) clientData; 41 int arg, i, inSize, outSize, drain = 1, startpos = 0, endpos = -1, len; 42 int fs, fi, es, ei; 43 char *string = NULL; 44 Tcl_HashEntry *hPtr; 45 Snack_Filter f; 46 Snack_StreamInfo si; 47 static CONST84 char *subOptionStrings[] = { 48 "-start", "-end", "-continuedrain", "-progress", NULL 49 }; 50 enum subOptions { 51 START, END, DRAIN, PROGRESS 52 }; 53 54 if (s->storeType != SOUND_IN_MEMORY) { 55 Tcl_AppendResult(interp, "filter only works with in-memory sounds", 56 (char *) NULL); 57 return TCL_ERROR; 58 } 59 if (objc < 3) { 60 Tcl_WrongNumArgs(interp, 1, objv, "filter filterCmd"); 61 return TCL_ERROR; 62 } 63 64 if (s->cmdPtr != NULL) { 65 Tcl_DecrRefCount(s->cmdPtr); 66 s->cmdPtr = NULL; 67 } 68 69 for (arg = 3; arg < objc; arg += 2) { 70 int index; 71 72 if (Tcl_GetIndexFromObj(interp, objv[arg], subOptionStrings, 73 "option", 0, &index) != TCL_OK) { 74 return TCL_ERROR; 75 } 76 77 if (arg + 1 == objc) { 78 Tcl_AppendResult(interp, "No argument given for ", 79 subOptionStrings[index], " option", (char *) NULL); 80 return TCL_ERROR; 81 } 82 83 switch ((enum subOptions) index) { 84 case START: 85 { 86 if (Tcl_GetIntFromObj(interp, objv[arg+1], &startpos) != TCL_OK) 87 return TCL_ERROR; 88 break; 89 } 90 case END: 91 { 92 if (Tcl_GetIntFromObj(interp, objv[arg+1], &endpos) != TCL_OK) 93 return TCL_ERROR; 94 break; 95 } 96 case DRAIN: 97 { 98 if (Tcl_GetIntFromObj(interp, objv[arg+1], &drain) != TCL_OK) 99 return TCL_ERROR; 100 break; 101 } 102 case PROGRESS: 103 { 104 char *str = Tcl_GetStringFromObj(objv[arg+1], NULL); 105 106 if (strlen(str) > 0) { 107 Tcl_IncrRefCount(objv[arg+1]); 108 s->cmdPtr = objv[arg+1]; 109 } 110 break; 111 } 112 } 113 } 114 115 if (startpos < 0) startpos = 0; 116 if (endpos > (s->length - 1) || endpos == -1) 117 endpos = s->length - 1; 118 if (startpos > endpos && endpos != -1) return TCL_OK; 119 len = endpos - startpos + 1; 120 121 string = Tcl_GetStringFromObj(objv[2], NULL); 122 123 hPtr = Tcl_FindHashEntry(filterHashTable, string); 124 if (hPtr != NULL) { 125 f = (Snack_Filter) Tcl_GetHashValue(hPtr); 126 } else { 127 Tcl_AppendResult(interp, "No such filter: ", string, (char *) NULL); 128 return TCL_ERROR; 129 } 130 Snack_StopSound(s, interp); 131 132 si = (Snack_StreamInfo) ckalloc(sizeof(SnackStreamInfo)); 133 134 si->streamWidth = s->nchannels; 135 si->outWidth = s->nchannels; 136 si->rate = s->samprate; 137 138 Snack_ProgressCallback(s->cmdPtr, interp, "Filtering sound", 0.0); 139 140 (f->startProc)(f, si); 141 142 len *= s->nchannels; 143 fi = (startpos * s->nchannels) >> FEXP; 144 fs = (startpos * s->nchannels) - (fi << FEXP); 145 ei = (endpos * s->nchannels) >> FEXP; 146 es = (endpos * s->nchannels) - (ei << FEXP); 147 148 if (len > 0) { 149 for (i = fi; i <= ei; i++) { 150 int res; 151 152 if (i > fi) fs = 0; 153 if (i < ei) { 154 inSize = min(len, (FBLKSIZE - fs) / s->nchannels); 155 outSize = min(len, (FBLKSIZE - fs) / s->nchannels); 156 } else { 157 inSize = (es - fs) / s->nchannels + 1; 158 outSize = (es - fs) / s->nchannels + 1; 159 } 160 161 (f->flowProc)(f, si, &s->blocks[i][fs], &s->blocks[i][fs], 162 &inSize, &outSize); 163 res = Snack_ProgressCallback(s->cmdPtr, interp, "Filtering sound", 164 (float) (i - fi) / (ei - fi + 1)); 165 if (res != TCL_OK) { 166 return TCL_ERROR; 167 } 168 } 169 } 170 while (drain) { 171 int j; 172 173 inSize = 0; 174 outSize = PBSIZE; 175 (f->flowProc)(f, si, floatBuffer, floatBuffer, &inSize, &outSize); 176 177 if (endpos + outSize + 1 > s->length) { 178 if (Snack_ResizeSoundStorage(s, endpos + outSize + 1) != TCL_OK) { 179 return TCL_ERROR; 180 } 181 for (i = s->length; i < endpos + outSize + 1; i++) { 182 FSAMPLE(s, i) = 0.0f; 183 } 184 } 185 186 for (i = endpos + 1, j = 0; j < min(outSize, PBSIZE); i++, j++) { 187 FSAMPLE(s, i) = FSAMPLE(s, i) + floatBuffer[j]; 188 } 189 if (endpos + outSize + 1 > s->length) { 190 /*Snack_PutSoundData(s, i, &floatBuffer[j], (endpos+outSize+1)-s->length);*/ 191 s->length = endpos + outSize + 1; 192 } 193 drain = 0; 194 } 195 196 Snack_ProgressCallback(s->cmdPtr, interp, "Filtering sound", 1.0); 197 198 ckfree((char *) si); 199 200 Snack_UpdateExtremes(s, 0, s->length, SNACK_NEW_SOUND); 201 Snack_ExecCallbacks(s, SNACK_NEW_SOUND); 202 203 return TCL_OK; 204} 205 206int 207filterObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, 208 Tcl_Obj *CONST objv[]) 209{ 210 Snack_Filter f = (Snack_Filter) clientData; 211 int length = 0; 212 char *string = NULL; 213 Tcl_HashEntry *hPtr; 214 215 if (objc < 2) { 216 Tcl_WrongNumArgs(interp, 1, objv, "cmd"); 217 return TCL_ERROR; 218 } 219 string = Tcl_GetStringFromObj(objv[1], &length); 220 221 if (strncmp("configure", string, length) == 0) { 222 if ((f->configProc)(f, interp, objc-2, &objv[2]) != TCL_OK) { 223 return TCL_ERROR; 224 } 225 } else if (strncmp("destroy", string, length) == 0) { 226 string = Tcl_GetStringFromObj(objv[0], &length); 227 hPtr = Tcl_FindHashEntry(filterHashTable, string); 228 if (hPtr != NULL) { 229 Tcl_DeleteCommand(interp, string); 230 Tcl_DeleteHashEntry(hPtr); 231 } 232 if (f->freeProc != NULL) { 233 (f->freeProc)(f); 234 } 235 } else { 236 Tcl_AppendResult(interp, "bad option \"", string, "\": must be configure, " 237 "destroy or ...", (char *) NULL); 238 return TCL_ERROR; 239 } 240 241 return TCL_OK; 242} 243 244int 245Snack_FilterCmd(ClientData cdata, Tcl_Interp *interp, int objc, 246 Tcl_Obj *CONST objv[]) 247{ 248 Snack_FilterType *sf; 249 Snack_Filter new = NULL; 250 int flag; 251 static int id = 0; 252 static char ids[80]; 253 char *name; 254 Tcl_HashTable *hTab = (Tcl_HashTable *) cdata; 255 Tcl_HashEntry *hPtr; 256 int length = 0; 257 char *string = NULL; 258 259 if (objc < 2) { 260 Tcl_WrongNumArgs(interp, 1, objv, "type"); 261 return TCL_ERROR; 262 } 263 string = Tcl_GetStringFromObj(objv[1], &length); 264 265 do { 266 sprintf(ids, "%s%d", string, ++id); 267 } while (Tcl_FindHashEntry(hTab, ids) != NULL); 268 name = ids; 269 270 hPtr = Tcl_FindHashEntry(hTab, name); 271 if (hPtr != NULL) { 272 Tcl_DeleteCommand(interp, name); 273 } 274 275 for (sf = snackFilterTypes; sf != NULL; sf = sf->nextPtr) { 276 if (strcmp(string, sf->name) == 0) { 277 if ((new = (sf->createProc)(interp, objc-2, &objv[2])) == (Snack_Filter) NULL) return TCL_ERROR; 278 break; 279 } 280 } 281 if (sf == NULL) { 282 Tcl_AppendResult(interp, "No such filter type: ", string, NULL); 283 return TCL_ERROR; 284 } 285 new->configProc = sf->configProc; 286 new->startProc = sf->startProc; 287 new->flowProc = sf->flowProc; 288 new->freeProc = sf->freeProc; 289 new->si = NULL; 290 new->prev = NULL; 291 new->next = NULL; 292 293 hPtr = Tcl_CreateHashEntry(hTab, name, &flag); 294 Tcl_SetHashValue(hPtr, (ClientData) new); 295 296 Tcl_CreateObjCommand(interp, name, filterObjCmd, (ClientData) new, 297 (Tcl_CmdDeleteProc *) NULL); 298 299 Tcl_SetObjResult(interp, Tcl_NewStringObj(name, -1)); 300 301 filterHashTable = hTab; 302 303 return TCL_OK; 304} 305 306void 307Snack_FilterDeleteCmd(ClientData clientData) 308{ 309} 310 311/* 312typedef struct lowpassFilter { 313 configProc *configProc; 314 flowProc *flowProc; 315 Snack_Filter prev, next; 316 double dataRatio; 317 double center; 318 double last; 319} lowpassFilter; 320 321typedef struct lowpassFilter *lowpassFilter_t; 322 323Snack_Filter 324lowpassCreateProc(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) 325{ 326 gainFilter_t sf; 327 328 sf = (gainFilter_t) ckalloc(sizeof(gainFilter)); 329 330 if (gainConfigProc((Snack_Filter) sf, interp, objc, objv) != TCL_OK) { 331 ckfree((char *) sf); 332 return (Snack_Filter) NULL; 333 } 334 335 return (Snack_Filter) sf; 336} 337 338int 339lowpassConfigProc(Snack_Filter f, Tcl_Interp *interp, int objc, 340 Tcl_Obj *CONST objv[]) 341{ 342 lowpassFilter_t lf = (lowpassFilter_t) f; 343 int arg, arg1 = 0; 344 double val; 345 static char *optionStrings[] = { 346 "-factor", NULL 347 }; 348 enum options { 349 FACTOR 350 }; 351 352 if (objc != 0 && objc != 2) { 353 Tcl_WrongNumArgs(interp, 0, objv, "configure -factor value"); 354 return TCL_ERROR; 355 } 356 357 for (arg = arg1; arg < objc; arg += 2) { 358 int index; 359 360 if (Tcl_GetIndexFromObj(interp, objv[arg], optionStrings, "option", 0, 361 &index) != TCL_OK) { 362 return TCL_ERROR; 363 } 364 365 switch ((enum options) index) { 366 case FACTOR: 367 { 368 if (Tcl_GetDoubleFromObj(interp, objv[arg+1], &val) != TCL_OK) { 369 return TCL_ERROR; 370 } 371 break; 372 } 373 } 374 } 375 376 lf->center= val; 377 lf->last = 0.0; 378 lf->prev = (Snack_Filter) NULL; 379 lf->next = (Snack_Filter) NULL; 380 381 return TCL_OK; 382} 383 384int 385lowpassFlowProc(Snack_Filter f, Snack_StreamInfo si, float *in, float *out, 386 int frames) 387{ 388 lowpassFilter_t lpf = (lowpassFilter_t) f; 389 int i; 390 double insmp = 0.0, outsmp, last; 391 double a = 6.28318530718 * lpf->center / 16000.0; 392 double b = exp(-a / 16000.0); 393 394 last = lpf->last; 395 for (i = 0; i < frames; i++) { 396 insmp = (double) in[i]; 397 outsmp = insmp * a + last * b; 398 last = insmp; 399 out[i] = (float) (0.4 * outsmp); 400 } 401 lpf->last = last; 402 403 return(frames); 404} 405 406Snack_FilterType snackLowpassType = { 407 "lowpass", 408 lowpassCreateProc, 409 lowpassConfigProc, 410 lowpassFlowProc, 411 NULL, 412 (Snack_FilterType *) NULL 413}; 414*/ 415 416struct mapFilter { 417 configProc *configProc; 418 startProc *startProc; 419 flowProc *flowProc; 420 freeProc *freeProc; 421 Tcl_Interp *interp; 422 Snack_Filter prev, next; 423 Snack_StreamInfo si; 424 double dataRatio; 425 int reserved[4]; 426 /* private members */ 427 int nm; 428 float *m; 429 int ns; 430 float *s; 431 int width; 432} mapFilter; 433 434typedef struct mapFilter *mapFilter_t; 435 436int 437mapConfigProc(Snack_Filter f, Tcl_Interp *interp, int objc, 438 Tcl_Obj *CONST objv[]) 439{ 440 mapFilter_t mf = (mapFilter_t) f; 441 int i; 442 double val; 443 444 if (objc > mf->nm) { 445 ckfree((char *) mf->m); 446 mf->m = (float *) ckalloc(sizeof(float) * objc); 447 mf->nm = objc; 448 } 449 for (i = 0; i < objc; i++) { 450 if (Tcl_GetDoubleFromObj(interp, objv[i], &val) != TCL_OK) { 451 return TCL_ERROR; 452 } 453 mf->m[i] = (float) val; 454 } 455 456 if (objc == 1 && mf->nm > 1 && mf->width > 0) { 457 /* Special case, duplicate m[0] on the diagonal */ 458 for (i = 0; i < mf->nm; i = i + mf->width + 1) { 459 mf->m[i] = mf->m[0]; 460 } 461 } 462 463 return TCL_OK; 464} 465 466Snack_Filter 467mapCreateProc(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) 468{ 469 mapFilter_t mf; 470 471 mf = (mapFilter_t) ckalloc(sizeof(mapFilter)); 472 mf->nm = objc; 473 if ((mf->m = (float *) ckalloc(sizeof(float) * objc)) == NULL) { 474 return (Snack_Filter) NULL; 475 } 476 mf->ns = 0; 477 mf->s = NULL; 478 mf->width = 0; 479 480 if (mapConfigProc((Snack_Filter) mf, interp, objc, objv) != TCL_OK) { 481 ckfree((char *) mf->m); 482 ckfree((char *) mf); 483 return (Snack_Filter) NULL; 484 } 485 486 return (Snack_Filter) mf; 487} 488 489int 490mapStartProc(Snack_Filter f, Snack_StreamInfo si) 491{ 492 mapFilter_t mf = (mapFilter_t) f; 493 int n; 494 495 if (mf->nm < si->outWidth * si->streamWidth) { 496 int needed = si->outWidth * si->streamWidth; 497 float *tmp = (float *) ckalloc(sizeof(float) * needed); 498 499 for (n = 0; n < mf->nm; n++) { 500 tmp[n] = mf->m[n]; 501 } 502 for (; n < needed; n++) { 503 tmp[n] = 0.0f; 504 } 505 if (mf->nm == 1) { /* Special case, duplicate m[0] on the diagonal */ 506 for (n = si->streamWidth + 1; n < needed; n = n + si->streamWidth + 1) { 507 tmp[n] = mf->m[0]; 508 } 509 } 510 ckfree((char *) mf->m); 511 mf->nm = needed; 512 mf->m = tmp; 513 } 514 if (mf->ns < si->streamWidth) { 515 mf->ns = si->streamWidth; 516 if (mf->s != NULL) { 517 ckfree((char *) mf->s); 518 } 519 mf->s = (float *) ckalloc(sizeof(float) * mf->ns); 520 } 521 522 /* Stream width can change dynamically, remember what was allocated above */ 523 524 mf->width = si->streamWidth; 525 526 return TCL_OK; 527} 528 529int 530mapFlowProc(Snack_Filter f, Snack_StreamInfo si, float *in, float *out, 531 int *inFrames, int *outFrames) 532{ 533 mapFilter_t mf = (mapFilter_t) f; 534 int i = 0, fr, wi, n, j, k, jstart; 535 float tmp; 536 537 for (fr = 0; fr < *inFrames; fr++) { 538 n = 0; 539 jstart = i; 540 for (wi = 0; wi < si->outWidth; wi++) { 541 tmp = 0.0f; 542 j = jstart; 543 for (k = 0; k < mf->width; k++) { 544 tmp += (in[j++] * mf->m[n++]); 545 } 546 mf->s[wi] = tmp; 547 } 548 for (wi = 0; wi < si->outWidth; wi++) { 549 out[i++] = mf->s[wi]; 550 } 551 i += (si->streamWidth - si->outWidth); 552 } 553 554 *outFrames = *inFrames; 555 556 return TCL_OK; 557} 558 559void 560mapFreeProc(Snack_Filter f) 561{ 562 mapFilter_t mf = (mapFilter_t) f; 563 564 if (mf->m != NULL) { 565 ckfree((char *) mf->m); 566 } 567 if (mf->s != NULL) { 568 ckfree((char *) mf->s); 569 } 570 ckfree((char *) mf); 571} 572 573Snack_FilterType snackMapType = { 574 "map", 575 mapCreateProc, 576 mapConfigProc, 577 mapStartProc, 578 mapFlowProc, 579 mapFreeProc, 580 (Snack_FilterType *) NULL 581}; 582 583#define NMAXECHOS 10 584 585struct echoFilter { 586 configProc *configProc; 587 startProc *startProc; 588 flowProc *flowProc; 589 freeProc *freeProc; 590 Tcl_Interp *interp; 591 Snack_Filter prev, next; 592 Snack_StreamInfo si; 593 double dataRatio; 594 int reserved[4]; 595 /* private members */ 596 int cnt; 597 int numDelays; 598 float *buf; 599 float inGain; 600 float outGain; 601 float delay[NMAXECHOS]; 602 float decay[NMAXECHOS]; 603 int nsmp[NMAXECHOS]; 604 int nmax; 605 int rest; 606} echoFilter; 607 608typedef struct echoFilter *echoFilter_t; 609 610int 611echoConfigProc(Snack_Filter f, Tcl_Interp *interp, int objc, 612 Tcl_Obj *CONST objv[]) 613{ 614 echoFilter_t rf = (echoFilter_t) f; 615 int i; 616 double val; 617 618 /* Arguments need to be at least four and also come in pairs */ 619 620 if (objc < 4 || objc % 2) { 621 Tcl_WrongNumArgs(interp, 0, objv, "echo inGain outGain delay1 decay1 ..."); 622 return TCL_ERROR; 623 } 624 625 if (Tcl_GetDoubleFromObj(interp, objv[0], &val) != TCL_OK) { 626 return TCL_ERROR; 627 } 628 rf->inGain = (float) val; 629 if (Tcl_GetDoubleFromObj(interp, objv[1], &val) != TCL_OK) { 630 return TCL_ERROR; 631 } 632 rf->outGain = (float) val; 633 634 rf->numDelays = 0; 635 636 for (i = 2; i < objc; i += 2) { 637 if (Tcl_GetDoubleFromObj(interp, objv[i], &val) != TCL_OK) { 638 return TCL_ERROR; 639 } 640 if (val < 0.0) { 641 Tcl_AppendResult(interp, "Delay must be positive", NULL); 642 return TCL_ERROR; 643 } 644 rf->delay[i/2-1] = (float) val; 645 if (Tcl_GetDoubleFromObj(interp, objv[i+1], &val) != TCL_OK) { 646 return TCL_ERROR; 647 } 648 if (val < 0.0) { 649 Tcl_AppendResult(interp, "Decay must be positive", NULL); 650 return TCL_ERROR; 651 } 652 if (val > 1.0) { 653 Tcl_AppendResult(interp, "Decay must be < 1.0", NULL); 654 return TCL_ERROR; 655 } 656 rf->decay[i/2-1] = (float) val; 657 rf->numDelays++; 658 } 659 660 if (rf->buf != NULL && rf->si != NULL) { 661 int nmax = 0; 662 663 for (i = 0; i < rf->numDelays; i++) { 664 rf->nsmp[i] = (int) (rf->delay[i] * rf->si->rate / 1000.0) * 665 rf->si->outWidth; 666 if (rf->nsmp[i] > nmax) { 667 nmax = rf->nsmp[i]; 668 } 669 } 670 671 if (nmax != rf->nmax) { 672 float *tmpbuf = (float *) ckalloc(sizeof(float) * nmax); 673 674 for (i = 0; i < rf->nmax; i++) { 675 if (i == nmax) break; 676 tmpbuf[i] = rf->buf[rf->cnt]; 677 rf->cnt = (rf->cnt+1) % rf->nmax; 678 } 679 for (; i < nmax; i++) { 680 tmpbuf[i] = 0.0f; 681 } 682 ckfree((char *) rf->buf); 683 rf->buf = tmpbuf; 684 if (nmax < rf->nmax) { 685 rf->cnt = nmax - 1; 686 } else { 687 rf->cnt = rf->nmax; 688 } 689 /* 690 if (nmax < rf->nmax) { 691 for (i = nmax - 1; i >= 0; i--) { 692 tmpbuf[i] = rf->buf[rf->cnt]; 693 rf->cnt = (rf->cnt+rf->nmax+1) % rf->nmax; 694 } 695 rf->cnt = 0; 696 } else { 697 for (i = 0; i < rf->nmax; i++) { 698 tmpbuf[i] = rf->buf[rf->cnt]; 699 rf->cnt = (rf->cnt+1) % rf->nmax; 700 } 701 for (; i < nmax; i++) { 702 tmpbuf[i] = 0.0f; 703 } 704 rf->cnt = rf->nmax; 705 } 706 ckfree((char *) rf->buf); 707 rf->buf = tmpbuf; 708 */ 709 rf->nmax = nmax; 710 rf->rest = nmax; 711 } 712 } 713 714 return TCL_OK; 715} 716 717Snack_Filter 718echoCreateProc(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) 719{ 720 echoFilter_t rf; 721 722 rf = (echoFilter_t) ckalloc(sizeof(echoFilter)); 723 rf->nmax = 0; 724 rf->numDelays = 0; 725 rf->buf = NULL; 726 727 if (echoConfigProc((Snack_Filter) rf, interp, objc, objv) != TCL_OK) { 728 ckfree((char *) rf); 729 return (Snack_Filter) NULL; 730 } 731 732 return (Snack_Filter) rf; 733} 734 735int 736echoStartProc(Snack_Filter f, Snack_StreamInfo si) 737{ 738 echoFilter_t rf = (echoFilter_t) f; 739 int i; 740 741 if (rf->buf == NULL) { 742 rf->nmax = 0; 743 for (i = 0; i < rf->numDelays; i++) { 744 rf->nsmp[i] = (int) (rf->delay[i] * si->rate / 1000.0) * si->outWidth; 745 if (rf->nsmp[i] > rf->nmax) { 746 rf->nmax = rf->nsmp[i]; 747 } 748 } 749 750 rf->buf = (float *) ckalloc(sizeof(float) * rf->nmax); 751 } 752 for (i = 0; i < rf->nmax; i++) { 753 rf->buf[i] = 0.0f; 754 } 755 rf->cnt = 0; 756 rf->rest = rf->nmax; 757 758 return TCL_OK; 759} 760 761int 762echoFlowProc(Snack_Filter f, Snack_StreamInfo si, float *in, float *out, 763 int *inFrames, int *outFrames) 764{ 765 echoFilter_t rf = (echoFilter_t) f; 766 int i, j, c; 767 float tmp; 768 769 /* Process *inFrames number samples, algorithm from SoX */ 770 771 for (i = 0; i < *inFrames; i++) { 772 for (c = 0; c < si->outWidth; c++) { 773 int index = i * si->outWidth + c; 774 tmp = in[index] * rf->inGain; 775 for (j = 0; j < rf->numDelays; j++) { 776 tmp += rf->buf[(rf->cnt + rf->nmax - rf->nsmp[j]) % rf->nmax] * 777 rf->decay[j]; 778 } 779 tmp *= rf->outGain; 780 rf->buf[rf->cnt] = in[index]; 781 out[index] = tmp; 782 rf->cnt = (rf->cnt+1) % rf->nmax; 783 } 784 } 785 786 /* If input exhausted start draining out delayed samples */ 787 788 if (*inFrames < *outFrames) { 789 for (i = *inFrames; i < *outFrames; i++) { 790 for (c = 0; c < si->outWidth; c++) { 791 tmp = 0.0f; 792 for (j = 0; j < rf->numDelays; j++) { 793 tmp += rf->buf[(rf->cnt + rf->nmax - rf->nsmp[j]) % rf->nmax] * 794 rf->decay[j]; 795 } 796 tmp *= rf->outGain; 797 rf->buf[rf->cnt] = 0.0f; 798 out[i * si->outWidth + c] = tmp; 799 rf->cnt = (rf->cnt+1) % rf->nmax; 800 rf->rest--; 801 if (rf->rest < 0) break; 802 } 803 if (rf->rest < 0) break; 804 } 805 806 if (i < *outFrames) { /* last invocation prepare for next usage */ 807 *outFrames = i; 808 for (j = 0; j < rf->nmax; j++) { 809 rf->buf[j] = 0.0f; 810 } 811 } 812 } 813 814 return TCL_OK; 815} 816 817void 818echoFreeProc(Snack_Filter f) 819{ 820 echoFilter_t rf = (echoFilter_t) f; 821 822 if (rf->buf != NULL) { 823 ckfree((char *) rf->buf); 824 } 825 ckfree((char *) rf); 826} 827 828Snack_FilterType snackEchoType = { 829 "echo", 830 echoCreateProc, 831 echoConfigProc, 832 echoStartProc, 833 echoFlowProc, 834 echoFreeProc, 835 (Snack_FilterType *) NULL 836}; 837 838#define NMAXREVERBS 10 839 840struct reverbFilter { 841 configProc *configProc; 842 startProc *startProc; 843 flowProc *flowProc; 844 freeProc *freeProc; 845 Tcl_Interp *interp; 846 Snack_Filter prev, next; 847 Snack_StreamInfo si; 848 double dataRatio; 849 int reserved[4]; 850 /* private members */ 851 int cnt; 852 int numDelays; 853 float *buf; 854 float inGain; 855 float outGain; 856 float time; 857 float delay[NMAXREVERBS]; 858 float decay[NMAXREVERBS]; 859 int nsmp[NMAXREVERBS]; 860 int nmax; 861 float pl, ppl, pppl; 862} reverbFilter; 863 864typedef struct reverbFilter *reverbFilter_t; 865 866int 867reverbConfigProc(Snack_Filter f, Tcl_Interp *interp, int objc, 868 Tcl_Obj *CONST objv[]) 869{ 870 reverbFilter_t rf = (reverbFilter_t) f; 871 int i; 872 double val; 873 874 /* Arguments need to be at least three */ 875 876 if (objc < 3) { 877 Tcl_WrongNumArgs(interp, 0, objv, "reverb outGain time delay1 ..."); 878 return TCL_ERROR; 879 } 880 881 if (Tcl_GetDoubleFromObj(interp, objv[0], &val) != TCL_OK) { 882 return TCL_ERROR; 883 } 884 rf->outGain = (float) val; 885 if (Tcl_GetDoubleFromObj(interp, objv[1], &val) != TCL_OK) { 886 return TCL_ERROR; 887 } 888 rf->time = (float) val; 889 rf->inGain = 1.0f; 890 rf->numDelays = 0; 891 892 for (i = 2; i < objc; i++) { 893 if (Tcl_GetDoubleFromObj(interp, objv[i], &val) != TCL_OK) { 894 return TCL_ERROR; 895 } 896 if (val < 0.0) { 897 Tcl_AppendResult(interp, "Delay must be positive", NULL); 898 return TCL_ERROR; 899 } 900 rf->delay[i-2] = (float) val; 901 rf->numDelays++; 902 } 903 904 if (rf->buf != NULL && rf->si != NULL) { 905 int nmax = 0; 906 907 for (i = 0; i < rf->numDelays; i++) { 908 rf->nsmp[i] = (int) (rf->delay[i] * rf->si->rate / 1000.0) * 909 rf->si->outWidth; 910 if (rf->nsmp[i] > nmax) { 911 nmax = rf->nsmp[i]; 912 } 913 rf->decay[i] = (float) pow(10.0, (-3.0 * rf->delay[i] / rf->time)); 914 } 915 rf->pppl = rf->ppl = rf->pl = 32767.0f; 916 for (i = 0; i < rf->numDelays; i++) 917 rf->inGain *= (1.0f - (rf->decay[i] * rf->decay[i])); 918 919 if (nmax != rf->nmax) { 920 float *tmpbuf = (float *) ckalloc(sizeof(float) * nmax); 921 922 for (i = 0; i < rf->nmax; i++) { 923 if (i == nmax) break; 924 tmpbuf[i] = rf->buf[rf->cnt]; 925 rf->cnt = (rf->cnt+1) % rf->nmax; 926 } 927 for (; i < nmax; i++) { 928 tmpbuf[i] = 0.0f; 929 } 930 ckfree((char *) rf->buf); 931 rf->buf = tmpbuf; 932 if (nmax < rf->nmax) { 933 rf->cnt = nmax - 1; 934 } else { 935 rf->cnt = rf->nmax; 936 } 937 /* 938 if (nmax < rf->nmax) { 939 for (i = nmax - 1; i >= 0; i--) { 940 tmpbuf[i] = rf->buf[rf->cnt]; 941 rf->cnt = (rf->cnt+rf->nmax+1) % rf->nmax; 942 } 943 rf->cnt = 0; 944 } else { 945 for (i = 0; i < rf->nmax; i++) { 946 tmpbuf[i] = rf->buf[rf->cnt]; 947 rf->cnt = (rf->cnt+1) % rf->nmax; 948 } 949 for (; i < nmax; i++) { 950 tmpbuf[i] = 0.0f; 951 } 952 rf->cnt = rf->nmax; 953 } 954 ckfree((char *) rf->buf); 955 rf->buf = tmpbuf; 956 */ 957 rf->nmax = nmax; 958 } 959 } 960 961 return TCL_OK; 962} 963 964Snack_Filter 965reverbCreateProc(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) 966{ 967 reverbFilter_t rf; 968 969 rf = (reverbFilter_t) ckalloc(sizeof(reverbFilter)); 970 rf->nmax = 0; 971 rf->numDelays = 0; 972 rf->buf = NULL; 973 974 if (reverbConfigProc((Snack_Filter) rf, interp, objc, objv) != TCL_OK) { 975 ckfree((char *) rf); 976 return (Snack_Filter) NULL; 977 } 978 979 return (Snack_Filter) rf; 980} 981 982int 983reverbStartProc(Snack_Filter f, Snack_StreamInfo si) 984{ 985 reverbFilter_t rf = (reverbFilter_t) f; 986 int i; 987 988 if (rf->buf == NULL) { 989 rf->nmax = 0; 990 for (i = 0; i < rf->numDelays; i++) { 991 rf->nsmp[i] = (int) (rf->delay[i] * si->rate / 1000.0) * si->outWidth; 992 if (rf->nsmp[i] > rf->nmax) { 993 rf->nmax = rf->nsmp[i]; 994 } 995 rf->decay[i] = (float) pow(10.0, (-3.0 * rf->delay[i] / rf->time)); 996 } 997 rf->pppl = rf->ppl = rf->pl = 32767.0f; 998 for (i = 0; i < rf->numDelays; i++) 999 rf->inGain *= (1.0f - (rf->decay[i] * rf->decay[i])); 1000 1001 rf->buf = (float *) ckalloc(sizeof(float) * rf->nmax); 1002 for (i = 0; i < rf->nmax; i++) { 1003 rf->buf[i] = 0.0f; 1004 } 1005 } 1006 rf->cnt = 0; 1007 1008 return TCL_OK; 1009} 1010 1011int 1012reverbFlowProc(Snack_Filter f, Snack_StreamInfo si, float *in, float *out, 1013 int *inFrames, int *outFrames) 1014{ 1015 reverbFilter_t rf = (reverbFilter_t) f; 1016 int i, j, c; 1017 float tmp; 1018 1019 /* Process *inFrames number samples, algorithm from SoX */ 1020 1021 for (i = 0; i < *inFrames; i++) { 1022 for (c = 0; c < si->outWidth; c++) { 1023 int index = i * si->outWidth + c; 1024 tmp = in[index] * rf->inGain; 1025 for (j = 0; j < rf->numDelays; j++) { 1026 tmp += rf->buf[(rf->cnt + rf->nmax - rf->nsmp[j]) % rf->nmax] * 1027 rf->decay[j]; 1028 } 1029 rf->buf[rf->cnt] = tmp; 1030 tmp *= rf->outGain; 1031 out[index] = tmp; 1032 rf->cnt = (rf->cnt+1) % rf->nmax; 1033 } 1034 } 1035 1036 /* If input exhausted start draining out delayed samples */ 1037 1038 if (*inFrames < *outFrames) { 1039 for (i = *inFrames; i < *outFrames; i++) { 1040 for (c = 0; c < si->outWidth; c++) { 1041 tmp = 0.0f; 1042 for (j = 0; j < rf->numDelays; j++) { 1043 tmp += rf->buf[(rf->cnt + rf->nmax - rf->nsmp[j]) % rf->nmax] * 1044 rf->decay[j]; 1045 } 1046 rf->buf[rf->cnt] = tmp; 1047 tmp *= rf->outGain; 1048 out[i * si->outWidth + c] = tmp; 1049 rf->cnt = (rf->cnt+1) % rf->nmax; 1050 1051 rf->pppl = rf->ppl; 1052 rf->ppl = rf->pl; 1053 rf->pl = tmp; 1054 1055 if (fabs(rf->pl)+fabs(rf->ppl)+fabs(rf->pppl) < 10.0f) break; 1056 } 1057 if (fabs(rf->pl)+fabs(rf->ppl)+fabs(rf->pppl) < 10.0f) break; 1058 } 1059 1060 if (i < *outFrames) { /* last invocation prepare for next usage */ 1061 *outFrames = i; 1062 for (j = 0; j < rf->nmax; j++) { 1063 rf->buf[j] = 0.0f; 1064 } 1065 } 1066 } 1067 1068 return TCL_OK; 1069} 1070 1071void 1072reverbFreeProc(Snack_Filter f) 1073{ 1074 reverbFilter_t rf = (reverbFilter_t) f; 1075 1076 if (rf->buf != NULL) { 1077 ckfree((char *) rf->buf); 1078 } 1079 ckfree((char *) rf); 1080} 1081 1082Snack_FilterType snackReverbType = { 1083 "reverb", 1084 reverbCreateProc, 1085 reverbConfigProc, 1086 reverbStartProc, 1087 reverbFlowProc, 1088 reverbFreeProc, 1089 (Snack_FilterType *) NULL 1090}; 1091 1092struct composeFilter { 1093 configProc *configProc; 1094 startProc *startProc; 1095 flowProc *flowProc; 1096 freeProc *freeProc; 1097 Tcl_Interp *interp; 1098 Snack_Filter prev, next; 1099 Snack_StreamInfo si; 1100 double dataRatio; 1101 int reserved[4]; 1102 /* private members */ 1103 Snack_Filter ff, lf; 1104} composeFilter; 1105 1106typedef struct composeFilter *composeFilter_t; 1107 1108int 1109composeConfigProc(Snack_Filter f, Tcl_Interp *interp, int objc, 1110 Tcl_Obj *CONST objv[]) 1111{ 1112 composeFilter_t cf = (composeFilter_t) f; 1113 int i; 1114 char *string = NULL; 1115 Tcl_HashEntry *hPtr; 1116 Snack_Filter curr, last; 1117 1118 /* Need at least two filters to be composed */ 1119 1120 if (objc < 2) { 1121 Tcl_WrongNumArgs(interp, 0, objv, "compose filter1 filter2 ..."); 1122 return TCL_ERROR; 1123 } 1124 1125 for (i = 0; i < objc; i++) { 1126 string = Tcl_GetStringFromObj(objv[i], NULL); 1127 hPtr = Tcl_FindHashEntry(filterHashTable, string); 1128 if (hPtr == NULL) { 1129 Tcl_AppendResult(interp, "No such filter: ", string, (char *) NULL); 1130 return TCL_ERROR; 1131 } 1132 } 1133 1134 string = Tcl_GetStringFromObj(objv[0], NULL); 1135 hPtr = Tcl_FindHashEntry(filterHashTable, string); 1136 cf->ff = (Snack_Filter) Tcl_GetHashValue(hPtr); 1137 curr = last = cf->ff; 1138 1139 string = Tcl_GetStringFromObj(objv[objc-1], NULL); 1140 hPtr = Tcl_FindHashEntry(filterHashTable, string); 1141 cf->lf = (Snack_Filter) Tcl_GetHashValue(hPtr); 1142 1143 for (i = 1; i < objc-1; i++) { 1144 string = Tcl_GetStringFromObj(objv[i], NULL); 1145 hPtr = Tcl_FindHashEntry(filterHashTable, string); 1146 if (hPtr != NULL) { 1147 curr = (Snack_Filter) Tcl_GetHashValue(hPtr); 1148 curr->prev = last; 1149 last->next = curr; 1150 last = last->next; 1151 } 1152 } 1153 curr->next = cf->lf; 1154 cf->lf->prev = cf->ff; 1155 1156 return TCL_OK; 1157} 1158 1159Snack_Filter 1160composeCreateProc(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) 1161{ 1162 composeFilter_t cf; 1163 1164 cf = (composeFilter_t) ckalloc(sizeof(composeFilter)); 1165 1166 if (composeConfigProc((Snack_Filter) cf, interp, objc, objv) != TCL_OK) { 1167 ckfree((char *) cf); 1168 return (Snack_Filter) NULL; 1169 } 1170 1171 return (Snack_Filter) cf; 1172} 1173 1174int 1175composeStartProc(Snack_Filter f, Snack_StreamInfo si) 1176{ 1177 composeFilter_t cf = (composeFilter_t) f; 1178 Snack_Filter pf = cf->ff; 1179 1180 while (pf != NULL) { 1181 pf->si = si; 1182 (pf->startProc)(pf, si); 1183 pf = pf->next; 1184 } 1185 1186 return TCL_OK; 1187} 1188 1189int 1190composeFlowProc(Snack_Filter f, Snack_StreamInfo si, float *in, float *out, 1191 int *inFrames, int *outFrames) 1192{ 1193 composeFilter_t cf = (composeFilter_t) f; 1194 Snack_Filter pf = cf->ff; 1195 int inSize = *inFrames; 1196 int outSize = *outFrames; 1197 1198 while (pf != NULL) { 1199 (pf->flowProc)(pf, si, in, out, &inSize, &outSize); 1200 inSize = outSize; 1201 pf = pf->next; 1202 } 1203 1204 if (outSize < *outFrames) { 1205 } 1206 1207 *outFrames = outSize; 1208 1209 return TCL_OK; 1210} 1211 1212void 1213composeFreeProc(Snack_Filter f) 1214{ 1215 composeFilter_t cf = (composeFilter_t) f; 1216 1217 ckfree((char *) cf); 1218} 1219 1220Snack_FilterType snackComposeType = { 1221 "compose", 1222 composeCreateProc, 1223 composeConfigProc, 1224 composeStartProc, 1225 composeFlowProc, 1226 composeFreeProc, 1227 (Snack_FilterType *) NULL 1228}; 1229 1230struct fadeFilter { 1231 configProc *configProc; 1232 startProc *startProc; 1233 flowProc *flowProc; 1234 freeProc *freeProc; 1235 Tcl_Interp *interp; 1236 Snack_Filter prev, next; 1237 Snack_StreamInfo si; 1238 double dataRatio; 1239 int reserved[4]; 1240 /* private members */ 1241 int in; 1242 int type; 1243 float msLength; 1244 int length; 1245 int pos; 1246 float floor; 1247} fadeFilter; 1248 1249typedef struct fadeFilter *fadeFilter_t; 1250#define LINEAR 0 1251#define EXPONENTIAL 1 1252#define LOGARITHMIC 2 1253 1254int 1255fadeConfigProc(Snack_Filter f, Tcl_Interp *interp, int objc, 1256 Tcl_Obj *CONST objv[]) 1257{ 1258 fadeFilter_t mf = (fadeFilter_t) f; 1259 char *typestr; 1260 double val; 1261 1262 if (objc == 3 || objc == 4) { 1263 typestr = Tcl_GetStringFromObj(objv[0], NULL); 1264 if (strcasecmp(typestr, "in") == 0) { 1265 mf->in = 1; 1266 } else if (strcasecmp(typestr, "out") == 0) { 1267 mf->in = 0; 1268 } else { 1269 Tcl_SetResult(interp, "bad fade direction, must be in or out", 1270 TCL_STATIC); 1271 return TCL_ERROR; 1272 } 1273 1274 typestr = Tcl_GetStringFromObj(objv[1], NULL); 1275 if (strncasecmp(typestr, "linear", 3) == 0) { 1276 mf->type = LINEAR; 1277 } else if (strncasecmp(typestr, "exponential", 3) == 0) { 1278 mf->type = EXPONENTIAL; 1279 } else if (strncasecmp(typestr, "logarithmic", 3) == 0) { 1280 mf->type = LOGARITHMIC; 1281 } else { 1282 Tcl_SetResult(interp, 1283 "bad fade type, must be linear, exponential, or logarithmic", 1284 TCL_STATIC); 1285 return TCL_ERROR; 1286 } 1287 1288 if (Tcl_GetDoubleFromObj(interp, objv[2], &val) != TCL_OK) { 1289 return TCL_ERROR; 1290 } 1291 mf->msLength = (float) val; 1292 1293 if (objc == 4) { 1294 if (Tcl_GetDoubleFromObj(interp, objv[3], &val) != TCL_OK) { 1295 return TCL_ERROR; 1296 } 1297 mf->floor = (float) val; 1298 } 1299 1300 } else { 1301 1302 /* Arguments need to be at least three */ 1303 1304 Tcl_WrongNumArgs(interp, 0, objv, "fade direction type length"); 1305 return TCL_ERROR; 1306 } 1307 1308 return TCL_OK; 1309} 1310 1311Snack_Filter 1312fadeCreateProc(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) 1313{ 1314 fadeFilter_t mf; 1315 1316 mf = (fadeFilter_t) ckalloc(sizeof(fadeFilter)); 1317 mf->floor = 0.0; 1318 1319 if (fadeConfigProc((Snack_Filter) mf, interp, objc, objv) != TCL_OK) { 1320 ckfree((char *) mf); 1321 return (Snack_Filter) NULL; 1322 } 1323 1324 return (Snack_Filter) mf; 1325} 1326 1327int 1328fadeStartProc(Snack_Filter f, Snack_StreamInfo si) 1329{ 1330 fadeFilter_t mf = (fadeFilter_t) f; 1331 mf->length = (int) (mf->msLength * si->rate / 1000.0); 1332 mf->pos = 0; 1333 1334 return TCL_OK; 1335} 1336 1337#define EULER 2.7182818284590452354 1338#define EXP_MINUS_1 0.36787944117 1339 1340int 1341fadeFlowProc(Snack_Filter f, Snack_StreamInfo si, float *in, float *out, 1342 int *inFrames, int *outFrames) 1343{ 1344 fadeFilter_t mf = (fadeFilter_t) f; 1345 int i = 0, fr, wi; 1346 float factor = 1.0; 1347 1348 for (fr = 0; fr < *inFrames; fr++) { 1349 if (mf->pos < mf->length) { 1350 switch (mf->type) { 1351 case LINEAR: 1352 if (mf->in) { 1353 factor = (float) ((1.0 - mf->floor) * mf->pos / (mf->length - 1) + mf->floor); 1354 } else { 1355 factor = (float) (1.0 - ((1.0 - mf->floor) * mf->pos / (mf->length - 1))); 1356 } 1357 break; 1358 case EXPONENTIAL: 1359 if (mf->in) { 1360 factor = (float) (1.0 - mf->floor) * exp(-10.0+10.0 * mf->pos/(mf->length-1)) +mf->floor; 1361 } else { 1362 factor = (float) (1.0 - mf->floor) * exp(-10.0 * mf->pos/(mf->length-1)) + mf->floor; 1363 } 1364 break; 1365 case LOGARITHMIC: 1366 if (mf->in) { 1367 factor = (float) (1.0 - mf->floor) * (0.5 + 0.5 * 1368 log(EXP_MINUS_1 + (EULER - EXP_MINUS_1) 1369 * (float) mf->pos / (mf->length-1))) + mf->floor; 1370 } else { 1371 factor = (float) (1.0 - mf->floor) * (0.5 + 0.5 * 1372 log(EXP_MINUS_1 + (EULER - EXP_MINUS_1) 1373 * (1.0-(float) mf->pos / (mf->length-1)))) + mf->floor; 1374 } 1375 break; 1376 } 1377 } else { 1378 factor = 1.0; 1379 } 1380 for (wi = 0; wi < si->outWidth; wi++) { 1381 out[i] = factor * in[i]; 1382 i++; 1383 } 1384 mf->pos++; 1385 } 1386 1387 *outFrames = *inFrames; 1388 1389 return TCL_OK; 1390} 1391 1392void 1393fadeFreeProc(Snack_Filter f) 1394{ 1395 fadeFilter_t mf = (fadeFilter_t) f; 1396 1397 ckfree((char *) mf); 1398} 1399 1400Snack_FilterType snackFadeType = { 1401 "fade", 1402 fadeCreateProc, 1403 fadeConfigProc, 1404 fadeStartProc, 1405 fadeFlowProc, 1406 fadeFreeProc, 1407 (Snack_FilterType *) NULL 1408}; 1409 1410void 1411Snack_CreateFilterType(Snack_FilterType *typePtr) 1412{ 1413 Snack_FilterType *typePtr2, *prevPtr; 1414 1415 /* 1416 * If there's already a filter type with the given name, remove it. 1417 */ 1418 1419 for (typePtr2 = snackFilterTypes, prevPtr = NULL; typePtr2 != NULL; 1420 prevPtr = typePtr2, typePtr2 = typePtr2->nextPtr) { 1421 if (strcmp(typePtr2->name, typePtr->name) == 0) { 1422 if (prevPtr == NULL) { 1423 snackFilterTypes = typePtr2->nextPtr; 1424 } else { 1425 prevPtr->nextPtr = typePtr2->nextPtr; 1426 } 1427 break; 1428 } 1429 } 1430 typePtr->nextPtr = snackFilterTypes; 1431 snackFilterTypes = typePtr; 1432} 1433 1434extern void createSynthesisFilters(); 1435extern void createIIRFilter(); 1436 1437void 1438SnackCreateFilterTypes(Tcl_Interp *interp) 1439{ 1440 snackFilterTypes = &snackComposeType; 1441 snackComposeType.nextPtr = NULL; 1442 Snack_CreateFilterType(&snackMapType); 1443 Snack_CreateFilterType(&snackEchoType); 1444 Snack_CreateFilterType(&snackReverbType); 1445 Snack_CreateFilterType(&snackFadeType); 1446 createSynthesisFilters(); 1447 createIIRFilter(); 1448 /* Snack_CreateFilterType(&snackLowpassType);*/ 1449} 1450