cubicspline.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. /*
  2. * Copyright (c) 1983-2023 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 cubic spline 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 added
  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, double **secondderiv, double **x, double **y, int *itemcnt);
  56. static int test_the_special_data(double *parray, int itemcnt, double nyquist);
  57. static int smooth(double *x,double *y,double *secondderiv,double *outarray,int *outcnt,double chwidth,double halfchwidth,double nyquist,int arraycnt);
  58. //static int smooth(double *parray,double *outarray,int *outcnt, double chwidth, double halfchwidth,double nyquist);
  59. static int spline(double *parray,int itemcnt,double *secondderiv,double *x,double *y);
  60. static int splint(double thisfrq,double *thisamp,int *hi,int arraycnt,double *secondderiv, double *x, double *y);
  61. static int dove(double *parray,int itemcnt,double *outarray,int outcnt);
  62. static int get_float_with_e_from_within_string(char **str,double *val);
  63. /**************************************** MAIN *********************************************/
  64. int main(int argc,char *argv[])
  65. {
  66. FILE *fpi, *fpo;
  67. int k, OK = 0, dovetail = 0;
  68. int exit_status, srate, pointcnt, clength;
  69. double chwidth, halfchwidth, nyquist, *parray, *secondderiv, *x, *y, *outarray;
  70. int itemcnt, outcnt, arraycnt;
  71. char temp[200], temp2[200];
  72. if(argc < 5 || argc > 6) {
  73. usage1();
  74. return(FAILED);
  75. }
  76. if((fpi = fopen(argv[1],"r")) == NULL) {
  77. fprintf(stdout,"CANNOT OPEN DATAFILE %s\n",argv[1]);
  78. fflush(stdout);
  79. return(FAILED);
  80. }
  81. if((exit_status = handle_the_special_data(fpi,&parray,&secondderiv,&x,&y,&itemcnt))<0) {
  82. return(FAILED);
  83. }
  84. arraycnt = itemcnt/2;
  85. if(sscanf(argv[3],"%d",&pointcnt) != 1) {
  86. fprintf(stdout,"CANNOT READ POINTCNT (%s)\n",argv[3]);
  87. fflush(stdout);
  88. return(FAILED);
  89. }
  90. OK = 0;
  91. k = 1;
  92. while(k < pointcnt) {
  93. k *= 2;
  94. if(k == pointcnt){
  95. OK= 1;
  96. break;
  97. }
  98. }
  99. if(!OK) {
  100. fprintf(stdout,"ANALYSIS POINTS PARAMETER MUST BE A POWER OF TWO.\n");
  101. fflush(stdout);
  102. return(FAILED);
  103. }
  104. if(sscanf(argv[4],"%d",&srate) != 1) {
  105. fprintf(stdout,"CANNOT READ SAMPLE RATE (%s)\n",argv[3]);
  106. fflush(stdout);
  107. return(FAILED);
  108. }
  109. if(srate < 44100 || BAD_SR(srate)) {
  110. fprintf(stdout,"INVALID SAMPLE RATE ENTERED (44100,48000,88200,96000 only).\n");
  111. fflush(stdout);
  112. return(DATA_ERROR);
  113. }
  114. if(argc == 6) {
  115. if(strcmp(argv[5],"-s")) {
  116. fprintf(stdout,"UNKNOWN FINAL PARAMETER (%s).\n",argv[5]);
  117. fflush(stdout);
  118. return(DATA_ERROR);
  119. }
  120. dovetail = 1;
  121. }
  122. clength = (pointcnt + 2)/2;
  123. nyquist = (double)(srate / 2);
  124. chwidth = nyquist/(double)clength;
  125. halfchwidth = chwidth / 2;
  126. if((exit_status = test_the_special_data(parray,itemcnt,nyquist))<0)
  127. return(FAILED);
  128. if((exit_status = allocate_output_array(&outarray,&outcnt,chwidth,halfchwidth,nyquist))<0)
  129. return(FAILED);
  130. if((fpo = fopen(argv[2],"w")) == NULL) {
  131. fprintf(stdout,"CANNOT OPEN OUTPUT DATAFILE %s\n",argv[2]);
  132. fflush(stdout);
  133. return(FAILED);
  134. }
  135. if((exit_status = spline(parray,itemcnt,secondderiv,x,y))<0)
  136. return(FAILED);
  137. if((exit_status = smooth(x,y,secondderiv,outarray,&outcnt,chwidth,halfchwidth,nyquist,arraycnt)) < 0)
  138. return(FAILED);
  139. if(dovetail) {
  140. if((exit_status = dove(parray,itemcnt,outarray,outcnt)) < 0)
  141. return(FAILED);
  142. }
  143. k = 0;
  144. for(k = 0;k < outcnt;k+=2) {
  145. sprintf(temp,"%lf",outarray[k]);
  146. sprintf(temp2,"%lf",outarray[k+1]);
  147. strcat(temp2,"\n");
  148. strcat(temp,"\t");
  149. strcat(temp,temp2);
  150. fputs(temp,fpo);
  151. }
  152. if(fclose(fpo)<0) {
  153. fprintf(stdout,"WARNING: Failed to close output data file %s.\n",argv[2]);
  154. fflush(stdout);
  155. }
  156. return(SUCCEEDED);
  157. }
  158. /******************************** USAGE1 ********************************/
  159. int usage1(void)
  160. {
  161. fprintf(stderr,
  162. "USAGE: cubicspline datafile outdatafile pointcnt srate [-s]\n"
  163. "\n"
  164. "Generate smoothed curve from input data re spectrum.\n"
  165. "\n"
  166. "DATAFILE textfile of frq/amp pairs.\n"
  167. "OUTDATAFILE textfile of smoothed data.\n"
  168. "POINTCNT no of analysis points required in handling data.\n"
  169. "SRATE samplerate of eventual sound output.\n"
  170. "-s if spectral amp falls to zero before the nyquist,\n"
  171. " keep zeroed end of spectrum at zero.\n."
  172. "\n");
  173. return(USAGE_ONLY);
  174. }
  175. /************************** HANDLE_THE_SPECIAL_DATA **********************************/
  176. int handle_the_special_data(FILE *fpi, double **parray, double **secondderiv, double **x, double **y,int *itemcnt)
  177. {
  178. int cnt/*, linecnt*/;
  179. // int arraycnt;
  180. double *p, dummy;
  181. char temp[200], *q;
  182. cnt = 0;
  183. p = &dummy;
  184. while(fgets(temp,200,fpi)==temp) {
  185. q = temp;
  186. if(*q == ';') // Allow comments in file
  187. continue;
  188. while(get_float_with_e_from_within_string(&q,p))
  189. cnt++;
  190. }
  191. if(ODD(cnt)) {
  192. fprintf(stdout,"Data not paired correctly in input data file\n");
  193. fflush(stdout);
  194. return(DATA_ERROR);
  195. }
  196. if(cnt == 0) {
  197. fprintf(stdout,"No data in input data file\n");
  198. fflush(stdout);
  199. return(DATA_ERROR);
  200. }
  201. if((*parray = (double *)malloc(cnt * sizeof(double)))==NULL) {
  202. fprintf(stdout,"INSUFFICIENT MEMORY for input data.\n");
  203. fflush(stdout);
  204. return(MEMORY_ERROR);
  205. }
  206. // arraycnt = cnt/2;
  207. if((*secondderiv = (double *)malloc(cnt/2 * sizeof(double)))==NULL) {
  208. fprintf(stdout,"INSUFFICIENT MEMORY for storing slope of input data.\n");
  209. fflush(stdout);
  210. return(MEMORY_ERROR);
  211. }
  212. //RWD 2025 move to calloc, to pacify gcc Linux
  213. if((*x = (double *)calloc(cnt/2, sizeof(double)))==NULL) {
  214. fprintf(stdout,"INSUFFICIENT MEMORY for storing x-coords of input data.\n");
  215. fflush(stdout);
  216. return(MEMORY_ERROR);
  217. }
  218. if((*y = (double *)calloc(cnt/2, sizeof(double)))==NULL) {
  219. fprintf(stdout,"INSUFFICIENT MEMORY for storing y-coords of input data.\n");
  220. fflush(stdout);
  221. return(MEMORY_ERROR);
  222. }
  223. fseek(fpi,0,0);
  224. // linecnt = 1;
  225. p = *parray;
  226. while(fgets(temp,200,fpi)==temp) {
  227. q = temp;
  228. if(*q == ';') // Allow comments in file
  229. continue;
  230. while(get_float_with_e_from_within_string(&q,p)) {
  231. p++;
  232. }
  233. // linecnt++;
  234. }
  235. if(fclose(fpi)<0) {
  236. fprintf(stdout,"WARNING: Failed to close input data file.\n");
  237. fflush(stdout);
  238. }
  239. *itemcnt = cnt;
  240. return(FINISHED);
  241. }
  242. /************************** TEST_THE_SPECIAL_DATA **********************************/
  243. int test_the_special_data(double *parray, int itemcnt, double nyquist)
  244. {
  245. double *p;
  246. int isamp = 0, linecnt = 1;
  247. p = parray;
  248. while(linecnt <= itemcnt/2) {
  249. if(isamp) {
  250. if(*p < 0.0 || *p > 1.0) {
  251. fprintf(stdout,"Amp (%lf) out of range (0-1) at line %d in datafile.\n",*p,linecnt);
  252. fflush(stdout);
  253. return(DATA_ERROR);
  254. }
  255. } else {
  256. if(*p < 0.0 || *p > nyquist) {
  257. fprintf(stdout,"Frq (%lf) out of range (0 - %lf) at line %d in datafile.\n",*p,nyquist,linecnt);
  258. fflush(stdout);
  259. return(DATA_ERROR);
  260. }
  261. }
  262. if(!isamp)
  263. linecnt++;
  264. isamp = !isamp;
  265. p++;
  266. }
  267. return(FINISHED);
  268. }
  269. /************************** ALLOCATE_OUTPUT_ARRAY **********************************/
  270. int allocate_output_array(double **outarray,int *outcnt,double chwidth,double halfchwidth,double nyquist)
  271. {
  272. int outsize = 1; // Count initial halfwidht channel
  273. double lastlofrq, lofrq = halfchwidth;
  274. while(lofrq <= nyquist) {
  275. outsize++;
  276. lastlofrq = lofrq;
  277. lofrq *= PSEUDOCENT;
  278. if(lofrq - lastlofrq >= chwidth) // Once chan separation is > chwidth
  279. lofrq = lastlofrq + chwidth; // Just set 1 value per channel
  280. }
  281. outsize += 64; // SAFETY
  282. outsize *= 2;
  283. if((*outarray = (double *)malloc(outsize * sizeof(double)))==NULL) {
  284. fprintf(stdout,"INSUFFICIENT MEMORY for input data.\n");
  285. fflush(stdout);
  286. return(MEMORY_ERROR);
  287. }
  288. *outcnt = outsize;
  289. return( FINISHED);
  290. }
  291. /************************** SMOOTH **********************************
  292. static int smooth(double *parray,double *outarray,int *outcnt,double chwidth,double halfchwidth,double nyquist)
  293. {
  294. int k = 0;
  295. double lastlofrq, lofrq = halfchwidth;
  296. outarray[k] = 1.0;
  297. outarray[k+1] = 0.0;
  298. k = 2;
  299. while(lofrq <= nyquist) {
  300. k += 2;
  301. if(k > *outcnt) {
  302. fprintf(stdout,"OUT OF MEMORY IN OUTPUT ARRAY.\n");
  303. fflush(stdout);
  304. return(MEMORY_ERROR);
  305. }
  306. outarray[k] = 0.0;
  307. outarray[k+1] = lofrq;
  308. lastlofrq = lofrq;
  309. lofrq *= PSEUDOCENT;
  310. if(lofrq - lastlofrq >= chwidth) { // Once chan separation is < HEREH
  311. lofrq = lastlofrq + chwidth; // Just set 1 value per channel
  312. outarray[k] = 1.0;
  313. }
  314. }
  315. *outcnt = k;
  316. return( FINISHED);
  317. }
  318. */
  319. /************************************ SPLINE ************************************
  320. *
  321. * This function returns the second derivative at all points of the curve.
  322. *
  323. * We assume that the curve is flat (first derivative = 0) at start and end points
  324. */
  325. int spline(double *parray,int itemcnt,double *secondderiv,double *x,double *y)
  326. {
  327. double firstderivstt = 0.0, firstderivend = 0.0;
  328. double *u, sig, ppp, qend, uend;
  329. int i, k, arraylen = itemcnt/2;
  330. if((u = (double *)malloc(arraylen * sizeof(double)))==NULL) {
  331. fprintf(stdout,"INSUFFICIENT MEMORY for slope calculations 1.\n");
  332. fflush(stdout);
  333. return(MEMORY_ERROR);
  334. }
  335. for(i=0,k=0;k<itemcnt;k+=2,i++) {
  336. x[i] = parray[k]; // FRQ is the independent, monotonically increasing variable
  337. y[i] = parray[k+1];
  338. }
  339. secondderiv[0] = -0.5;
  340. u[0] = (3.0/(x[1] - x[0])) * (((y[1] - y[0])/(x[1] - x[0])) - firstderivstt);
  341. for(i = 1;i < arraylen-1; i++) {
  342. sig = (x[i] - x[i-1])/(x[i+1] - x[i-1]);
  343. ppp = (sig * secondderiv[i-1]) + 2.0;
  344. secondderiv[i] = (sig - 1.0)/ppp;
  345. u[i] = ((y[i+1] - y[i])/(x[i+1] - x[i])) - ((y[i] - y[i-1])/(x[i] - x[i-1]));
  346. u[i] = (((6.0 * u[i])/(x[i+1] - x[i-1])) - (sig * u[i-1]))/ppp;
  347. }
  348. // i = arraylen-1; = last entry in array
  349. qend = 0.5;
  350. uend = (3.0/(x[i] - x[i-1])) * (firstderivend - ((y[i] - y[i-1])/(x[i] - x[i-1])));
  351. secondderiv[i] = (uend - (qend * u[i-1]))/((qend * secondderiv[i-1]) + 1.0);
  352. for(k = i-1;k >= 0;k--) {
  353. secondderiv[k] = (secondderiv[k] * secondderiv[k+1]) + u[k];
  354. }
  355. free(u);
  356. return(FINISHED);
  357. }
  358. /************************************ SPLINT ************************************
  359. *
  360. * This function returns the value at a specified frq on curve, using cspline interp (2nd-deriv) data..
  361. */
  362. int splint(double thisfrq,double *thisamp,int *hi,int arraycnt,double *secondderiv, double *x, double *y)
  363. {
  364. int klo, khi = *hi;
  365. double fullstep, a, b, amp, jjj, kkk;
  366. /* Establish which input values bracket 'thisfrq' */
  367. while(x[khi] <= thisfrq) {
  368. khi++;
  369. if(khi >= arraycnt) {
  370. *thisamp = y[arraycnt - 1];
  371. return(FINISHED);
  372. }
  373. }
  374. klo = khi - 1;
  375. if(klo < 0) {
  376. *thisamp = y[0];
  377. return(FINISHED);
  378. }
  379. fullstep = x[khi] - x[klo];
  380. a = (x[khi] - thisfrq)/fullstep;
  381. b = (thisfrq - x[klo])/fullstep;
  382. amp = a * y[klo];
  383. amp += b * y[khi];
  384. jjj = a * a * a;
  385. jjj -= a;
  386. jjj *= secondderiv[klo];
  387. kkk = b * b * b;
  388. kkk -= b;
  389. kkk *= secondderiv[khi];
  390. jjj += kkk;
  391. jjj *= fullstep * fullstep;
  392. jjj /= 6.0;
  393. amp += jjj;
  394. if(amp < 0.0)
  395. amp = 0.0;
  396. if(amp > 1.0) {
  397. fprintf(stdout,"INFO: SMOOTHED DATA CLIPPED\n");
  398. fflush(stdout);
  399. amp = 1.0;
  400. }
  401. *thisamp = amp;
  402. *hi = khi;
  403. return(FINISHED);
  404. }
  405. /************************** SMOOTH **********************************/
  406. int smooth(double *x,double *y,double *secondderiv,double *outarray,int *outcnt,double chwidth,double halfchwidth,double nyquist,int arraycnt)
  407. {
  408. int exit_status;
  409. int k = 0, hi = 0;
  410. double lastlofrq, lofrq = halfchwidth, thisamp;
  411. outarray[k] = 0.0;
  412. outarray[k+1] = 0.0;
  413. k = 2;
  414. while(lofrq < nyquist) {
  415. k += 2;
  416. if(k > *outcnt) {
  417. fprintf(stdout,"OUT OF MEMORY IN OUTPUT ARRAY.\n");
  418. fflush(stdout);
  419. return(MEMORY_ERROR);
  420. }
  421. if((exit_status = splint(lofrq,&thisamp,&hi,arraycnt,secondderiv,x,y))<0)
  422. return(FAILED);
  423. outarray[k] = lofrq;
  424. outarray[k+1] = thisamp;
  425. lastlofrq = lofrq;
  426. lofrq *= PSEUDOCENT;
  427. if(lofrq - lastlofrq >= chwidth) // Once chan separation is < PSEUDOCENT
  428. lofrq = lastlofrq + chwidth; // Just set 1 value per channel
  429. }
  430. k += 2;
  431. outarray[k++] = nyquist;
  432. outarray[k++] = 0.0;
  433. *outcnt = k;
  434. if(outarray[0] == 0.0 && outarray[2] == 0.0) { // Eliminate any duplication of zero start frq
  435. k = 0;
  436. hi = 2;
  437. while(hi < *outcnt) {
  438. outarray[k] = outarray[hi];
  439. k++;
  440. hi++;
  441. }
  442. *outcnt = k;
  443. }
  444. return(FINISHED);
  445. }
  446. /************************** DOVE **********************************/
  447. int dove(double *parray,int itemcnt,double *outarray,int outcnt)
  448. {
  449. double *amp, *frq, *inarrayend = parray + itemcnt, *outarrayend = outarray + outcnt, tailstartfrq;
  450. amp = inarrayend - 1;
  451. if(!flteq(*amp,0.0)) // Input spectrum never falls to zero
  452. return(FINISHED);
  453. while(amp > parray) { // Search back from end of (orig) spectrum, until spectral vals are >0
  454. if(!flteq(*amp,0.0))
  455. break;
  456. amp -= 2;
  457. }
  458. if(amp <= parray) // (original) spectrum is everywhere zero
  459. return(FINISHED);
  460. frq = amp - 1;
  461. tailstartfrq = *frq; // Note frq at which (original) spectrum finally falls to zero
  462. frq = outarray; // Parse frq vals in output array
  463. while(frq < outarrayend) {
  464. if(*frq >= tailstartfrq) {
  465. amp = frq - 1; // Note where outfrq exceeds tailstartfrq of input, and step to amp BEFORE it
  466. break;
  467. }
  468. frq += 2;
  469. }
  470. if(frq >= outarrayend) { // Output frq never exceeds input frq: should be impossible
  471. fprintf(stdout,"DOVETAILING FAILED.\n");
  472. fflush(stdout);
  473. return(PROGRAM_ERROR);
  474. }
  475. while(amp < outarrayend) { // Search for point where output first touches zero
  476. if(flteq(*amp,0.0))
  477. break;
  478. amp += 2;
  479. }
  480. if(amp >= outarrayend) {
  481. fprintf(stdout,"NO DOVETAILING OCCURED.\n");
  482. fflush(stdout);
  483. }
  484. while(amp < outarrayend) { // Set tail to zero
  485. *amp = 0.0;
  486. amp += 2;
  487. }
  488. return(FINISHED);
  489. }
  490. /************************** GET_FLOAT_WITH_E_FROM_WITHIN_STRING **************************
  491. * takes a pointer TO A POINTER to a string. If it succeeds in finding
  492. * a float it returns the float value (*val), and it's new position in the
  493. * string (*str).
  494. */
  495. int get_float_with_e_from_within_string(char **str,double *val)
  496. {
  497. char *p, *valstart;
  498. int decimal_point_cnt = 0, has_digits = 0, has_e = 0, lastchar = 0;
  499. p = *str;
  500. while(isspace(*p))
  501. p++;
  502. valstart = p;
  503. switch(*p) {
  504. case('-'): break;
  505. case('.'): decimal_point_cnt=1; break;
  506. default:
  507. if(!isdigit(*p))
  508. return(FALSE);
  509. has_digits = TRUE;
  510. break;
  511. }
  512. p++;
  513. while(!isspace(*p) && *p!=NEWLINE && *p!=ENDOFSTR) {
  514. if(isdigit(*p))
  515. has_digits = TRUE;
  516. else if(*p == 'e') {
  517. if(has_e || !has_digits)
  518. return(FALSE);
  519. has_e = 1;
  520. } else if(*p == '-') {
  521. if(!has_e || (lastchar != 'e'))
  522. return(FALSE);
  523. } else if(*p == '.') {
  524. if(has_e || (++decimal_point_cnt>1))
  525. return(FALSE);
  526. } else
  527. return(FALSE);
  528. lastchar = *p;
  529. p++;
  530. }
  531. if(!has_digits || sscanf(valstart,"%lf",val)!=1)
  532. return(FALSE);
  533. *str = p;
  534. return(TRUE);
  535. }