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