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 <string.h>
25 #include <math.h>
26 
27 #define SNACK_PI 3.141592653589793
28 
29 void
Snack_InitWindow(float * win,int winlen,int fftlen,int type)30 Snack_InitWindow(float *win, int winlen, int fftlen, int type)
31 {
32   int i;
33 
34   if (winlen > fftlen)
35     winlen = fftlen;
36 
37   if (type == SNACK_WIN_RECT) {
38     for (i = 0; i < winlen; i++)
39       win[i] = (float) 1.0;
40   } else if (type == SNACK_WIN_HANNING) {
41     for (i = 0; i < winlen; i++)
42       win[i] = (float)(0.5 * (1.0 - cos(i * 2.0 * SNACK_PI / (winlen - 1))));
43   } else if (type == SNACK_WIN_BARTLETT) {
44     for (i = 0; i < winlen/2; i++)
45       win[i] = (float)(((2.0 * i) / (winlen - 1)));
46     for (i = winlen/2; i < winlen; i++)
47       win[i] = (float)(2.0 * (1.0 - ((float)i / (winlen - 1))));
48   } else if (type == SNACK_WIN_BLACKMAN) {
49     for (i = 0; i < winlen; i++)
50       win[i] = (float)((0.42 - 0.5 * cos(i * 2.0 * SNACK_PI / (winlen - 1))
51 			+ 0.08 * cos(i * 4.0 * SNACK_PI / (winlen - 1))));
52   } else {  /* default: Hamming window */
53     for (i = 0; i < winlen; i++)
54       win[i] = (float)(0.54 - 0.46 * cos(i * 2.0 * SNACK_PI / (winlen - 1)));
55   }
56 
57   for (i = winlen; i < fftlen; i++)
58     win[i] = 0.0;
59 }
60 
61 int
CheckFFTlen(Tcl_Interp * interp,int fftlen)62 CheckFFTlen(Tcl_Interp *interp, int fftlen)
63 {
64   int n = NMIN;
65   char str[10];
66 
67   while (n <= NMAX) {
68     if (n == fftlen) return TCL_OK;
69     n *= 2;
70   }
71 
72   Tcl_AppendResult(interp, "-fftlength must be one of { ", NULL);
73 
74   for (n = NMIN; n <= NMAX; n*=2) {
75     sprintf(str, "%d ", n);
76     Tcl_AppendResult(interp, str, NULL);
77   }
78   Tcl_AppendResult(interp, "}", NULL);
79   return TCL_ERROR;
80 }
81 
82 int
CheckWinlen(Tcl_Interp * interp,int winlen,int fftlen)83 CheckWinlen(Tcl_Interp *interp, int winlen, int fftlen)
84 {
85   char str[10];
86 
87   if (winlen < 1) {
88     Tcl_AppendResult(interp, "-winlength must be > 0", NULL);
89     return TCL_ERROR;
90   }
91   if (winlen > fftlen) {
92     Tcl_AppendResult(interp, "-winlength must be <= fftlength (", NULL);
93     sprintf(str, "%d)", fftlen);
94     Tcl_AppendResult(interp, str, NULL);
95     return TCL_ERROR;
96   }
97 
98   return TCL_OK;
99 }
100 
101 void
GetFloatMonoSig(Sound * s,SnackLinkedFileInfo * info,float * sig,int beg,int len,int channel)102 GetFloatMonoSig(Sound *s,SnackLinkedFileInfo *info,
103 		float *sig, int beg, int len, int channel) {
104   /* sig buffer must be allocated, file must be open! */
105 
106   int i;
107 
108   if (s->storeType == SOUND_IN_MEMORY) {
109     if (s->nchannels == 1 || channel != -1) {
110       int p = beg * s->nchannels + channel;
111 
112       for (i = 0; i < len; i++) {
113 	sig[i] = (float) (FSAMPLE(s, p));
114 	p += s->nchannels;
115       }
116     } else {
117       int c;
118 
119       for (i = 0; i < len; i++) {
120 	sig[i] = 0.0;
121       }
122       for (c = 0; c < s->nchannels; c++) {
123 	int p = beg * s->nchannels + c;
124 
125 	for (i = 0; i < len; i++) {
126 	  sig[i] += (float) (FSAMPLE(s, p));
127 	  p += s->nchannels;
128 	}
129       }
130       for (i = 0; i < len; i++) {
131 	sig[i] /= s->nchannels;
132       }
133     }
134   } else { /* storeType != SOUND_IN_MEMORY */
135     if (s->nchannels == 1 || channel != -1) {
136       int p = beg * s->nchannels + channel;
137 
138       for (i = 0; i < len; i++) {
139 	sig[i] = (float) (GetSample(info, p));
140 	p += s->nchannels;
141       }
142     } else {
143       int c;
144 
145       for (i = 0; i < len; i++) {
146 	sig[i] = 0.0;
147       }
148       for (c = 0; c < s->nchannels; c++) {
149 	int p = beg * s->nchannels + c;
150 
151 	for (i = 0; i < len; i++) {
152 	  sig[i] += (float) (GetSample(info, p));
153 	  p += s->nchannels;
154 	}
155       }
156       for (i = 0; i < len; i++) {
157 	sig[i] /= s->nchannels;
158       }
159     }
160   }
161 }
162 
163 static float xfft[NMAX];
164 static float ffts[NMAX];
165 static float hamwin[NMAX];
166 #define SNACK_DEFAULT_DBPWINTYPE SNACK_WIN_HAMMING
167 #define SNACK_DEFAULT_DBP_LPC_ORDER 20
168 #define SNACK_MAX_LPC_ORDER  40
169 
170 int
CheckLPCorder(Tcl_Interp * interp,int lpcorder)171 CheckLPCorder(Tcl_Interp *interp, int lpcorder)
172 {
173   char str[10];
174   if (lpcorder < 1) {
175     Tcl_AppendResult(interp, "-lpcorder must be > 0", NULL);
176     return TCL_ERROR;
177   }
178   if (lpcorder > SNACK_MAX_LPC_ORDER) {
179     Tcl_AppendResult(interp, "-lpcorder must be <= ", NULL);
180     sprintf(str, "%d)", SNACK_MAX_LPC_ORDER);
181     Tcl_AppendResult(interp, str, NULL);
182     return TCL_ERROR;
183   }
184 
185   return TCL_OK;
186 }
187 
188 extern void Snack_PowerSpectrum(float *z);
189 
190 int
dBPowerSpectrumCmd(Sound * s,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])191 dBPowerSpectrumCmd(Sound *s, Tcl_Interp *interp, int objc,
192 		   Tcl_Obj *CONST objv[])
193 {
194   double dpreemph = 0.0;
195   float preemph = 0.0;
196   int i, j, n = 0, arg;
197   int channel = 0, winlen = 256, fftlen = 512;
198   int startpos = 0, endpos = -1, skip = -1;
199   Tcl_Obj *list;
200   SnackLinkedFileInfo info;
201   SnackWindowType wintype = SNACK_DEFAULT_DBPWINTYPE;
202   static CONST84 char *subOptionStrings[] = {
203     "-start", "-end", "-channel", "-fftlength", "-winlength", "-windowlength",
204     "-preemphasisfactor", "-skip", "-windowtype", "-analysistype",
205     "-lpcorder", NULL
206   };
207   enum subOptions {
208     START, END, CHANNEL, FFTLEN, WINLEN, WINDOWLEN, PREEMPH, SKIP, WINTYPE,
209     ANATYPE, LPCORDER
210   };
211   float *sig_lpc;
212   float presample = 0.0;
213   int siglen, type = 0, lpcOrder = SNACK_DEFAULT_DBP_LPC_ORDER;
214   float g_lpc;
215 
216   if (s->debug > 0) Snack_WriteLog("Enter dBPowerSpectrumCmd\n");
217 
218   for (arg = 2; arg < objc; arg += 2) {
219     int index;
220 
221     if (Tcl_GetIndexFromObj(interp, objv[arg], subOptionStrings,
222 			    "option", 0, &index) != TCL_OK) {
223       return TCL_ERROR;
224     }
225 
226     if (arg + 1 == objc) {
227       Tcl_AppendResult(interp, "No argument given for ",
228 		       subOptionStrings[index], " option", (char *) NULL);
229       return TCL_ERROR;
230     }
231 
232     switch ((enum subOptions) index) {
233     case START:
234       {
235 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &startpos) != TCL_OK)
236 	  return TCL_ERROR;
237 	break;
238       }
239     case END:
240       {
241 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &endpos) != TCL_OK)
242 	  return TCL_ERROR;
243 	break;
244       }
245     case CHANNEL:
246       {
247 	char *str = Tcl_GetStringFromObj(objv[arg+1], NULL);
248 	if (GetChannel(interp, str, s->nchannels, &channel) != TCL_OK) {
249 	  return TCL_ERROR;
250 	}
251 	break;
252       }
253     case FFTLEN:
254       {
255 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &fftlen) != TCL_OK)
256 	  return TCL_ERROR;
257 	break;
258       }
259     case WINDOWLEN:
260     case WINLEN:
261       {
262 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &winlen) != TCL_OK)
263 	  return TCL_ERROR;
264 	break;
265       }
266     case PREEMPH:
267       {
268 	if (Tcl_GetDoubleFromObj(interp, objv[arg+1], &dpreemph) != TCL_OK)
269 	  return TCL_ERROR;
270 	break;
271       }
272     case SKIP:
273       {
274 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &skip) != TCL_OK)
275 	  return TCL_ERROR;
276 	break;
277       }
278     case WINTYPE:
279       {
280 	char *str = Tcl_GetStringFromObj(objv[arg+1], NULL);
281 	if (GetWindowType(interp, str, &wintype) != TCL_OK)
282 	  return TCL_ERROR;
283 	break;
284       }
285     case ANATYPE:
286       {
287 	int len;
288  	char *str = Tcl_GetStringFromObj(objv[arg+1], &len);
289 
290 	if (strncasecmp(str, "lpc", len) == 0) {
291 	  type = 1;
292 	} else if (strncasecmp(str, "fft", len) == 0) {
293 	  type = 0;
294 	} else {
295 	  Tcl_AppendResult(interp, "-type should be FFT or LPC", (char *)NULL);
296 	  return TCL_ERROR;
297 	}
298 	break;
299       }
300     case LPCORDER:
301       {
302 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &lpcOrder) != TCL_OK)
303 	  return TCL_ERROR;
304 	if (CheckLPCorder(interp, lpcOrder) != TCL_OK)
305 	  return TCL_ERROR;
306 	break;
307       }
308     }
309   }
310 
311   if (CheckFFTlen(interp, fftlen) != TCL_OK) return TCL_ERROR;
312 
313   if (CheckWinlen(interp, winlen, fftlen) != TCL_OK) return TCL_ERROR;
314 
315   preemph = (float) dpreemph;
316 
317   if (startpos < 0 || startpos > s->length - fftlen) {
318     Tcl_AppendResult(interp, "FFT window out of bounds", NULL);
319     return TCL_ERROR;
320   }
321 
322   if (endpos < 0)
323     endpos = startpos;
324 
325   if (endpos > s->length - 1) {
326     Tcl_AppendResult(interp, "FFT window out of bounds", NULL);
327     return TCL_ERROR;
328   }
329 
330   for (i = 0; i < NMAX/2; i++) {
331     ffts[i] = 0.0;
332   }
333 
334   if (s->storeType != SOUND_IN_MEMORY) {
335     if (OpenLinkedFile(s, &info) != TCL_OK) {
336       return TCL_OK;
337     }
338   }
339 
340   Snack_InitWindow(hamwin, winlen, fftlen, wintype);
341   Snack_InitFFT(fftlen);
342 
343   if (skip < 1) {
344     skip = fftlen;
345   }
346   siglen = endpos - startpos;
347   n = siglen / skip;
348   if (n < 1) {
349     n = 1;
350   }
351 
352   if (s->nchannels == 1) {
353     channel = 0;
354   }
355 
356   if (type != 0 && n > 0) { /* LPC + FFT */
357     if (siglen < fftlen) siglen = fftlen;
358     sig_lpc = (float *) ckalloc(siglen * sizeof(float));
359 
360     GetFloatMonoSig(s, &info, sig_lpc, startpos, siglen, channel);
361     if (startpos > 0)
362       GetFloatMonoSig(s, &info, &presample, startpos - 1, 1, channel);
363     PreEmphase(sig_lpc, presample, siglen, preemph);
364 
365     /* windowing signal to make lpc look more like the fft spectrum ??? */
366     for (i = 0; i < winlen/2; i++) {
367       sig_lpc[i] = sig_lpc[i] * hamwin[i];
368     }
369     for (i = winlen/2; i < winlen; i++) {
370       sig_lpc[i + siglen - winlen] = sig_lpc[i + siglen - winlen] * hamwin[i];
371     }
372 
373     g_lpc = LpcAnalysis(sig_lpc, siglen, xfft, lpcOrder);
374     ckfree((char *) sig_lpc);
375 
376     for (i = 0; i <= lpcOrder; i++) {
377       /* the factor is a guess, try looking for analytical value */
378       xfft[i] = xfft[i] * 5000000000.0f;
379     }
380     for (i = lpcOrder + 1; i < fftlen; i++) {
381       xfft[i] = 0.0;
382     }
383 
384     Snack_DBPowerSpectrum(xfft);
385 
386     for (i = 0; i < fftlen/2; i++) {
387       ffts[i] = -xfft[i];
388     }
389 
390   } else {  /* usual FFT */
391 
392     for (j = 0; j < n; j++) {
393       if (s->storeType == SOUND_IN_MEMORY) {
394 	if (s->nchannels == 1 || channel != -1) {
395 	  int p = (startpos + j * skip) * s->nchannels + channel;
396 
397 	  for (i = 0; i < fftlen; i++) {
398 	    xfft[i] = (float) ((FSAMPLE(s, p + s->nchannels)
399 				- preemph * FSAMPLE(s, p))
400 			       * hamwin[i]);
401 	    p += s->nchannels;
402 	  }
403 	} else {
404 	  int c;
405 
406 	  for (i = 0; i < fftlen; i++) {
407 	    xfft[i] = 0.0;
408 	  }
409 	  for (c = 0; c < s->nchannels; c++) {
410 	    int p = (startpos + j * skip) * s->nchannels + c;
411 
412 	    for (i = 0; i < fftlen; i++) {
413 	      xfft[i] += (float) ((FSAMPLE(s, p + s->nchannels)
414 				   - preemph * FSAMPLE(s, p))
415 				  * hamwin[i]);
416 	      p += s->nchannels;
417 	    }
418 	  }
419 	  for (i = 0; i < fftlen; i++) {
420 	    xfft[i] /= s->nchannels;
421 	  }
422 	}
423       } else { /* storeType != SOUND_IN_MEMORY */
424 	if (s->nchannels == 1 || channel != -1) {
425 	  int p = (startpos + j * skip) * s->nchannels + channel;
426 
427 	  for (i = 0; i < fftlen; i++) {
428 	    xfft[i] = (float) ((GetSample(&info, p + s->nchannels)
429 				- preemph * GetSample(&info, p))
430 			       * hamwin[i]);
431 	    p += s->nchannels;
432 	  }
433 	} else {
434 	  int c;
435 
436 	  for (i = 0; i < fftlen; i++) {
437 	    xfft[i] = 0.0;
438 	  }
439 	  for (c = 0; c < s->nchannels; c++) {
440 	    int p = (startpos + j * skip) * s->nchannels + c;
441 
442 	    for (i = 0; i < fftlen; i++) {
443 	      xfft[i] += (float) ((GetSample(&info, p + s->nchannels)
444 				   - preemph * GetSample(&info, p))
445 				  * hamwin[i]);
446 	      p += s->nchannels;
447 	    }
448 	  }
449 	  for (i = 0; i < fftlen; i++) {
450 	    xfft[i] /= s->nchannels;
451 	  }
452 	}
453       }
454 
455       Snack_PowerSpectrum(xfft);
456 
457       for (i = 0; i < fftlen/2; i++) {
458 	ffts[i] += xfft[i];
459       }
460     }
461   }
462 
463   if (s->storeType != SOUND_IN_MEMORY) {
464     CloseLinkedFile(&info);
465   }
466 
467   if (type == 0) {
468     for (i = 0; i < fftlen/2; i++) {
469       ffts[i] = ffts[i] / (float) n;
470     }
471     for (i = 1; i < fftlen/2; i++) {
472       if (ffts[i] < SNACK_INTLOGARGMIN)
473 	ffts[i] = SNACK_INTLOGARGMIN;
474       ffts[i] = (float) (SNACK_DB * log(ffts[i]) - SNACK_CORRN);
475     }
476     if (ffts[0] < SNACK_INTLOGARGMIN)
477       ffts[0] = SNACK_INTLOGARGMIN;
478     ffts[0] = (float) (SNACK_DB * log(ffts[0]) - SNACK_CORR0);
479   }
480   list = Tcl_NewListObj(0, NULL);
481   for (i = 0; i < fftlen/2; i++) {
482     Tcl_ListObjAppendElement(interp, list, Tcl_NewDoubleObj(ffts[i]));
483   }
484 
485   Tcl_SetObjResult(interp, list);
486 
487   if (s->debug > 0) Snack_WriteLog("Exit dBPowerSpectrumCmd\n");
488 
489   return TCL_OK;
490 }
491 
492 int
GetChannel(Tcl_Interp * interp,char * str,int nchan,int * channel)493 GetChannel(Tcl_Interp *interp, char *str, int nchan, int *channel)
494 {
495   int n = -2;
496   int len = strlen(str);
497 
498   if (strncasecmp(str, "left", len) == 0) {
499     n = 0;
500   } else if (strncasecmp(str, "right", len) == 0) {
501     n = 1;
502   } else if (strncasecmp(str, "all", len) == 0) {
503     n = -1;
504   } else if (strncasecmp(str, "both", len) == 0) {
505     n = -1;
506   } else {
507     Tcl_GetInt(interp, str, &n);
508   }
509 
510   if (n < -1 || n >= nchan) {
511     Tcl_AppendResult(interp, "-channel must be left, right, both, all, -1, or an integer between 0 and the number channels - 1", NULL);
512     return TCL_ERROR;
513   }
514 
515   *channel = n;
516 
517   return TCL_OK;
518 }
519 
520 int
GetWindowType(Tcl_Interp * interp,char * str,SnackWindowType * wintype)521 GetWindowType(Tcl_Interp *interp, char *str, SnackWindowType *wintype)
522 {
523   SnackWindowType type = -1;
524   int len = strlen(str);
525 
526   if (strncasecmp(str, "hamming", len) == 0) {
527     type = SNACK_WIN_HAMMING;
528   } else if (strncasecmp(str, "hanning", len) == 0) {
529     type = SNACK_WIN_HANNING;
530   } else if (strncasecmp(str, "bartlett", len) == 0) {
531     type = SNACK_WIN_BARTLETT;
532   } else if (strncasecmp(str, "blackman", len) == 0) {
533     type = SNACK_WIN_BLACKMAN;
534   } else if (strncasecmp(str, "rectangle", len) == 0) {
535     type = SNACK_WIN_RECT;
536   }
537 
538   if (type == -1) {
539     Tcl_AppendResult(interp, "-windowtype must be hanning, hamming, bartlett,"
540 		     "blackman, or rectangle", NULL);
541     return TCL_ERROR;
542   }
543 
544   *wintype = type;
545 
546   return TCL_OK;
547 }
548 
549 float
LpcAnalysis(float * data,int N,float * f,int order)550 LpcAnalysis (float *data, int N, float *f, int order) {
551    int    i,m;
552    float  sumU = 0.0;
553    float  sumD = 0.0;
554    float  b[SNACK_MAX_LPC_ORDER+1];
555    float KK;
556 
557    float ParcorCoeffs[SNACK_MAX_LPC_ORDER];
558    /* PreData should be used when signal is not windowed */
559    float PreData[SNACK_MAX_LPC_ORDER];
560    float *errF;
561    float *errB;
562    float errF_m = 0.0;
563 
564    if ((order < 1) || (order > SNACK_MAX_LPC_ORDER))  return 0.0f;
565 
566    errF = (float *) ckalloc((N+SNACK_MAX_LPC_ORDER) * sizeof(float));
567    errB = (float *) ckalloc((N+SNACK_MAX_LPC_ORDER) * sizeof(float));
568 
569    for (i=0; i<order; i++) {
570      ParcorCoeffs[i] = 0.0;
571      PreData[i] = 0.0; /* delete here and use as argument when sig not windowed */
572    };
573 
574    for (m=0; m<order; m++)
575      errF[m] = PreData[m];
576    for (m=0; m<N; m++)
577      errF[m+order] = data[m] ;
578 
579    errB[0] = 0.0;
580    for (m=1; m<N+order; m++)
581      errB[m] = errF[m-1];
582 
583    for (i=0; i<order; i++) {
584 
585      sumU=0.0;
586      sumD=0.0;
587      for (m=i+1; m<N+order; m++) {
588        sumU -= errF[m] * errB[m];
589        sumD += errF[m] * errF[m] + errB[m] * errB[m];
590      };
591 
592      if (sumD != 0) KK = 2.0f * sumU / sumD;
593      else KK = 0;
594      ParcorCoeffs[i] = KK;
595 
596 
597      for (m=N+order-1; m>i; m--) {
598        errF[m] += KK * errB[m];
599        errB[m] = errB[m-1] + KK * errF[m-1];
600      };
601    };
602 
603    for (i=order; i<N+order; i++) {
604      errF_m += errF[i]*errF[i];
605    }
606    errF_m /= N;
607 
608    ckfree((char *)errF);
609    ckfree((char *)errB);
610 
611    /* convert to direct filter coefficients */
612    f[0] = 1.0;
613    for (m=1; m<=order; m++) {
614      f[m] = ParcorCoeffs[m-1];
615      for (i=1; i<m; i++)
616        b[i] = f[i];
617      for (i=1; i<m; i++)
618        f[i] = b[i] + ParcorCoeffs[m-1] * b[m-i];
619    }
620 
621    return (float)sqrt(errF_m);
622 }
623 
624 
PreEmphase(float * sig,float presample,int len,float preemph)625 void PreEmphase(float *sig, float presample, int len, float preemph) {
626   int i;
627   float temp;
628 
629   if (preemph == 0.0)  return;
630 
631   for (i = 0; i < len; i++) {
632     temp = sig[i];
633     sig[i] = temp - preemph * presample;
634     presample = temp;
635   }
636 }
637 
638 int
powerSpectrumCmd(Sound * s,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])639 powerSpectrumCmd(Sound *s, Tcl_Interp *interp, int objc,
640 		   Tcl_Obj *CONST objv[])
641 {
642   double dpreemph = 0.0;
643   float preemph = 0.0;
644   int i, j, n = 0, arg;
645   int channel = 0, winlen = 256, fftlen = 512;
646   int startpos = 0, endpos = -1, skip = -1;
647   Tcl_Obj *list;
648   SnackLinkedFileInfo info;
649   SnackWindowType wintype = SNACK_DEFAULT_DBPWINTYPE;
650   static CONST84 char *subOptionStrings[] = {
651     "-start", "-end", "-channel", "-fftlength", "-winlength", "-windowlength",
652     "-preemphasisfactor", "-skip", "-windowtype", "-analysistype",
653     "-lpcorder", NULL
654   };
655   enum subOptions {
656     START, END, CHANNEL, FFTLEN, WINLEN, WINDOWLEN, PREEMPH, SKIP, WINTYPE,
657     ANATYPE, LPCORDER
658   };
659   float *sig_lpc;
660   float presample = 0.0;
661   int siglen, type = 0, lpcOrder = SNACK_DEFAULT_DBP_LPC_ORDER;
662   float g_lpc;
663 
664   if (s->debug > 0) Snack_WriteLog("Enter powerSpectrumCmd\n");
665 
666   for (arg = 2; arg < objc; arg += 2) {
667     int index;
668 
669     if (Tcl_GetIndexFromObj(interp, objv[arg], subOptionStrings,
670 			    "option", 0, &index) != TCL_OK) {
671       return TCL_ERROR;
672     }
673 
674     if (arg + 1 == objc) {
675       Tcl_AppendResult(interp, "No argument given for ",
676 		       subOptionStrings[index], " option", (char *) NULL);
677       return TCL_ERROR;
678     }
679 
680     switch ((enum subOptions) index) {
681     case START:
682       {
683 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &startpos) != TCL_OK)
684 	  return TCL_ERROR;
685 	break;
686       }
687     case END:
688       {
689 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &endpos) != TCL_OK)
690 	  return TCL_ERROR;
691 	break;
692       }
693     case CHANNEL:
694       {
695 	char *str = Tcl_GetStringFromObj(objv[arg+1], NULL);
696 	if (GetChannel(interp, str, s->nchannels, &channel) != TCL_OK) {
697 	  return TCL_ERROR;
698 	}
699 	break;
700       }
701     case FFTLEN:
702       {
703 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &fftlen) != TCL_OK)
704 	  return TCL_ERROR;
705 	break;
706       }
707     case WINDOWLEN:
708     case WINLEN:
709       {
710 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &winlen) != TCL_OK)
711 	  return TCL_ERROR;
712 	break;
713       }
714     case PREEMPH:
715       {
716 	if (Tcl_GetDoubleFromObj(interp, objv[arg+1], &dpreemph) != TCL_OK)
717 	  return TCL_ERROR;
718 	break;
719       }
720     case SKIP:
721       {
722 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &skip) != TCL_OK)
723 	  return TCL_ERROR;
724 	break;
725       }
726     case WINTYPE:
727       {
728 	char *str = Tcl_GetStringFromObj(objv[arg+1], NULL);
729 	if (GetWindowType(interp, str, &wintype) != TCL_OK)
730 	  return TCL_ERROR;
731 	break;
732       }
733     case ANATYPE:
734       {
735 	int len;
736  	char *str = Tcl_GetStringFromObj(objv[arg+1], &len);
737 
738 	if (strncasecmp(str, "lpc", len) == 0) {
739 	  type = 1;
740 	} else if (strncasecmp(str, "fft", len) == 0) {
741 	  type = 0;
742 	} else {
743 	  Tcl_AppendResult(interp, "-type should be FFT or LPC", (char *)NULL);
744 	  return TCL_ERROR;
745 	}
746 	break;
747       }
748     case LPCORDER:
749       {
750 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &lpcOrder) != TCL_OK)
751 	  return TCL_ERROR;
752 	if (CheckLPCorder(interp, lpcOrder) != TCL_OK)
753 	  return TCL_ERROR;
754 	break;
755       }
756     }
757   }
758 
759   if (CheckFFTlen(interp, fftlen) != TCL_OK) return TCL_ERROR;
760 
761   if (CheckWinlen(interp, winlen, fftlen) != TCL_OK) return TCL_ERROR;
762 
763   preemph = (float) dpreemph;
764 
765   if (startpos < 0 || startpos > s->length - fftlen) {
766     Tcl_AppendResult(interp, "FFT window out of bounds", NULL);
767     return TCL_ERROR;
768   }
769 
770   if (endpos < 0)
771     endpos = startpos;
772 
773   if (endpos > s->length - 1) {
774     Tcl_AppendResult(interp, "FFT window out of bounds", NULL);
775     return TCL_ERROR;
776   }
777 
778   for (i = 0; i < NMAX/2; i++) {
779     ffts[i] = 0.0;
780   }
781 
782   if (s->storeType != SOUND_IN_MEMORY) {
783     if (OpenLinkedFile(s, &info) != TCL_OK) {
784       return TCL_OK;
785     }
786   }
787 
788   Snack_InitWindow(hamwin, winlen, fftlen, wintype);
789 
790   Snack_InitFFT(fftlen);
791 
792   if (skip < 1) {
793     skip = fftlen;
794   }
795   siglen = endpos - startpos;
796   n = siglen / skip;
797   if (n < 1) {
798     n = 1;
799   }
800 
801   if (s->nchannels == 1) {
802     channel = 0;
803   }
804 
805   if (type != 0 && n > 0) { /* LPC + FFT */
806     if (siglen < fftlen) siglen = fftlen;
807     sig_lpc = (float *) ckalloc(siglen * sizeof(float));
808 
809     GetFloatMonoSig(s, &info, sig_lpc, startpos, siglen, channel);
810     if (startpos > 0)
811       GetFloatMonoSig(s, &info, &presample, startpos - 1, 1, channel);
812     PreEmphase(sig_lpc, presample, siglen, preemph);
813 
814     /* windowing signal to make lpc look more like the fft spectrum ??? */
815     for (i = 0; i < winlen/2; i++) {
816       sig_lpc[i] = sig_lpc[i] * hamwin[i];
817     }
818     for (i = winlen/2; i < winlen; i++) {
819       sig_lpc[i + siglen - winlen] = sig_lpc[i + siglen - winlen] * hamwin[i];
820     }
821 
822     g_lpc = LpcAnalysis(sig_lpc, siglen, xfft, lpcOrder);
823     ckfree((char *) sig_lpc);
824 
825     for (i = 0; i <= lpcOrder; i++) {
826       /* the factor is a guess, try looking for analytical value */
827       xfft[i] = xfft[i] * 5000000000.0f;
828     }
829     for (i = lpcOrder + 1; i < fftlen; i++) {
830       xfft[i] = 0.0;
831     }
832 
833     Snack_PowerSpectrum(xfft);
834 
835     for (i = 0; i < fftlen/2; i++) {
836       ffts[i] = -xfft[i];
837     }
838 
839   } else {  /* usual FFT */
840 
841     for (j = 0; j < n; j++) {
842       if (s->storeType == SOUND_IN_MEMORY) {
843 	if (s->nchannels == 1 || channel != -1) {
844 	  int p = (startpos + j * skip) * s->nchannels + channel;
845 
846 	  for (i = 0; i < fftlen; i++) {
847 	    /*	    if (i < 4) printf("a %f %d\n", FSAMPLE(s, i), n);*/
848 	    xfft[i] = (float) ((FSAMPLE(s, p + s->nchannels)
849 				- preemph * FSAMPLE(s, p))
850 			       * hamwin[i]);
851 	    /*	    if (i < 4) printf("b %f %f\n", xfft[i], hamwin[i]);*/
852 	    p += s->nchannels;
853 	  }
854 	} else {
855 	  int c;
856 
857 	  for (i = 0; i < fftlen; i++) {
858 	    xfft[i] = 0.0;
859 	  }
860 	  for (c = 0; c < s->nchannels; c++) {
861 	    int p = (startpos + j * skip) * s->nchannels + c;
862 
863 	    for (i = 0; i < fftlen; i++) {
864 	      xfft[i] += (float) ((FSAMPLE(s, p + s->nchannels)
865 				   - preemph * FSAMPLE(s, p))
866 				  * hamwin[i]);
867 	      p += s->nchannels;
868 	    }
869 	  }
870 	  for (i = 0; i < fftlen; i++) {
871 	    xfft[i] /= s->nchannels;
872 	  }
873 	}
874       } else { /* storeType != SOUND_IN_MEMORY */
875 	if (s->nchannels == 1 || channel != -1) {
876 	  int p = (startpos + j * skip) * s->nchannels + channel;
877 
878 	  for (i = 0; i < fftlen; i++) {
879 	    xfft[i] = (float) ((GetSample(&info, p + s->nchannels)
880 				- preemph * GetSample(&info, p))
881 			       * hamwin[i]);
882 	    p += s->nchannels;
883 	  }
884 	} else {
885 	  int c;
886 
887 	  for (i = 0; i < fftlen; i++) {
888 	    xfft[i] = 0.0;
889 	  }
890 	  for (c = 0; c < s->nchannels; c++) {
891 	    int p = (startpos + j * skip) * s->nchannels + c;
892 
893 	    for (i = 0; i < fftlen; i++) {
894 	      xfft[i] += (float) ((GetSample(&info, p + s->nchannels)
895 				   - preemph * GetSample(&info, p))
896 				  * hamwin[i]);
897 	      p += s->nchannels;
898 	    }
899 	  }
900 	  for (i = 0; i < fftlen; i++) {
901 	    xfft[i] /= s->nchannels;
902 	  }
903 	}
904       }
905 
906       Snack_PowerSpectrum(xfft);
907       for (i = 0; i < fftlen/2; i++) {
908 	ffts[i] += (float)sqrt(xfft[i]);
909 	/*		if (i < 4) printf("power %f %f\n", xfft[i],ffts[i]);*/
910       }
911     }
912   }
913 
914   if (s->storeType != SOUND_IN_MEMORY) {
915     CloseLinkedFile(&info);
916   }
917 
918   for (i = 0; i < fftlen/2; i++) {
919     ffts[i] = ffts[i] / (float) n;
920   }
921 
922   list = Tcl_NewListObj(0, NULL);
923   for (i = 0; i < fftlen/2; i++) {
924     Tcl_ListObjAppendElement(interp, list, Tcl_NewDoubleObj(ffts[i]));
925   }
926 
927   Tcl_SetObjResult(interp, list);
928 
929   if (s->debug > 0) Snack_WriteLog("Exit powerSpectrumCmd\n");
930 
931   return TCL_OK;
932 }
933 
934 #define SNACK_DEFAULT_POWERWINTYPE SNACK_WIN_HAMMING
935 
936 int
powerCmd(Sound * s,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])937 powerCmd(Sound *s, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
938 {
939   double dpreemph = 0.0, dscale = 1.0, dframelen = -1.0;
940   float preemph = 0.0, scale = 1.0;
941   int i, j, n = 0;
942   int channel = 0, winlen = 256;
943   int arg, startpos = 0, endpos = -1, framelen;
944   float *powers = NULL;
945   Tcl_Obj *list;
946   SnackLinkedFileInfo info;
947   SnackWindowType wintype = SNACK_DEFAULT_POWERWINTYPE;
948   static CONST84 char *subOptionStrings[] = {
949     "-start", "-end", "-channel", "-framelength", "-windowlength",
950     "-windowtype", "-preemphasisfactor", "-scale", "-progress", NULL
951   };
952   enum subOptions {
953     START, END, CHANNEL, FRAMELEN, WINDOWLEN, WINTYPE, PREEMPH, SCALE,
954     PROGRESS
955   };
956 
957   if (s->debug > 0) { Snack_WriteLog("Enter powerCmd\n"); }
958 
959   if (s->cmdPtr != NULL) {
960     Tcl_DecrRefCount(s->cmdPtr);
961     s->cmdPtr = NULL;
962   }
963 
964   for (arg = 2; arg < objc; arg += 2) {
965     int index;
966 
967     if (Tcl_GetIndexFromObj(interp, objv[arg], subOptionStrings,
968 			    "option", 0, &index) != TCL_OK) {
969       return TCL_ERROR;
970     }
971 
972     if (arg + 1 == objc) {
973       Tcl_AppendResult(interp, "No argument given for ",
974 		       subOptionStrings[index], " option", (char *) NULL);
975       return TCL_ERROR;
976     }
977 
978     switch ((enum subOptions) index) {
979     case START:
980       {
981 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &startpos) != TCL_OK)
982 	  return TCL_ERROR;
983 	break;
984       }
985     case END:
986       {
987 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &endpos) != TCL_OK)
988 	  return TCL_ERROR;
989 	break;
990       }
991     case CHANNEL:
992       {
993 	char *str = Tcl_GetStringFromObj(objv[arg+1], NULL);
994 	if (GetChannel(interp, str, s->nchannels, &channel) != TCL_OK) {
995 	  return TCL_ERROR;
996 	}
997 	break;
998       }
999     case FRAMELEN:
1000       {
1001 	if (Tcl_GetDoubleFromObj(interp, objv[arg+1], &dframelen) != TCL_OK)
1002 	  return TCL_ERROR;
1003 	break;
1004       }
1005     case WINDOWLEN:
1006       {
1007 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &winlen) != TCL_OK)
1008 	  return TCL_ERROR;
1009 	break;
1010       }
1011     case WINTYPE:
1012       {
1013 	char *str = Tcl_GetStringFromObj(objv[arg+1], NULL);
1014 	if (GetWindowType(interp, str, &wintype) != TCL_OK)
1015 	  return TCL_ERROR;
1016 	break;
1017       }
1018     case PREEMPH:
1019       {
1020 	if (Tcl_GetDoubleFromObj(interp, objv[arg+1], &dpreemph) != TCL_OK)
1021 	  return TCL_ERROR;
1022 	break;
1023       }
1024     case SCALE:
1025       {
1026 	if (Tcl_GetDoubleFromObj(interp, objv[arg+1], &dscale) != TCL_OK)
1027 	  return TCL_ERROR;
1028 	break;
1029       }
1030     case PROGRESS:
1031       {
1032 	char *str = Tcl_GetStringFromObj(objv[arg+1], NULL);
1033 
1034 	if (strlen(str) > 0) {
1035 	  Tcl_IncrRefCount(objv[arg+1]);
1036 	  s->cmdPtr = objv[arg+1];
1037 	}
1038 	break;
1039       }
1040     }
1041   }
1042 
1043   if (winlen < 1) {
1044     Tcl_AppendResult(interp, "-windowlength must be > 0", NULL);
1045     return TCL_ERROR;
1046   }
1047   if (winlen > NMAX) {
1048     char str[10];
1049 
1050     sprintf(str, "%d", NMAX);
1051     Tcl_AppendResult(interp, "-windowlength must be <= ", str, NULL);
1052     return TCL_ERROR;
1053   }
1054 
1055   preemph = (float) dpreemph;
1056   scale   = (float) dscale;
1057 
1058   if (startpos < 0) startpos = 0;
1059   if (endpos >= (s->length - 1) || endpos == -1)
1060     endpos = s->length - 1;
1061   if (startpos > endpos) return TCL_OK;
1062 
1063   if (s->storeType != SOUND_IN_MEMORY) {
1064     if (OpenLinkedFile(s, &info) != TCL_OK) {
1065       return TCL_OK;
1066     }
1067   }
1068 
1069   Snack_InitWindow(hamwin, winlen, winlen, wintype);
1070 
1071   if (dframelen == -1.0) {
1072     dframelen = 0.01;
1073   }
1074   framelen = (int) (dframelen * s->samprate);
1075   n = (endpos - startpos - winlen / 2) / framelen + 1;
1076   if (n < 1) {
1077     n = 1;
1078   }
1079   if (s->nchannels == 1) {
1080     channel = 0;
1081   }
1082 
1083   powers = (float *) ckalloc(sizeof(float) * n);
1084 
1085   Snack_ProgressCallback(s->cmdPtr, interp, "Computing power", 0.0);
1086 
1087   for (j = 0; j < n; j++) {
1088     float power;
1089     int winstart = 0;
1090     if (s->storeType == SOUND_IN_MEMORY) {
1091       if (s->nchannels == 1 || channel != -1) {
1092 	int p = (startpos + j * framelen - winlen/2) * s->nchannels + channel;
1093 
1094 	if (p < 0) p = 0;
1095 	if (p < winlen / 2) winstart = winlen / 2 - p;
1096 	for (i = winstart; i < winlen; i++) {
1097 	  xfft[i] = (float) ((FSAMPLE(s, p + s->nchannels)
1098 			      - preemph * FSAMPLE(s, p))
1099 			     * hamwin[i]);
1100 	  p += s->nchannels;
1101 	}
1102       } else {
1103 	int c;
1104 
1105 	for (i = 0; i < winlen; i++) {
1106 	  xfft[i] = 0.0;
1107 	}
1108 	for (c = 0; c < s->nchannels; c++) {
1109 	  int p = (startpos + j * framelen - winlen/2) * s->nchannels + c;
1110 
1111 	  if (p < 0) p = 0;
1112 	  if (p < winlen / 2) winstart = winlen / 2 - p;
1113 	  for (i = winstart; i < winlen; i++) {
1114 	    xfft[i] += (float) ((FSAMPLE(s, p + s->nchannels)
1115 				 - preemph * FSAMPLE(s, p))
1116 				* hamwin[i]);
1117 	    p += s->nchannels;
1118 	  }
1119 	}
1120 	for (i = 0; i < winlen; i++) {
1121 	  xfft[i] /= s->nchannels;
1122 	}
1123       }
1124     } else { /* storeType != SOUND_IN_MEMORY */
1125       if (s->nchannels == 1 || channel != -1) {
1126 	int p = (startpos + j * framelen - winlen/2) * s->nchannels + channel;
1127 
1128 	if (p < 0) p = 0;
1129 	if (p < winlen / 2) winstart = winlen / 2 - p;
1130 	for (i = winstart; i < winlen; i++) {
1131 	  xfft[i] = (float) ((GetSample(&info, p + s->nchannels)
1132 			      - preemph * GetSample(&info, p))
1133 			     * hamwin[i]);
1134 	  p += s->nchannels;
1135 	}
1136       } else {
1137 	int c;
1138 
1139 	for (i = 0; i < winlen; i++) {
1140 	  xfft[i] = 0.0;
1141 	}
1142 	for (c = 0; c < s->nchannels; c++) {
1143 	  int p = (startpos + j * framelen - winlen/2) * s->nchannels + c;
1144 
1145 	  if (p < 0) p = 0;
1146 	  if (p < winlen / 2) winstart = winlen / 2 - p;
1147 	  for (i = winstart; i < winlen; i++) {
1148 	    xfft[i] += (float) ((GetSample(&info, p + s->nchannels)
1149 				 - preemph * GetSample(&info, p))
1150 				* hamwin[i]);
1151 	    p += s->nchannels;
1152 	  }
1153 	}
1154 	for (i = 0; i < winlen; i++) {
1155 	  xfft[i] /= s->nchannels;
1156 	}
1157       }
1158     }
1159 
1160     power = 0.0f;
1161     for (i = winstart; i < winlen; i++) {
1162       power += xfft[i] * xfft[i];
1163     }
1164     if (power < 1.0) power = 1.0;
1165     powers[j] = (float) (SNACK_DB * log(scale * power / (float)(winlen - winstart)));
1166 
1167     if ((j % 10000) == 9999) {
1168       int res = Snack_ProgressCallback(s->cmdPtr, interp, "Computing power",
1169 				       (double) j / n);
1170       if (res != TCL_OK) {
1171 	ckfree((char *) powers);
1172 	return TCL_ERROR;
1173       }
1174     }
1175   }
1176 
1177   Snack_ProgressCallback(s->cmdPtr, interp, "Computing power", 1.0);
1178 
1179   list = Tcl_NewListObj(0, NULL);
1180   for (i = 0; i < n; i++) {
1181     Tcl_ListObjAppendElement(interp,list, Tcl_NewDoubleObj((double)powers[i]));
1182   }
1183   Tcl_SetObjResult(interp, list);
1184 
1185   if (s->storeType != SOUND_IN_MEMORY) {
1186     CloseLinkedFile(&info);
1187   }
1188   ckfree((char *) powers);
1189 
1190   if (s->debug > 0) { Snack_WriteLog("Exit powerCmd\n"); }
1191 
1192   return TCL_OK;
1193 }
1194 
1195 float
mel(float f)1196 mel(float f)
1197 {
1198   return((float)(1127.0 * log(1.0f + f / 700.0f)));
1199 }
1200 
1201 float regarr[15][5];
1202 
1203 static void
obsv(Sound * s,int nStatCoeffs,float * v,int t,int nReg)1204 obsv(Sound *s, int nStatCoeffs, float *v, int t, int nReg)
1205 {
1206   int i, j, ne, pr;
1207   float sum;
1208 
1209   for (i = 0; i < nStatCoeffs; i++) {
1210     for (j = 1; j < 5; j++) {
1211       regarr[i][j-1] = regarr[i][j];
1212     }
1213   }
1214 
1215   if (t >= 0) {
1216     for (i = 0; i < nStatCoeffs; i++) {
1217       v[i] = FSAMPLE(s, s->nchannels * t + i);
1218     }
1219   }
1220   if (nReg) {
1221     for (i = 0; i < nStatCoeffs; i++) {
1222       sum = 0.0f;
1223       for (j = 1; j <= 2; j++) {
1224 	pr = t + 2 - j;
1225 	if (pr < 0) pr = 0;
1226 	ne = t + 2 + j;
1227 	if (ne >= s->length) ne = s->length - 1;
1228 	sum += j * (FSAMPLE(s, s->nchannels * ne + i)-FSAMPLE(s, s->nchannels * pr + i));
1229       }
1230       regarr[i][4] = sum / 10.0f;
1231       v[i+nStatCoeffs] = regarr[i][2];
1232     }
1233   }
1234   if (nReg > 1) {
1235     for (i = 0; i < nStatCoeffs; i++) {
1236       sum = 0.0f;
1237       for (j = 1; j <= 2; j++) {
1238 	pr = 2 - j;
1239 	if (t == 0) pr = 2;
1240 	if (t == 1 && j == 2) pr = 1;
1241 	ne = 2 + j;
1242 	if (t == s->length-1) ne = 2;
1243 	if (t == s->length-2 && j == 2) ne = 3;
1244 	sum += j * (regarr[i][ne]-regarr[i][pr]);
1245       }
1246       v[i+2*nStatCoeffs] = sum / 10.0f;
1247     }
1248   }
1249 
1250   /*  for (i = 0; i < 13; i++) { printf("%f ",v[i]); } printf("\n");
1251       for (i=13; i < 26; i++) { printf("%f ",v[i]); } printf("\n");
1252       for (i=26; i < 39; i++) { printf("%f ",v[i]); } printf("\n");*/
1253 }
1254 
1255 int
speaturesCmd(Sound * s,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1256 speaturesCmd(Sound *s, Tcl_Interp *interp, int objc,
1257 	     Tcl_Obj *CONST objv[])
1258 {
1259   double tmpdouble = 0.0, dframelen = 0.01;
1260   float preemph = 0.97f, lowcut = 0.0f, highcut = (float) (s->samprate / 2.0);
1261   int i, j, k, n = 1, arg;
1262   int channel = 0;
1263   double dwinlen = 0.0256;
1264   int winlen;
1265   int fftlen = 2;
1266   int startpos = 0, endpos = -1, framelen;
1267   SnackLinkedFileInfo info;
1268   SnackWindowType wintype = SNACK_DEFAULT_DBPWINTYPE;
1269   static CONST84 char *subOptionStrings[] = {
1270     "-start", "-end", "-channel", "-framelength", "-windowlength",
1271     "-preemphasisfactor", "-windowtype",
1272     "-nchannels", "-ncoeff", "-cepstrallifter", "-energy", "-zeromean",
1273     "-zerothcepstralcoeff", "-lowcutoff", "-highcutoff",
1274     "-regressionorder", NULL
1275   };
1276   enum subOptions {
1277     START, END, CHANNEL, FRAMELEN, WINDOWLEN, PREEMPH, WINTYPE,
1278     NCHANNELS, NCOEFF, CEPLIFT, ENERGY, ZEROMEAN, ZEROTHCC,
1279     LOWCUT, HIGHCUT, REGRESSION
1280   };
1281   float *outarr;
1282   int numchannels = 20, ncoeff = 12;
1283   int lowInd, highInd;
1284   float lowMel, highMel;
1285   float cf[40];
1286   float fb[40];
1287   float lifter[20];
1288   float mfcc[20];
1289   int   map[512];
1290   float fbw[512];
1291   float frame[1024];
1292   double ceplifter = 22.0;
1293   int doEnergy = 0, doZeroMean = 0, doZerothCC = 0, regression = 0;
1294   int vectorLength, regVecLen;
1295   Sound *outsnd;
1296   char *string;
1297 
1298   if (s->debug > 0) Snack_WriteLog("Enter speaturesCmd\n");
1299 
1300   string = Tcl_GetStringFromObj(objv[2], NULL);
1301 
1302   if ((outsnd = Snack_GetSound(interp, string)) == NULL) {
1303     return TCL_ERROR;
1304   }
1305 
1306   for (arg = 3; arg < objc; arg += 2) {
1307     int index;
1308 
1309     if (Tcl_GetIndexFromObj(interp, objv[arg], subOptionStrings,
1310 			    "option", 0, &index) != TCL_OK) {
1311       return TCL_ERROR;
1312     }
1313 
1314     if (arg + 1 == objc) {
1315       Tcl_AppendResult(interp, "No argument given for ",
1316 		       subOptionStrings[index], " option", (char *) NULL);
1317       return TCL_ERROR;
1318     }
1319 
1320     switch ((enum subOptions) index) {
1321     case START:
1322       {
1323 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &startpos) != TCL_OK)
1324 	  return TCL_ERROR;
1325 	break;
1326       }
1327     case END:
1328       {
1329 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &endpos) != TCL_OK)
1330 	  return TCL_ERROR;
1331 	break;
1332       }
1333     case CHANNEL:
1334       {
1335 	char *str = Tcl_GetStringFromObj(objv[arg+1], NULL);
1336 	if (GetChannel(interp, str, s->nchannels, &channel) != TCL_OK) {
1337 	  return TCL_ERROR;
1338 	}
1339 	break;
1340       }
1341     case FRAMELEN:
1342       {
1343 	if (Tcl_GetDoubleFromObj(interp, objv[arg+1], &dframelen) != TCL_OK)
1344 	  return TCL_ERROR;
1345 	break;
1346       }
1347     case WINDOWLEN:
1348       {
1349 	if (Tcl_GetDoubleFromObj(interp, objv[arg+1], &dwinlen) != TCL_OK)
1350 	  return TCL_ERROR;
1351 	break;
1352       }
1353     case PREEMPH:
1354       {
1355 	if (Tcl_GetDoubleFromObj(interp, objv[arg+1], &tmpdouble) != TCL_OK)
1356 	  return TCL_ERROR;
1357 	preemph = (float) tmpdouble;
1358 	break;
1359       }
1360     case WINTYPE:
1361       {
1362 	char *str = Tcl_GetStringFromObj(objv[arg+1], NULL);
1363 	if (GetWindowType(interp, str, &wintype) != TCL_OK)
1364 	  return TCL_ERROR;
1365 	break;
1366       }
1367     case NCHANNELS:
1368       {
1369 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &numchannels) != TCL_OK)
1370 	  return TCL_ERROR;
1371 	break;
1372       }
1373     case NCOEFF:
1374       {
1375 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &ncoeff) != TCL_OK)
1376 	  return TCL_ERROR;
1377 	break;
1378       }
1379     case CEPLIFT:
1380       {
1381 	if (Tcl_GetDoubleFromObj(interp, objv[arg+1], &ceplifter) != TCL_OK)
1382 	  return TCL_ERROR;
1383 	break;
1384       }
1385     case ENERGY:
1386       {
1387 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &doEnergy) != TCL_OK)
1388 	  return TCL_ERROR;
1389 	break;
1390       }
1391     case ZEROMEAN:
1392       {
1393 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &doZeroMean) != TCL_OK)
1394 	  return TCL_ERROR;
1395 	break;
1396       }
1397     case ZEROTHCC:
1398       {
1399 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &doZerothCC) != TCL_OK)
1400 	  return TCL_ERROR;
1401 	break;
1402       }
1403     case LOWCUT:
1404       {
1405 	if (Tcl_GetDoubleFromObj(interp, objv[arg+1], &tmpdouble) != TCL_OK)
1406 	  return TCL_ERROR;
1407 	lowcut = (float) tmpdouble;
1408 	break;
1409       }
1410     case HIGHCUT:
1411       {
1412 	if (Tcl_GetDoubleFromObj(interp, objv[arg+1], &tmpdouble) != TCL_OK)
1413 	  return TCL_ERROR;
1414 	highcut = (float) tmpdouble;
1415 	break;
1416       }
1417     case REGRESSION:
1418       {
1419 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &regression) != TCL_OK)
1420 	  return TCL_ERROR;
1421 	break;
1422       }
1423     }
1424   }
1425 
1426   winlen = (int) (s->samprate * dwinlen);
1427 
1428   if ((startpos < 0 || startpos > s->length - fftlen) && s->length > 0) {
1429     Tcl_AppendResult(interp, "FFT window out of bounds", NULL);
1430     return TCL_ERROR;
1431   }
1432 
1433   if (startpos < 0) startpos = 0;
1434   if (endpos >= (s->length - 1) || endpos == -1)
1435     endpos = s->length - 1;
1436   if (startpos > endpos) return TCL_OK;
1437 
1438   if (s->storeType != SOUND_IN_MEMORY) {
1439     if (OpenLinkedFile(s, &info) != TCL_OK) {
1440       return TCL_OK;
1441     }
1442   }
1443 
1444   while (fftlen < winlen) fftlen *= 2;
1445 
1446   framelen = (int) (dframelen * s->samprate);
1447   n = (endpos - startpos - winlen) / framelen + 1;
1448   if (n < 1) {
1449     n = 0;
1450   }
1451 
1452   vectorLength  = ncoeff;
1453   if (doEnergy) vectorLength++;
1454   if (doZerothCC) vectorLength++;
1455 
1456   outarr = (float *) ckalloc(sizeof(float) * vectorLength * n + 1);
1457 
1458   lowMel = (float) mel(lowcut);
1459   lowInd = (int) ((lowcut * fftlen / s->samprate) + 1.5);
1460   if (lowInd < 1) lowInd = 1;
1461   highMel = (float) mel(highcut);
1462   highInd = (int) ((highcut * fftlen / s->samprate) - 0.5);
1463   if (highInd > fftlen / 2 - 1) highInd = fftlen / 2 - 1;
1464 
1465   for (i = 0; i < numchannels + 1; i++) {
1466     cf[i] = ((float) (i+1) / (numchannels+1)) * (highMel - lowMel) + lowMel;
1467   }
1468 
1469   for (i = 0; i < fftlen / 2; i++) {
1470     float melInd = mel((float) i * s->samprate / fftlen);
1471     float new;
1472     int ch = 0;
1473 
1474     if (i < lowInd || i > highInd) {
1475       map[i] = -1;
1476       new = 0.0;
1477     } else {
1478       while (cf[ch] < melInd && ch < numchannels + 1) ++ch;
1479       map[i] = ch - 1;
1480       if (ch - 1 >= 0) {
1481         new = ((cf[ch] - mel((float) i*s->samprate/fftlen)) /
1482 	       (cf[ch] - cf[ch-1]));
1483       } else {
1484 	new = (cf[0] - mel((float) i*s->samprate/fftlen)) / (cf[0] - lowMel);
1485       }
1486     }
1487     fbw[i] = new;
1488   }
1489 
1490   for (i = 0; i <= ncoeff; i++) {
1491     lifter[i] = (float)(1.0 + ceplifter /2.0 * sin(SNACK_PI * i / ceplifter));
1492   }
1493 
1494   Snack_InitWindow(hamwin, winlen, fftlen, wintype);
1495 
1496   Snack_InitFFT(fftlen);
1497 
1498   for (j = 0; j < n; j++) {
1499     float sum = 0.0;
1500 
1501     for (i = 0; i < fftlen; i++) {
1502       frame[i] = 0.0f;
1503     }
1504     if (s->storeType == SOUND_IN_MEMORY) {
1505       if (s->nchannels == 1 || channel != -1) {
1506 	  int p = (startpos + j * framelen) * s->nchannels + channel;
1507 
1508 	  for (i = 0; i < winlen; i++) {
1509 	    frame[i] = FSAMPLE(s, p);
1510 	    p += s->nchannels;
1511 	  }
1512       } else {
1513 	int c;
1514 
1515 	for (c = 0; c < s->nchannels; c++) {
1516 	  int p = (startpos + j * framelen) * s->nchannels + c;
1517 
1518 	  for (i = 0; i < winlen; i++) {
1519 	    frame[i] += FSAMPLE(s, p);
1520 	    p += s->nchannels;
1521 	  }
1522 	}
1523 	for (i = 0; i < winlen; i++) {
1524 	  frame[i] /= s->nchannels;
1525 	}
1526       }
1527     } else { /* storeType != SOUND_IN_MEMORY */
1528       if (s->nchannels == 1 || channel != -1) {
1529 	int p = (startpos + j * framelen) * s->nchannels + channel;
1530 
1531 	for (i = 0; i < winlen; i++) {
1532 	  frame[i] = GetSample(&info, p);
1533 	  p += s->nchannels;
1534 	}
1535       } else {
1536 	int c;
1537 
1538 	for (c = 0; c < s->nchannels; c++) {
1539 	  int p = (startpos + j * framelen) * s->nchannels + c;
1540 
1541 	  for (i = 0; i < winlen; i++) {
1542 	    frame[i] += GetSample(&info, p);
1543 	    p += s->nchannels;
1544 	  }
1545 	}
1546 	for (i = 0; i < winlen; i++) {
1547 	  frame[i] /= s->nchannels;
1548 	}
1549       }
1550     }
1551 
1552     for (i = 0; i < winlen; i++) {
1553       float sample = frame[i];
1554       sum += sample * sample;
1555     }
1556     if (sum < 1.0) sum = 1.0f;
1557 
1558     xfft[0] = (float) (1.0f - preemph) * frame[0] * hamwin[0];
1559     for (i = 1; i < fftlen; i++) {
1560       xfft[i] = (float) ((frame[i] - preemph * frame[i - 1]) * hamwin[i]);
1561     }
1562 
1563     Snack_PowerSpectrum(xfft);
1564 
1565     for (i = 0; i < numchannels + 1; i++) {
1566       fb[i] = 0.0;
1567     }
1568     for (i = lowInd; i <= highInd; i++) {
1569       float e = (float) (0.5 * sqrt(xfft[i]));
1570       int bin = map[i];
1571       float we = fbw[i] * e;
1572 
1573       if (bin > -1) fb[bin] += we;
1574       if (bin < numchannels - 1) fb[bin + 1] += e - we;
1575     }
1576     for (i = 0; i < numchannels; i++) {
1577       if (fb[i] < 1.0) {
1578 	fb[i] = 0.0f;
1579       } else {
1580 	fb[i] = (float) log(fb[i]);
1581       }
1582     }
1583 
1584     for (i = 0; i < ncoeff; i++) {
1585       mfcc[i] = 0.0f;
1586       for (k = 0; k < numchannels; k++) {
1587 	mfcc[i] += (float) (fb[k] * cos(SNACK_PI * (i + 1) / numchannels *
1588 					(k + 0.5f)));
1589       }
1590       mfcc[i] *= (float) (sqrt(2.0f / numchannels) * lifter[i + 1]);
1591     }
1592     mfcc[ncoeff] = (float) log(sum);
1593     for (i = 0; i < ncoeff + 1; i++) {
1594       outarr[j * vectorLength + i] = mfcc[i];
1595     }
1596     if (doZerothCC) {
1597       float sum = 0.0f;
1598 
1599       for (i = 0; i < numchannels; i++) {
1600 	sum += fb[i];
1601       }
1602       outarr[j*vectorLength+ncoeff] = (float) (sum * sqrt(2.0 / numchannels));
1603     }
1604   } /* for (j = 0;... */
1605 
1606   if (doZeroMean) {
1607     for (i = 0; i < ncoeff; i++) {
1608       float sum = 0.0f, offset;
1609 
1610       for (j = 0; j < n; j++) {
1611 	sum += outarr[j * vectorLength + i];
1612       }
1613       offset = sum / n;
1614       for (j = 0; j < n; j++) {
1615 	outarr[j * vectorLength + i] -= offset;
1616       }
1617     }
1618   }
1619   if (doEnergy) {
1620     float max = -1000000.0f, min;
1621 
1622     for (i = ncoeff; i < n * vectorLength; i += vectorLength) {
1623       if (outarr[i] > max) max = outarr[i];
1624     }
1625     min = (float) (max - 50.0f * log(10.0f) / 10.0f);
1626     for (i = ncoeff; i < n * vectorLength; i += vectorLength) {
1627       if (outarr[i] < min) outarr[i] = min;
1628       outarr[i] = 1.0f - 0.1f * (max - outarr[i]);
1629     }
1630   }
1631 
1632   if (s->storeType != SOUND_IN_MEMORY) {
1633     CloseLinkedFile(&info);
1634   }
1635   regVecLen = vectorLength * (regression + 1);
1636   if (outsnd->nchannels < regVecLen) {
1637     outsnd->nchannels = regVecLen;
1638   }
1639   if (Snack_ResizeSoundStorage(outsnd, n) != TCL_OK) {
1640     return TCL_ERROR;
1641   }
1642   outsnd->length = n;
1643 
1644   for (i = 0; i < n; i++) {
1645     for (k = 0; k < vectorLength; k++) {
1646       FSAMPLE(outsnd, i*outsnd->nchannels+k) = outarr[i*vectorLength+k];
1647     }
1648   }
1649 
1650   if (regression) {
1651     float obs[45];
1652 
1653     for (k = -2; k < 2; k++) {
1654       for (i = 0; i < vectorLength; i++) {
1655 	float sum = 0.0f;
1656 	for (j = 1; j <= 2; j++) {
1657 	  int pr = k - j;
1658 	  int ne = k + j;
1659 	  if (pr < 0) pr = 0;
1660 	  if (ne < 0) ne = 0;
1661 	  if (ne >= n) ne = n - 1;
1662 	  sum += j * (FSAMPLE(outsnd, outsnd->nchannels * ne + i)
1663 		      - FSAMPLE(outsnd, outsnd->nchannels * pr + i));
1664 	}
1665 	regarr[i][k+3] = sum / 10.0f;
1666       }
1667     }
1668     for (i = 0; i < n; i++) {
1669       obsv(outsnd, vectorLength, obs, i, regression);
1670       for (k = vectorLength; k < regVecLen; k++) {
1671 	FSAMPLE(outsnd, i * outsnd->nchannels + k) = obs[k];
1672       }
1673     }
1674   }
1675 
1676   ckfree((char*) outarr);
1677 
1678   outsnd->encoding = SNACK_FLOAT;
1679   outsnd->samprate = (int) (1.0 / dframelen + 0.5);
1680   Snack_UpdateExtremes(outsnd, 0, n, SNACK_NEW_SOUND);
1681   Snack_ExecCallbacks(outsnd, SNACK_NEW_SOUND);
1682 
1683   if (s->debug > 0) Snack_WriteLog("Exit speaturesCmd\n");
1684 
1685   return TCL_OK;
1686 }
1687 
1688 extern int cGet_f0(Sound *s, Tcl_Interp *interp, float **outlist, int *length);
1689 
1690 static int
searchZX(Sound * s,int pos)1691 searchZX(Sound *s, int pos)
1692 {
1693   int i, j;
1694 
1695   for(i = 0; i < 20000; i++) {
1696     j = pos + i;
1697     if (j>0 && j<s->length && FSAMPLE(s, j-1) < 0.0 && FSAMPLE(s, j) >= 0.0) {
1698       return(j);
1699     }
1700     j = pos - i;
1701     if (j>0 && j<s->length && FSAMPLE(s, j-1) < 0.0 && FSAMPLE(s, j) >= 0.0) {
1702       return(j);
1703     }
1704   }
1705   return(pos);
1706 }
1707 
1708 int
stretchCmd(Sound * s,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1709 stretchCmd(Sound *s, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
1710 {
1711   int i, ind, last, start, arg, segOnly = 0;
1712   static CONST84 char *subOptionStrings[] = {
1713     "-segments", NULL
1714   };
1715   enum subOptions {
1716     SEGMENTS
1717   };
1718   float *cPitchList;
1719   int *segs;
1720   int *sege;
1721   int cPitchLength = 0;
1722   int skip = s->samprate / 100;
1723 
1724   if (s->debug > 0) Snack_WriteLog("Enter stretchCmd\n");
1725 
1726   for (arg = 2; arg < objc; arg += 2) {
1727     int index;
1728 
1729     if (Tcl_GetIndexFromObj(interp, objv[arg], subOptionStrings,
1730 			    "option", 0, &index) != TCL_OK) {
1731       return TCL_ERROR;
1732     }
1733 
1734     if (arg + 1 == objc) {
1735       Tcl_AppendResult(interp, "No argument given for ",
1736 		       subOptionStrings[index], " option", (char *) NULL);
1737       return TCL_ERROR;
1738     }
1739 
1740     switch ((enum subOptions) index) {
1741     case SEGMENTS:
1742       {
1743 	if (Tcl_GetIntFromObj(interp, objv[arg+1], &segOnly) != TCL_OK)
1744 	  return TCL_ERROR;
1745 	break;
1746       }
1747     }
1748   }
1749 
1750   if (s->length == 0) {
1751     return TCL_OK;
1752   }
1753 
1754   cGet_f0(s, interp, &cPitchList, &cPitchLength);
1755 
1756   ind = 0;
1757   start = 0;
1758   last = 0;
1759   segs = (int *) ckalloc(sizeof(int) * cPitchLength * 2);
1760   sege = (int *) ckalloc(sizeof(int) * cPitchLength * 2);
1761 
1762   if (s->length < 8000 && cPitchList[0] == 0.0 && cPitchList[1] == 0.0 && \
1763       cPitchList[2] == 0.0) {
1764   } else {
1765     for (i = 1; i < s->length; i++) {
1766       int pitchListIndex = (int) (i/(float)skip+.5);
1767       float point;
1768 
1769       if (pitchListIndex >= cPitchLength) {
1770 	pitchListIndex = cPitchLength - 1;
1771       }
1772       if (ind >= cPitchLength*2) {
1773 	ind = cPitchLength * 2 - 1;
1774       }
1775       point = cPitchList[pitchListIndex];
1776 
1777       if (point == 0.0) {
1778 	i += 9;
1779 	continue;
1780       }
1781       if (start == 0) {
1782 	i = searchZX(s, (int)(i+s->samprate/point));
1783 	segs[ind] = start;
1784 	sege[ind] = i;
1785 	start = i;
1786 	ind++;
1787       } else {
1788 	i = searchZX(s, (int)(i+s->samprate/point));
1789 	if (i == last) {
1790 	  int j = i + 10;
1791 	  while (last == i) {
1792 	    i = searchZX(s, j);
1793 	    j += 10;
1794 	  }
1795 	}
1796 	/* this is needed to make stretch.test happy, can surely be improved*/
1797 	if (i - last < (int)(0.8*s->samprate/point) && s->length - i < 200) {
1798 	  i = -1;
1799 	}
1800 	last = i;
1801 	if (i > 0) {
1802 	  segs[ind] = start;
1803 	  sege[ind] = i;
1804 	  start = i;
1805 	  ind++;
1806 	} else {
1807 	  segs[ind] = start;
1808 	  sege[ind] = s->length;
1809 	  start = s->length;
1810 	  ind++;
1811 	  break;
1812 	}
1813       }
1814     }
1815     if (ind == 0) {
1816       segs[ind] = start;
1817       ind = 1;
1818     }
1819     sege[ind-1] = s->length-1;
1820   }
1821 
1822   if (segOnly) {
1823     Tcl_Obj *list = Tcl_NewListObj(0, NULL);
1824     for (i = 0; i < ind; i++) {
1825       Tcl_ListObjAppendElement(interp, list,
1826 			       Tcl_NewIntObj((int) segs[i]));
1827     }
1828     Tcl_SetObjResult(interp, list);
1829     ckfree((char *)segs);
1830     ckfree((char *)sege);
1831     ckfree((char *)cPitchList);
1832 
1833     if (s->debug > 0) Snack_WriteLog("Exit stretchCmd\n");
1834 
1835     return TCL_OK;
1836   }
1837 
1838   return TCL_OK;
1839 }
1840 
1841 int
joinCmd(Sound * s,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1842 joinCmd(Sound *s, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
1843 {
1844   return TCL_OK;
1845 }
1846 
1847 int
ocCmd(Sound * s,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1848 ocCmd(Sound *s, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
1849 {
1850   return TCL_OK;
1851 }
1852 
1853 int
fitCmd(Sound * s,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1854 fitCmd(Sound *s, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
1855 {
1856   return TCL_OK;
1857 }
1858 
1859 #define  PI   3.141592653589793
1860 
1861 double singtabf[32];
1862 double singtabb[32];
1863 float futdat[512+10];
1864 float smerg[512+10];
1865 
1866 int
inaCmd(Sound * s,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1867 inaCmd(Sound *s, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
1868 {
1869   float a1[32],a2[32],mn[32];
1870   int i,j,noli,nofpts=512,shobeg=2,nosing;
1871   int start;
1872   int plLen = 0;
1873   Tcl_Obj** plElems;
1874   Tcl_Obj *list, *listInv, *listFlow;
1875 
1876   if (Tcl_GetIntFromObj(interp, objv[2], &start) != TCL_OK) return TCL_ERROR;
1877 
1878   if (Tcl_ListObjGetElements(interp, objv[3], &plLen, &plElems) != TCL_OK)
1879     return TCL_ERROR;
1880 
1881   nosing = plLen / 2;
1882 
1883   for (i=0;i<nosing;i++) {
1884     if (Tcl_GetDoubleFromObj(interp, plElems[i], &singtabf[i]) != TCL_OK)
1885       return TCL_ERROR;
1886     if (Tcl_GetDoubleFromObj(interp, plElems[i+nosing], &singtabb[i]) != TCL_OK)
1887       return TCL_ERROR;
1888   }
1889 
1890   for (i = 0; i < nofpts; i++) {
1891     futdat[i] = FSAMPLE(s, start + i);
1892   }
1893   for (i = nofpts; i < nofpts+4; i++) {
1894     futdat[i] = 0.0f;
1895   }
1896 
1897   noli=0;                         /* zero 2nd order filter */
1898   for (i=0;i<nosing;i++) {
1899     if ((singtabf[i]>0.0) && (singtabb[i]>0.0)) {
1900       a2[noli]=(float)exp((double)(-PI*singtabb[i]/s->samprate));
1901       a1[noli]= -2*a2[noli]*(float)cos((double)(2.0*PI*singtabf[i]/s->samprate));
1902       a2[noli]=a2[noli]*a2[noli];
1903       mn[noli]=1.0f/(1.0f+a1[noli]+a2[noli]);
1904       a1[noli]=mn[noli]*a1[noli];
1905       a2[noli]=mn[noli]*a2[noli];
1906       noli++;
1907     }
1908   }
1909   for (j=0;j<noli;j++) {
1910       for (i=shobeg+nofpts;i>=shobeg;i--) {
1911         futdat[i]=mn[j]*futdat[i]+a1[j]*futdat[i-1]+a2[j]*futdat[i-2];
1912       }
1913   }
1914 
1915   noli=0;                         /* pole 2nd order filter */
1916   for (i=0;i<nosing;i++) {
1917     if ((singtabf[i]>0.0) && (singtabb[i]<0.0)) {
1918       a2[noli]=(float)exp((double)(PI*singtabb[i]/s->samprate));
1919       a1[noli]= -2*a2[noli]*(float)cos((double)(2.0*PI*singtabf[i]/s->samprate));
1920       a2[noli]=a2[noli]*a2[noli];
1921       mn[noli]=1.0f+a1[noli]+a2[noli];
1922       noli++;
1923     }
1924   }
1925   for (j=0;j<noli;j++) {
1926       for (i=shobeg;i<shobeg+nofpts;i++) {
1927         futdat[i]=mn[j]*futdat[i]-a1[j]*futdat[i-1]-a2[j]*futdat[i-2];
1928       }
1929   }
1930 
1931   noli=0;                         /* pole 1st order filter */
1932   for (i=0;i<nosing;i++) {
1933     if ((singtabf[i]==0.0) && (singtabb[i]<0.0)) {
1934       a1[noli]= -(float)exp((double)(PI*singtabb[i]/s->samprate));
1935       mn[noli]=1.0f+a1[noli];
1936       noli++;
1937     }
1938   }
1939   for (j=0;j<noli;j++) {
1940       for (i=shobeg;i<shobeg+nofpts;i++) {
1941         futdat[i]=mn[j]*(futdat[i]-futdat[i-1])+futdat[i-1];
1942       }
1943   }
1944   /*shobeg = 1;*/ /* ugly fix, think about this*/
1945   smerg[shobeg-1]=0.0;
1946   for (i=shobeg;i<shobeg+nofpts;i++) {
1947     smerg[i]=(futdat[i]-smerg[i-1])/32.0f+smerg[i-1];
1948   }
1949 
1950   list     = Tcl_NewListObj(0, NULL);
1951   listInv  = Tcl_NewListObj(0, NULL);
1952   listFlow = Tcl_NewListObj(0, NULL);
1953   for (i = shobeg; i < shobeg+nofpts; i++) {
1954     Tcl_ListObjAppendElement(interp, listInv, Tcl_NewDoubleObj(futdat[i]));
1955     Tcl_ListObjAppendElement(interp, listFlow, Tcl_NewDoubleObj(smerg[i]));
1956   }
1957   Tcl_ListObjAppendElement(interp, list, listInv);
1958   Tcl_ListObjAppendElement(interp, list, listFlow);
1959   Tcl_SetObjResult(interp, list);
1960 
1961   return TCL_OK;
1962 }
1963 
1964 Tcl_HashTable *arHashTable;
1965 
1966 int
Snack_arCmd(ClientData cdata,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1967 Snack_arCmd(ClientData cdata, Tcl_Interp *interp, int objc,
1968 		Tcl_Obj *CONST objv[])
1969 {
1970   return TCL_OK;
1971 }
1972 
1973 void
Snack_arDeleteCmd(ClientData clientData)1974 Snack_arDeleteCmd(ClientData clientData)
1975 {
1976 }
1977 
1978 int
vpCmd(Sound * s,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1979 vpCmd(Sound *s, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
1980 {
1981   return TCL_OK;
1982 }
1983 
1984 Tcl_HashTable *hsetHashTable;
1985 
1986 int
Snack_HSetCmd(ClientData cdata,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1987 Snack_HSetCmd(ClientData cdata, Tcl_Interp *interp, int objc,
1988 		Tcl_Obj *CONST objv[])
1989 {
1990 
1991   return TCL_OK;
1992 }
1993 
1994 void
Snack_HSetDeleteCmd(ClientData clientData)1995 Snack_HSetDeleteCmd(ClientData clientData)
1996 {
1997 }
1998 
1999 int
alCmd(Sound * s,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])2000 alCmd(Sound *s, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
2001 {
2002   return TCL_OK;
2003 }
2004 
2005 int
isynCmd(ClientData cdata,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])2006 isynCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
2007 {
2008   return TCL_OK;
2009 }
2010 
2011 int
osynCmd(ClientData cdata,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])2012 osynCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
2013 {
2014   return TCL_OK;
2015 }
2016