smooth.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. /*
  2. * Copyright (c) 1983-2013 Trevor Wishart and Composers Desktop Project Ltd
  3. * http://www.trevorwishart.co.uk
  4. * http://www.composersdesktop.com
  5. *
  6. This file is part of the CDP System.
  7. The CDP System is free software; you can redistribute it
  8. and/or modify it under the terms of the GNU Lesser General Public
  9. License as published by the Free Software Foundation; either
  10. version 2.1 of the License, or (at your option) any later version.
  11. The CDP System is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. GNU Lesser General Public License for more details.
  15. You should have received a copy of the GNU Lesser General Public
  16. License along with the CDP System; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  18. 02111-1307 USA
  19. *
  20. */
  21. /*
  22. * Take a set of amp-frq data in the correct range,
  23. * and use linear interpolation to replot curve to cover
  24. * every 1/64 tone over whole range.
  25. * Where, frq ratio between adjacent chans becomes < 1/64 tone
  26. * use chan centre frqs.
  27. */
  28. #define PSEUDOCENT (1.0009) // approx 1/64th of a tone
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <structures.h>
  32. #include <tkglobals.h>
  33. #include <pnames.h>
  34. #include <filetype.h>
  35. #include <processno.h>
  36. #include <modeno.h>
  37. #include <logic.h>
  38. #include <globcon.h>
  39. #include <cdpmain.h>
  40. #include <math.h>
  41. #include <mixxcon.h>
  42. #include <osbind.h>
  43. #include <science.h>
  44. #include <ctype.h>
  45. #include <sfsys.h>
  46. #include <string.h>
  47. #include <srates.h>
  48. int sloom = 0;
  49. int sloombatch = 0;
  50. const char* cdp_version = "6.1.0";
  51. char errstr[2400]; //RWD need this
  52. //CDP LIB REPLACEMENTS
  53. static int allocate_output_array(double **outarray, int *outcnt, double chwidth, double halfchwidth, double nyquist);
  54. //static int get_tk_cmdline_word(int *cmdlinecnt,char ***cmdline,char *q);
  55. static int handle_the_special_data(FILE *fpi, double **parray, int *itemcnt);
  56. static int test_the_special_data(double *parray, int itemcnt, double nyquist);
  57. static int smooth(double *parray,int itemcnt,double *outarray,int *outcnt,double chwidth,double halfchwidth,double nyquist,int arraycnt);
  58. static int dove(double *parray,int itemcnt,double *outarray,int outcnt);
  59. static int read_value_from_array(double *outval,double thisfrq,double *brk,double **brkptr,int brksize,double *firstval,double *lastval,double *lastind,int *brkinit);
  60. static int get_float_with_e_from_within_string(char **str,double *val);
  61. /**************************************** MAIN *********************************************/
  62. int main(int argc,char *argv[])
  63. {
  64. FILE *fpi, *fpo;
  65. int k, OK = 0, dovetail = 0;
  66. int exit_status, srate, pointcnt, clength;
  67. double chwidth, halfchwidth, nyquist, *parray, *outarray;
  68. int itemcnt, outcnt, arraycnt;
  69. char temp[200], temp2[200];
  70. if(argc < 5 || argc > 6) {
  71. usage1();
  72. return(FAILED);
  73. }
  74. if((fpi = fopen(argv[1],"r")) == NULL) {
  75. fprintf(stderr,"CANNOT OPEN DATAFILE %s\n",argv[1]);
  76. return(FAILED);
  77. }
  78. if((exit_status = handle_the_special_data(fpi,&parray,&itemcnt))<0)
  79. return(FAILED);
  80. arraycnt = itemcnt/2;
  81. if(sscanf(argv[3],"%d",&pointcnt) != 1) {
  82. fprintf(stderr,"CANNOT READ POINTCNT (%s)\n",argv[3]);
  83. return(FAILED);
  84. }
  85. OK = 0;
  86. k = 1;
  87. while(k < pointcnt) {
  88. k *= 2;
  89. if(k == pointcnt){
  90. OK= 1;
  91. break;
  92. }
  93. }
  94. if(!OK) {
  95. fprintf(stderr,"ANALYSIS POINTS PARAMETER MUST BE A POWER OF TWO.\n");
  96. return(FAILED);
  97. }
  98. if(sscanf(argv[4],"%d",&srate) != 1) {
  99. fprintf(stderr,"CANNOT READ SAMPLE RATE (%s)\n",argv[3]);
  100. return(FAILED);
  101. }
  102. if(srate < 44100 || BAD_SR(srate)) {
  103. fprintf(stderr,"INVALID SAMPLE RATE ENTERED (44100,48000,88200,96000 only).\n");
  104. return(DATA_ERROR);
  105. }
  106. if(argc == 6) {
  107. if(strcmp(argv[5],"-s")) {
  108. fprintf(stderr,"UNKNOWN FINAL PARAMETER (%s).\n",argv[5]);
  109. return(DATA_ERROR);
  110. }
  111. dovetail = 1;
  112. }
  113. clength = (pointcnt + 2)/2;
  114. nyquist = (double)(srate / 2);
  115. chwidth = nyquist/(double)clength;
  116. halfchwidth = chwidth / 2;
  117. if((exit_status = test_the_special_data(parray,itemcnt,nyquist))<0)
  118. return(FAILED);
  119. if((exit_status = allocate_output_array(&outarray,&outcnt,chwidth,halfchwidth,nyquist))<0)
  120. return(FAILED);
  121. if((fpo = fopen(argv[2],"w")) == NULL) {
  122. fprintf(stderr,"CANNOT OPEN OUTPUT DATAFILE %s\n",argv[2]);
  123. return(FAILED);
  124. }
  125. if((exit_status = smooth(parray,itemcnt,outarray,&outcnt,chwidth,halfchwidth,nyquist,arraycnt)) < 0)
  126. return(FAILED);
  127. if(dovetail) {
  128. if((exit_status = dove(parray,itemcnt,outarray,outcnt)) < 0)
  129. return(FAILED);
  130. }
  131. k = 0;
  132. for(k = 0;k < outcnt;k+=2) {
  133. sprintf(temp,"%lf",outarray[k]);
  134. sprintf(temp2,"%lf",outarray[k+1]);
  135. strcat(temp2,"\n");
  136. strcat(temp,"\t");
  137. strcat(temp,temp2);
  138. fputs(temp,fpo);
  139. }
  140. if(fclose(fpo)<0) {
  141. fprintf(stderr,"WARNING: Failed to close output data file %s.\n",argv[2]);
  142. }
  143. return(SUCCEEDED);
  144. }
  145. /******************************** USAGE1 ********************************/
  146. int usage1(void)
  147. {
  148. fprintf(stderr,
  149. "USAGE: smooth datafile outdatafile pointcnt srate [-s]\n"
  150. "\n"
  151. "Generate smoothed curve from input data re spectrum.\n"
  152. "\n"
  153. "DATAFILE textfile of frq/amp pairs.\n"
  154. "OUTDATAFILE textfile of smoothed data.\n"
  155. "POINTCNT no of analysis points required in handling data.\n"
  156. "SRATE samplerate of eventual sound output.\n"
  157. "-s if spectral amp falls to zero before the nyquist,\n"
  158. " keep zeroed end of spectrum at zero.\n."
  159. "\n");
  160. return(USAGE_ONLY);
  161. }
  162. /************************** HANDLE_THE_SPECIAL_DATA **********************************/
  163. int handle_the_special_data(FILE *fpi, double **parray, int *itemcnt)
  164. {
  165. int cnt, linecnt;
  166. double *p, dummy;
  167. char temp[200], *q;
  168. cnt = 0;
  169. p = &dummy;
  170. while(fgets(temp,200,fpi)==temp) {
  171. q = temp;
  172. if(*q == ';') // Allow comments in file
  173. continue;
  174. while(get_float_with_e_from_within_string(&q,p))
  175. cnt++;
  176. }
  177. if(ODD(cnt)) {
  178. fprintf(stderr,"Data not paired correctly in input data file\n");
  179. return(DATA_ERROR);
  180. }
  181. if(cnt == 0) {
  182. fprintf(stderr,"No data in input data file\n");
  183. return(DATA_ERROR);
  184. }
  185. if((*parray = (double *)malloc(cnt * sizeof(double)))==NULL) {
  186. fprintf(stderr,"INSUFFICIENT MEMORY for input data.\n");
  187. return(MEMORY_ERROR);
  188. }
  189. fseek(fpi,0,0);
  190. linecnt = 1;
  191. p = *parray;
  192. while(fgets(temp,200,fpi)==temp) {
  193. q = temp;
  194. if(*q == ';') // Allow comments in file
  195. continue;
  196. while(get_float_with_e_from_within_string(&q,p)) {
  197. p++;
  198. }
  199. linecnt++;
  200. }
  201. if(fclose(fpi)<0) {
  202. fprintf(stderr,"WARNING: Failed to close input data file.\n");
  203. }
  204. *itemcnt = cnt;
  205. return(FINISHED);
  206. }
  207. /************************** TEST_THE_SPECIAL_DATA **********************************/
  208. int test_the_special_data(double *parray, int itemcnt, double nyquist)
  209. {
  210. double *p;
  211. int isamp = 0, linecnt = 1;
  212. p = parray;
  213. while(linecnt <= itemcnt/2) {
  214. if(isamp) {
  215. if(*p < 0.0 || *p > 1.0) {
  216. fprintf(stderr,"Amp (%lf) out of range (0-1) at line %d in datafile.\n",*p,linecnt);
  217. return(DATA_ERROR);
  218. }
  219. } else {
  220. if(*p < 0.0 || *p > nyquist) {
  221. fprintf(stderr,"Frq (%lf) out of range (0 - %lf) at line %d in datafile.\n",*p,nyquist,linecnt);
  222. return(DATA_ERROR);
  223. }
  224. }
  225. if(!isamp)
  226. linecnt++;
  227. isamp = !isamp;
  228. p++;
  229. }
  230. return(FINISHED);
  231. }
  232. /************************** ALLOCATE_OUTPUT_ARRAY **********************************/
  233. int allocate_output_array(double **outarray,int *outcnt,double chwidth,double halfchwidth,double nyquist)
  234. {
  235. int outsize = 1; // Count initial halfwidht channel
  236. double lastlofrq, lofrq = halfchwidth;
  237. while(lofrq <= nyquist) {
  238. outsize++;
  239. lastlofrq = lofrq;
  240. lofrq *= PSEUDOCENT;
  241. if(lofrq - lastlofrq >= chwidth) // Once chan separation is > chwidth
  242. lofrq = lastlofrq + chwidth; // Just set 1 value per channel
  243. }
  244. outsize += 64; // SAFETY
  245. outsize *= 2;
  246. if((*outarray = (double *)malloc(outsize * sizeof(double)))==NULL) {
  247. fprintf(stderr,"INSUFFICIENT MEMORY for input data.\n");
  248. return(MEMORY_ERROR);
  249. }
  250. *outcnt = outsize;
  251. return( FINISHED);
  252. }
  253. /************************** SMOOTH **********************************/
  254. int smooth(double *parray,int itemcnt,double *outarray,int *outcnt,double chwidth,double halfchwidth,double nyquist,int arraycnt)
  255. {
  256. int exit_status;
  257. int k = 0, hi = 0;
  258. double lastlofrq, lofrq = halfchwidth, thisamp;
  259. double *brkptr = parray;
  260. int brksize = itemcnt/2;
  261. double firstval,lastval,lastind;
  262. int brkinit = 0;
  263. outarray[k] = 0.0;
  264. outarray[k+1] = 0.0;
  265. k = 2;
  266. while(lofrq < nyquist) {
  267. if(k >= *outcnt) {
  268. fprintf(stderr,"OUT OF MEMORY IN OUTPUT ARRAY.\n");
  269. return(MEMORY_ERROR);
  270. }
  271. if((exit_status = read_value_from_array(&thisamp,lofrq,parray,&brkptr,brksize,&firstval,&lastval,&lastind,&brkinit))<0)
  272. return(exit_status);
  273. outarray[k] = lofrq;
  274. outarray[k+1] = thisamp;
  275. lastlofrq = lofrq;
  276. lofrq *= PSEUDOCENT;
  277. if(lofrq - lastlofrq >= chwidth) // Once chan separation is < PSEUDOCENT
  278. lofrq = lastlofrq + chwidth; // Just set 1 value per channel
  279. k += 2;
  280. }
  281. outarray[k++] = nyquist;
  282. outarray[k++] = 0.0;
  283. *outcnt = k;
  284. if(outarray[0] == 0.0 && outarray[2] == 0.0) { // Eliminate any duplication of zero start frq
  285. k = 0;
  286. hi = 2;
  287. while(hi < *outcnt) {
  288. outarray[k] = outarray[hi];
  289. k++;
  290. hi++;
  291. }
  292. *outcnt = k;
  293. }
  294. return(FINISHED);
  295. }
  296. /************************** DOVE **********************************/
  297. int dove(double *parray,int itemcnt,double *outarray,int outcnt)
  298. {
  299. double *amp, *frq, *inarrayend = parray + itemcnt, *outarrayend = outarray + outcnt, tailstartfrq;
  300. amp = inarrayend - 1;
  301. if(!flteq(*amp,0.0)) // Input spectrum never falls to zero
  302. return(FINISHED);
  303. while(amp > parray) { // Search back from end of (orig) spectrum, until spectral vals are >0
  304. if(!flteq(*amp,0.0))
  305. break;
  306. amp -= 2;
  307. }
  308. if(amp <= parray) // (original) spectrum is everywhere zero
  309. return(FINISHED);
  310. frq = amp - 1;
  311. tailstartfrq = *frq; // Note frq at which (original) spectrum finally falls to zero
  312. frq = outarray; // Parse frq vals in output array
  313. while(frq < outarrayend) {
  314. if(*frq >= tailstartfrq) {
  315. amp = frq - 1; // Note where outfrq exceeds tailstartfrq of input, and step to amp BEFORE it
  316. break;
  317. }
  318. frq += 2;
  319. }
  320. if(frq >= outarrayend) { // Output frq never exceeds input frq: should be impossible
  321. fprintf(stderr,"DOVETAILING FAILED.\n");
  322. return(PROGRAM_ERROR);
  323. }
  324. while(amp < outarrayend) { // Search for point where output first touches zero
  325. if(flteq(*amp,0.0))
  326. break;
  327. amp += 2;
  328. }
  329. if(amp >= outarrayend) {
  330. fprintf(stderr,"NO DOVETAILING OCCURED.\n");
  331. }
  332. while(amp < outarrayend) { // Set tail to zero
  333. *amp = 0.0;
  334. amp += 2;
  335. }
  336. return(FINISHED);
  337. }
  338. /**************************** READ_VALUE_FROM_ARRAY *****************************/
  339. int read_value_from_array(double *outval,double thisfrq,double *brk,double **brkptr,int brksize,double *firstval,double *lastval,double *lastind,int *brkinit)
  340. {
  341. double *endpair, *p, val;
  342. double hival, loval, hiind, loind;
  343. if(!(*brkinit)) {
  344. *brkptr = brk;
  345. *firstval = *(brk+1);
  346. endpair = brk + ((brksize-1)*2);
  347. *lastind = *endpair;
  348. *lastval = *(endpair+1);
  349. *brkinit = 1;
  350. }
  351. p = *brkptr;
  352. if(thisfrq <= *brk) {
  353. *outval = *firstval;
  354. return(FINISHED);
  355. } else if(thisfrq >= *lastind) {
  356. *outval = *lastval;
  357. return(FINISHED);
  358. }
  359. if(thisfrq > *p) {
  360. while(*p < thisfrq)
  361. p += 2;
  362. } else {
  363. while(*p >=thisfrq)
  364. p -= 2;
  365. p += 2;
  366. }
  367. hival = *(p+1);
  368. hiind = *p;
  369. loval = *(p-1);
  370. loind = *(p-2);
  371. val = (thisfrq - loind)/(hiind - loind);
  372. val *= (hival - loval);
  373. val += loval;
  374. *outval = val;
  375. *brkptr = p;
  376. return(FINISHED);
  377. }
  378. /************************** GET_FLOAT_WITH_E_FROM_WITHIN_STRING **************************
  379. * takes a pointer TO A POINTER to a string. If it succeeds in finding
  380. * a float it returns the float value (*val), and it's new position in the
  381. * string (*str).
  382. */
  383. int get_float_with_e_from_within_string(char **str,double *val)
  384. {
  385. char *p, *valstart;
  386. int decimal_point_cnt = 0, has_digits = 0, has_e = 0, lastchar = 0;
  387. p = *str;
  388. while(isspace(*p))
  389. p++;
  390. valstart = p;
  391. switch(*p) {
  392. case('-'): break;
  393. case('.'): decimal_point_cnt=1; break;
  394. default:
  395. if(!isdigit(*p))
  396. return(FALSE);
  397. has_digits = TRUE;
  398. break;
  399. }
  400. p++;
  401. while(!isspace(*p) && *p!=NEWLINE && *p!=ENDOFSTR) {
  402. if(isdigit(*p))
  403. has_digits = TRUE;
  404. else if(*p == 'e') {
  405. if(has_e || !has_digits)
  406. return(FALSE);
  407. has_e = 1;
  408. } else if(*p == '-') {
  409. if(!has_e || (lastchar != 'e'))
  410. return(FALSE);
  411. } else if(*p == '.') {
  412. if(has_e || (++decimal_point_cnt>1))
  413. return(FALSE);
  414. } else
  415. return(FALSE);
  416. lastchar = *p;
  417. p++;
  418. }
  419. if(!has_digits || sscanf(valstart,"%lf",val)!=1)
  420. return(FALSE);
  421. *str = p;
  422. return(TRUE);
  423. }