/* * Copyright (c) 1983-2023 Trevor Wishart and Composers Desktop Project Ltd * http://www.trevorwishart.co.uk * http://www.composersdesktop.com * This file is part of the CDP System. The CDP System is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The CDP System is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the CDP System; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* * Take a set of amp-frq data in the correct range, * and use cubic spline to replot curve to cover * every 1/64 tone over whole range. * Where, frq ratio between adjacent chans becomes < 1/64 tone * use chan centre frqs. */ #define PSEUDOCENT (1.0009) // approx 1/64th of a tone #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int sloom = 0; int sloombatch = 0; const char* cdp_version = "6.1.0"; char errstr[2400]; //RWD added //CDP LIB REPLACEMENTS static int allocate_output_array(double **outarray, int *outcnt, double chwidth, double halfchwidth, double nyquist); //static int get_tk_cmdline_word(int *cmdlinecnt,char ***cmdline,char *q); static int handle_the_special_data(FILE *fpi, double **parray, double **secondderiv, double **x, double **y, int *itemcnt); static int test_the_special_data(double *parray, int itemcnt, double nyquist); static int smooth(double *x,double *y,double *secondderiv,double *outarray,int *outcnt,double chwidth,double halfchwidth,double nyquist,int arraycnt); //static int smooth(double *parray,double *outarray,int *outcnt, double chwidth, double halfchwidth,double nyquist); static int spline(double *parray,int itemcnt,double *secondderiv,double *x,double *y); static int splint(double thisfrq,double *thisamp,int *hi,int arraycnt,double *secondderiv, double *x, double *y); static int dove(double *parray,int itemcnt,double *outarray,int outcnt); static int get_float_with_e_from_within_string(char **str,double *val); /**************************************** MAIN *********************************************/ int main(int argc,char *argv[]) { FILE *fpi, *fpo; int k, OK = 0, dovetail = 0; int exit_status, srate, pointcnt, clength; double chwidth, halfchwidth, nyquist, *parray, *secondderiv, *x, *y, *outarray; int itemcnt, outcnt, arraycnt; char temp[200], temp2[200]; if(argc < 5 || argc > 6) { usage1(); return(FAILED); } if((fpi = fopen(argv[1],"r")) == NULL) { fprintf(stdout,"CANNOT OPEN DATAFILE %s\n",argv[1]); fflush(stdout); return(FAILED); } if((exit_status = handle_the_special_data(fpi,&parray,&secondderiv,&x,&y,&itemcnt))<0) { return(FAILED); } arraycnt = itemcnt/2; if(sscanf(argv[3],"%d",&pointcnt) != 1) { fprintf(stdout,"CANNOT READ POINTCNT (%s)\n",argv[3]); fflush(stdout); return(FAILED); } OK = 0; k = 1; while(k < pointcnt) { k *= 2; if(k == pointcnt){ OK= 1; break; } } if(!OK) { fprintf(stdout,"ANALYSIS POINTS PARAMETER MUST BE A POWER OF TWO.\n"); fflush(stdout); return(FAILED); } if(sscanf(argv[4],"%d",&srate) != 1) { fprintf(stdout,"CANNOT READ SAMPLE RATE (%s)\n",argv[3]); fflush(stdout); return(FAILED); } if(srate < 44100 || BAD_SR(srate)) { fprintf(stdout,"INVALID SAMPLE RATE ENTERED (44100,48000,88200,96000 only).\n"); fflush(stdout); return(DATA_ERROR); } if(argc == 6) { if(strcmp(argv[5],"-s")) { fprintf(stdout,"UNKNOWN FINAL PARAMETER (%s).\n",argv[5]); fflush(stdout); return(DATA_ERROR); } dovetail = 1; } clength = (pointcnt + 2)/2; nyquist = (double)(srate / 2); chwidth = nyquist/(double)clength; halfchwidth = chwidth / 2; if((exit_status = test_the_special_data(parray,itemcnt,nyquist))<0) return(FAILED); if((exit_status = allocate_output_array(&outarray,&outcnt,chwidth,halfchwidth,nyquist))<0) return(FAILED); if((fpo = fopen(argv[2],"w")) == NULL) { fprintf(stdout,"CANNOT OPEN OUTPUT DATAFILE %s\n",argv[2]); fflush(stdout); return(FAILED); } if((exit_status = spline(parray,itemcnt,secondderiv,x,y))<0) return(FAILED); if((exit_status = smooth(x,y,secondderiv,outarray,&outcnt,chwidth,halfchwidth,nyquist,arraycnt)) < 0) return(FAILED); if(dovetail) { if((exit_status = dove(parray,itemcnt,outarray,outcnt)) < 0) return(FAILED); } k = 0; for(k = 0;k < outcnt;k+=2) { sprintf(temp,"%lf",outarray[k]); sprintf(temp2,"%lf",outarray[k+1]); strcat(temp2,"\n"); strcat(temp,"\t"); strcat(temp,temp2); fputs(temp,fpo); } if(fclose(fpo)<0) { fprintf(stdout,"WARNING: Failed to close output data file %s.\n",argv[2]); fflush(stdout); } return(SUCCEEDED); } /******************************** USAGE1 ********************************/ int usage1(void) { fprintf(stderr, "USAGE: cubicspline datafile outdatafile pointcnt srate [-s]\n" "\n" "Generate smoothed curve from input data re spectrum.\n" "\n" "DATAFILE textfile of frq/amp pairs.\n" "OUTDATAFILE textfile of smoothed data.\n" "POINTCNT no of analysis points required in handling data.\n" "SRATE samplerate of eventual sound output.\n" "-s if spectral amp falls to zero before the nyquist,\n" " keep zeroed end of spectrum at zero.\n." "\n"); return(USAGE_ONLY); } /************************** HANDLE_THE_SPECIAL_DATA **********************************/ int handle_the_special_data(FILE *fpi, double **parray, double **secondderiv, double **x, double **y,int *itemcnt) { int cnt/*, linecnt*/; // int arraycnt; double *p, dummy; char temp[200], *q; cnt = 0; p = &dummy; while(fgets(temp,200,fpi)==temp) { q = temp; if(*q == ';') // Allow comments in file continue; while(get_float_with_e_from_within_string(&q,p)) cnt++; } if(ODD(cnt)) { fprintf(stdout,"Data not paired correctly in input data file\n"); fflush(stdout); return(DATA_ERROR); } if(cnt == 0) { fprintf(stdout,"No data in input data file\n"); fflush(stdout); return(DATA_ERROR); } if((*parray = (double *)malloc(cnt * sizeof(double)))==NULL) { fprintf(stdout,"INSUFFICIENT MEMORY for input data.\n"); fflush(stdout); return(MEMORY_ERROR); } // arraycnt = cnt/2; if((*secondderiv = (double *)malloc(cnt/2 * sizeof(double)))==NULL) { fprintf(stdout,"INSUFFICIENT MEMORY for storing slope of input data.\n"); fflush(stdout); return(MEMORY_ERROR); } //RWD 2025 move to calloc, to pacify gcc Linux if((*x = (double *)calloc(cnt/2, sizeof(double)))==NULL) { fprintf(stdout,"INSUFFICIENT MEMORY for storing x-coords of input data.\n"); fflush(stdout); return(MEMORY_ERROR); } if((*y = (double *)calloc(cnt/2, sizeof(double)))==NULL) { fprintf(stdout,"INSUFFICIENT MEMORY for storing y-coords of input data.\n"); fflush(stdout); return(MEMORY_ERROR); } fseek(fpi,0,0); // linecnt = 1; p = *parray; while(fgets(temp,200,fpi)==temp) { q = temp; if(*q == ';') // Allow comments in file continue; while(get_float_with_e_from_within_string(&q,p)) { p++; } // linecnt++; } if(fclose(fpi)<0) { fprintf(stdout,"WARNING: Failed to close input data file.\n"); fflush(stdout); } *itemcnt = cnt; return(FINISHED); } /************************** TEST_THE_SPECIAL_DATA **********************************/ int test_the_special_data(double *parray, int itemcnt, double nyquist) { double *p; int isamp = 0, linecnt = 1; p = parray; while(linecnt <= itemcnt/2) { if(isamp) { if(*p < 0.0 || *p > 1.0) { fprintf(stdout,"Amp (%lf) out of range (0-1) at line %d in datafile.\n",*p,linecnt); fflush(stdout); return(DATA_ERROR); } } else { if(*p < 0.0 || *p > nyquist) { fprintf(stdout,"Frq (%lf) out of range (0 - %lf) at line %d in datafile.\n",*p,nyquist,linecnt); fflush(stdout); return(DATA_ERROR); } } if(!isamp) linecnt++; isamp = !isamp; p++; } return(FINISHED); } /************************** ALLOCATE_OUTPUT_ARRAY **********************************/ int allocate_output_array(double **outarray,int *outcnt,double chwidth,double halfchwidth,double nyquist) { int outsize = 1; // Count initial halfwidht channel double lastlofrq, lofrq = halfchwidth; while(lofrq <= nyquist) { outsize++; lastlofrq = lofrq; lofrq *= PSEUDOCENT; if(lofrq - lastlofrq >= chwidth) // Once chan separation is > chwidth lofrq = lastlofrq + chwidth; // Just set 1 value per channel } outsize += 64; // SAFETY outsize *= 2; if((*outarray = (double *)malloc(outsize * sizeof(double)))==NULL) { fprintf(stdout,"INSUFFICIENT MEMORY for input data.\n"); fflush(stdout); return(MEMORY_ERROR); } *outcnt = outsize; return( FINISHED); } /************************** SMOOTH ********************************** static int smooth(double *parray,double *outarray,int *outcnt,double chwidth,double halfchwidth,double nyquist) { int k = 0; double lastlofrq, lofrq = halfchwidth; outarray[k] = 1.0; outarray[k+1] = 0.0; k = 2; while(lofrq <= nyquist) { k += 2; if(k > *outcnt) { fprintf(stdout,"OUT OF MEMORY IN OUTPUT ARRAY.\n"); fflush(stdout); return(MEMORY_ERROR); } outarray[k] = 0.0; outarray[k+1] = lofrq; lastlofrq = lofrq; lofrq *= PSEUDOCENT; if(lofrq - lastlofrq >= chwidth) { // Once chan separation is < HEREH lofrq = lastlofrq + chwidth; // Just set 1 value per channel outarray[k] = 1.0; } } *outcnt = k; return( FINISHED); } */ /************************************ SPLINE ************************************ * * This function returns the second derivative at all points of the curve. * * We assume that the curve is flat (first derivative = 0) at start and end points */ int spline(double *parray,int itemcnt,double *secondderiv,double *x,double *y) { double firstderivstt = 0.0, firstderivend = 0.0; double *u, sig, ppp, qend, uend; int i, k, arraylen = itemcnt/2; if((u = (double *)malloc(arraylen * sizeof(double)))==NULL) { fprintf(stdout,"INSUFFICIENT MEMORY for slope calculations 1.\n"); fflush(stdout); return(MEMORY_ERROR); } for(i=0,k=0;k= 0;k--) { secondderiv[k] = (secondderiv[k] * secondderiv[k+1]) + u[k]; } free(u); return(FINISHED); } /************************************ SPLINT ************************************ * * This function returns the value at a specified frq on curve, using cspline interp (2nd-deriv) data.. */ int splint(double thisfrq,double *thisamp,int *hi,int arraycnt,double *secondderiv, double *x, double *y) { int klo, khi = *hi; double fullstep, a, b, amp, jjj, kkk; /* Establish which input values bracket 'thisfrq' */ while(x[khi] <= thisfrq) { khi++; if(khi >= arraycnt) { *thisamp = y[arraycnt - 1]; return(FINISHED); } } klo = khi - 1; if(klo < 0) { *thisamp = y[0]; return(FINISHED); } fullstep = x[khi] - x[klo]; a = (x[khi] - thisfrq)/fullstep; b = (thisfrq - x[klo])/fullstep; amp = a * y[klo]; amp += b * y[khi]; jjj = a * a * a; jjj -= a; jjj *= secondderiv[klo]; kkk = b * b * b; kkk -= b; kkk *= secondderiv[khi]; jjj += kkk; jjj *= fullstep * fullstep; jjj /= 6.0; amp += jjj; if(amp < 0.0) amp = 0.0; if(amp > 1.0) { fprintf(stdout,"INFO: SMOOTHED DATA CLIPPED\n"); fflush(stdout); amp = 1.0; } *thisamp = amp; *hi = khi; return(FINISHED); } /************************** SMOOTH **********************************/ int smooth(double *x,double *y,double *secondderiv,double *outarray,int *outcnt,double chwidth,double halfchwidth,double nyquist,int arraycnt) { int exit_status; int k = 0, hi = 0; double lastlofrq, lofrq = halfchwidth, thisamp; outarray[k] = 0.0; outarray[k+1] = 0.0; k = 2; while(lofrq < nyquist) { k += 2; if(k > *outcnt) { fprintf(stdout,"OUT OF MEMORY IN OUTPUT ARRAY.\n"); fflush(stdout); return(MEMORY_ERROR); } if((exit_status = splint(lofrq,&thisamp,&hi,arraycnt,secondderiv,x,y))<0) return(FAILED); outarray[k] = lofrq; outarray[k+1] = thisamp; lastlofrq = lofrq; lofrq *= PSEUDOCENT; if(lofrq - lastlofrq >= chwidth) // Once chan separation is < PSEUDOCENT lofrq = lastlofrq + chwidth; // Just set 1 value per channel } k += 2; outarray[k++] = nyquist; outarray[k++] = 0.0; *outcnt = k; if(outarray[0] == 0.0 && outarray[2] == 0.0) { // Eliminate any duplication of zero start frq k = 0; hi = 2; while(hi < *outcnt) { outarray[k] = outarray[hi]; k++; hi++; } *outcnt = k; } return(FINISHED); } /************************** DOVE **********************************/ int dove(double *parray,int itemcnt,double *outarray,int outcnt) { double *amp, *frq, *inarrayend = parray + itemcnt, *outarrayend = outarray + outcnt, tailstartfrq; amp = inarrayend - 1; if(!flteq(*amp,0.0)) // Input spectrum never falls to zero return(FINISHED); while(amp > parray) { // Search back from end of (orig) spectrum, until spectral vals are >0 if(!flteq(*amp,0.0)) break; amp -= 2; } if(amp <= parray) // (original) spectrum is everywhere zero return(FINISHED); frq = amp - 1; tailstartfrq = *frq; // Note frq at which (original) spectrum finally falls to zero frq = outarray; // Parse frq vals in output array while(frq < outarrayend) { if(*frq >= tailstartfrq) { amp = frq - 1; // Note where outfrq exceeds tailstartfrq of input, and step to amp BEFORE it break; } frq += 2; } if(frq >= outarrayend) { // Output frq never exceeds input frq: should be impossible fprintf(stdout,"DOVETAILING FAILED.\n"); fflush(stdout); return(PROGRAM_ERROR); } while(amp < outarrayend) { // Search for point where output first touches zero if(flteq(*amp,0.0)) break; amp += 2; } if(amp >= outarrayend) { fprintf(stdout,"NO DOVETAILING OCCURED.\n"); fflush(stdout); } while(amp < outarrayend) { // Set tail to zero *amp = 0.0; amp += 2; } return(FINISHED); } /************************** GET_FLOAT_WITH_E_FROM_WITHIN_STRING ************************** * takes a pointer TO A POINTER to a string. If it succeeds in finding * a float it returns the float value (*val), and it's new position in the * string (*str). */ int get_float_with_e_from_within_string(char **str,double *val) { char *p, *valstart; int decimal_point_cnt = 0, has_digits = 0, has_e = 0, lastchar = 0; p = *str; while(isspace(*p)) p++; valstart = p; switch(*p) { case('-'): break; case('.'): decimal_point_cnt=1; break; default: if(!isdigit(*p)) return(FALSE); has_digits = TRUE; break; } p++; while(!isspace(*p) && *p!=NEWLINE && *p!=ENDOFSTR) { if(isdigit(*p)) has_digits = TRUE; else if(*p == 'e') { if(has_e || !has_digits) return(FALSE); has_e = 1; } else if(*p == '-') { if(!has_e || (lastchar != 'e')) return(FALSE); } else if(*p == '.') { if(has_e || (++decimal_point_cnt>1)) return(FALSE); } else return(FALSE); lastchar = *p; p++; } if(!has_digits || sscanf(valstart,"%lf",val)!=1) return(FALSE); *str = p; return(TRUE); }