/* * Copyright (c) 1983-2013 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 * */ /* columns */ #include //#ifdef unix #define round(x) lround((x)) //#endif #define CALCLIM 0.001 #define M 7 #define NSTACK 50 #define MIDDLE_C_MIDI_OCTAVE 5 #define CONVERT_LOG10_TO_LOG2 (double)3.321928 #define LOW_A 6.875 /* Frequency of A below MIDI 0 */ double cntevents(double,double,double); int samesize(double,double,double,double,double); void approxtimes(double,double,int); void getetime(double,double,double,double,int); double gethibnd(double,double,double,double,double); double getlobnd(double,double,double,double,double); double refinesize(double,double,double,double,double,double); void prnt_pitchclass(int,int); int strgetfloat_db(char **,double *); static void bublsort(void); static void test_warp_data(void); int *cntr; char **strings = 0; char *stringstore; int stringscnt = 0; int stringstoresize = 0, stringstart = 0; /************************ TIMEVENTS ****************************** * * Generates event positions from startsize and end size * * Let start-time be T0 and end time be T1 * Let start size be S0 and end S1 * * number of event is given by :- * * N = (T1-T0) log S1 * ------- e__ * (S1-S0) S0 * * In general this will be non-integral, and we should * round N to an integer, and recalculate S1 by successive * approximation. * * Then positions of events are given by * * for X = 0 to N (S1-S0) * ----- X * T = (S1T0 - S0T1) S0(T1 - T0) (T1-T0) * s ----------- + ---------- e * (S1 - S0) (S1 - S0) * * If difference in eventsizes input to the main program is very small * then infinite values result. To avoid this we divert the calculation * to another routine (which assumes the input values are equal. * The value of CALCLIM = 0.001. (LArger than usual i.e. 0.05) * * If size values are so BIG that they exceed segment duration, * eventsize becomes segment duration and a Warning is printed. *(1) events approx same size. *(2) events of different sizes, find the number of events which approx * fit in the duration. *(3) If there's only one event .. that's it. *(4) Note the error (difference between approximated whole number of events * and the actual,non-integer, number of events). *(5) Calculate acceptable bounds on a new endeventsize, which will give * a better fit of the integer number of events within the given duration. *(6) Find an acceptable final value for endeventsize by successive approx * NB we don't know whether the function increases as d increases * so lobound may be > hibound. However, the maths of the method will * still work, just inverting the sense of the search!! *(7) Calculate the inital times of the events. * * This function returns the number of events within the time-interval, * and returns the times of these events in the array-of-doubles pos. */ int timevents(double intime0,double intime1,double insize0,double insize1) { int inumber; double fnum, fnumber, error; double lobound, hibound, duration; if(flteq(insize0,0.0) || flteq(insize1,0.0)) { sprintf(errstr,"Event size of zero encountered.\n"); do_error(); } duration = (intime1-intime0); if(duration<=0.0) { sprintf(errstr,"Inconsistent input times (2nd before 1st).\n"); do_error(); } if(fabs(insize1-insize0)fnumber) lobound = getlobnd(fnum,fnumber,insize1,insize0,duration); if(error > FLTERR) { /* 6 */ if(lobound < hibound) /* LOBOUND is a HIGH SIZE for a LOW COUNT!! */ swap(&hibound,&lobound); insize1 = refinesize(hibound,lobound,fnumber,error,duration,insize0); } else { insize1 = (hibound+lobound)/2; } /* 7 */ getetime(intime0,intime1,insize0,insize1,inumber); pos[inumber] = intime1; return(inumber+1); } /*************************** CNTEVENTS *****************************/ double cntevents(double dur,double s0,double s1) { double f1,f2; f1 = dur; f2 = s1-s0; f1 /= f2; f2 = s1/s0; f2 = log(f2); f1 *= f2; return(fabs(f1)); } /******************************* SAMESIZE ******************************* * * get event positions, if eventsize approx same throughout segment. * *(1) Get average size, find number of events and round to nearest int. *(3) Recalculate size, and thence event times. */ int samesize (double intime0,double intime1,double insize0,double insize1,double duration) { int inumber; double fnum, size; /* 1 */ size = (insize0+insize1)/2; fnum = duration/size; inumber = round(fnum); size = duration/(double)inumber; pos = (double *)exmalloc((inumber+1) * sizeof(double)); approxtimes(intime0,size,inumber); pos[inumber] = intime1; return(inumber+1); } /************************ APPROXTIME *************************** * * Calculate time-positions of equally spaced events. */ void approxtimes(double intime0,double size,int inumber) { int k; double *q = pos; *q++ = intime0; for(k=1;k lastfnum, while we're trying to DECREASE fnum) * restore values and increment in the opposite direction,but only half * as much! */ double getlobnd(double fnum,double fnumber,double insize1, double insize0,double duration) { double try = 1.0; double lastfnum; double bound = insize1; while(fnum>fnumber) { lastfnum = fnum; bound += try; while(bound<=0) { bound -= try; try /= 2; bound += try; } fnum = cntevents(duration,insize0,bound); if(fnum>lastfnum) { fnum =lastfnum; bound -= try; try = -(try/2); } } return(bound); } /***************************** REFINESIZE ****************************** * * refine size of final event to reduce error within bounds. */ double refinesize(double hibound,double lobound,double fnumber, double error,double duration,double insize0) { double size = (hibound+lobound)/2, fnum; while(error>(FLTERR)) { size = (hibound+lobound)/2; fnum = cntevents(duration,insize0,size); error = fabs(fnumber-fnum); if(error>FLTERR) { if(fnum m) { /* shuffle up */ i = &permmm[m]; for(n=m;n>k;n--) { *i = *(i-1); i--; } } else { /* shuffle down */ i = &permmm[0]; for(n=0;n= arraysize) { arraysize += BIGARRAY; /* NOVEMBER 2001 NEW */ number2 = (double *)malloc(arraysize*sizeof(double)); memcpy((void *)number2,(void *)number,cnt * sizeof(double)); number = number2; /* NOVEMBER 2001 OLD number = (double *)exrealloc((char *)number,arraysize*sizeof(double)); */ } } } } /************************* DO_INFILE ***************************/ void do_infile(char *argv) { char *p; double *number2; if((fp[0] = fopen(argv,"r"))==NULL) { sprintf(errstr,"Cannot open infile %s\n",argv); do_error(); } while(fgets(temp,20000,fp[0])!=NULL) { p = temp; while(strgetfloat(&p,&number[cnt])) { if(++cnt >= arraysize) { arraysize += BIGARRAY; /* NOVEMBER 2001 NEW */ if((number2=(double *)malloc(arraysize*sizeof(double)))==NULL) { sprintf(errstr,"Out of memory for more numbers at %d numbers\n",cnt); do_error(); } memcpy((void *)number2,(void *)number,cnt * sizeof(double)); number = number2; /* NOVEMBER 2001 OLD if((number=(double *)exrealloc((char *)number,arraysize*sizeof(double)))==NULL) { sprintf(errstr,"Out of memory for more numbers at %d numbers\n",cnt); do_error(); } */ } } } if(cnt <=0) { sprintf(errstr,"Invalid or missing data.\n"); do_error(); } fclose(fp[0]); } /************************* DO_DB_INFILE ***************************/ void do_DB_infile(char *argv) { char *p; if((fp[0] = fopen(argv,"r"))==NULL) { sprintf(errstr,"Cannot open infile %s\n",argv); do_error(); } while(fgets(temp,20000,fp[0])!=NULL) { p = temp; while(strgetfloat_db(&p,&number[cnt])) { if(++cnt >= arraysize) { arraysize += BIGARRAY; number=(double *)exrealloc((char *)number,arraysize*sizeof(double)); } } } if(cnt <=0) { sprintf(errstr,"Invalid or missing data.\n"); do_error(); } fclose(fp[0]); } /************************* DO_PITCHTEXT_INFILE ***************************/ void do_pitchtext_infile(char *argv) { char *p; char temp2[200]; if((fp[0] = fopen(argv,"r"))==NULL) { sprintf(errstr,"Cannot open infile %s\n",argv); do_error(); } while(fgets(temp,200,fp[0])!=NULL) { p = temp; while(strgetstr(&p,temp2)) { number[cnt] = texttomidi(temp2); if(++cnt >= arraysize) { arraysize += BIGARRAY; number=(double *)exrealloc((char *)number,arraysize*sizeof(double)); } } } if(cnt <=0) { sprintf(errstr,"ERROR: Invalid or missing data.\n"); do_error(); } fclose(fp[0]); } /************************* DO_OTHER_INFILE **********************/ void do_other_infiles(char *argv[]) { int n; if(infilecnt>2) fp = (FILE **)exrealloc((char *)fp,infilecnt*sizeof(FILE *)); firstcnt = cnt; for(n=1;nupperbnd) || (f1 MIDIMAX) { fprintf(stdout,"ERROR: MIDI value %d (%lf) is out of range for conversion to pitch.\n", n+1,number[n]); fflush(stdout); exit(1); } } else { if(number[n] < MIDIMINFRQ || number[n] > MIDIMAXFRQ) { fprintf(stdout,"ERROR: frq value %d (%lf) is out of range for conversion to pitch.\n", n+1,number[n]); fflush(stdout); exit(1); } } } for(n=0;n .5) basetone++; prnt_pitchclass(basetone%12,oct); if(flteq(diff,0.0)) strcat(errstr,"\n"); else if(flteq(diff,0.5)) /* handle quarter tones */ strcat(errstr,"+\n"); else if(diff > .5) strcat(errstr,"(--)\n"); else strcat(errstr,"(++)\n"); fprintf(stdout,"INFO: %s",errstr); } fflush(stdout); } /****************************** PRNT_PITCHCLASS ******************************/ void prnt_pitchclass(int z,int oct) { switch(z) { case(0): sprintf(errstr,"C%d",oct); break; case(1): sprintf(errstr,"C#%d",oct); break; case(2): sprintf(errstr,"D%d",oct); break; case(3): sprintf(errstr,"Eb%d",oct); break; case(4): sprintf(errstr,"E%d",oct); break; case(5): sprintf(errstr,"F%d",oct); break; case(6): sprintf(errstr,"F#%d",oct); break; case(7): sprintf(errstr,"G%d",oct); break; case(8): sprintf(errstr,"Ab%d",oct); break; case(9): sprintf(errstr,"A%d",oct); break; case(10): sprintf(errstr,"Bb%d",oct); break; case(11): sprintf(errstr,"B%d",oct); break; } } //RWD Nov 2025 nb this func uses global var 'number' (columns.h), TODO: make local, somehow! void bublsort(void) { int n, m; double dtemp; for(n=0;n= total_space) { while(stringstoresize >= total_space) total_space += space_step; if((zong = (char *)malloc(total_space))==NULL) { sprintf(errstr,"Out of Memory\n"); do_error(); } memcpy(zong,stringstore,old_stringstoresize); free(stringstore); stringstore = zong; } strcpy(stringstore + stringstart,temp2); stringstart += strspace; stringscnt++; cccnt++; } if(lcnt == 0) { ccnt = cccnt; lcnt++; } else if(ccnt != cccnt) { if(cccnt != 0) { fprintf(stdout,"ERROR: File %s is not a true table file (line %d has %d cols instead of %d).\n", argv,lcnt+1,cccnt,ccnt); fflush(stdout); exit(1); } else { continue; } } else { lcnt++; } } if(stringscnt <= n) { fprintf(stdout,"ERROR: Invalid or missing data.\n"); fflush(stdout); exit(1); } if(strings == 0) { if((strings = (char **)malloc(stringscnt * sizeof(char *)))==NULL) { sprintf(errstr,"Out of Memory\n"); do_error(); } } else { if((strings = (char **)realloc((char *)strings,stringscnt * sizeof(char *)))==NULL) { sprintf(errstr,"Out of Memory\n"); do_error(); } } p = stringstore; nn = 0; while(nn < stringscnt) { strings[nn] = p; while(*p != ENDOFSTR) p++; p++; nn++; } fclose(fp[0]); return ccnt; } /************************* DO_OTHER_STRINGLINE_INFILE ***************************/ void do_other_stringline_infiles(char *argv[],char c) { int n; int sum, thiscolcnt, last_stringscnt, this_stringscnt, rowcnt=0, thisrowcnt; if((cntr = (int *)malloc(infilecnt*sizeof(int)))==NULL) { fprintf(stdout,"ERROR: Out of memory.\n"); fflush(stdout); exit(1); } cntr[0] = stringscnt; sum = cntr[0]; if(c == 'J') { if((rowcnt = stringscnt/colcnt) * colcnt != stringscnt) { fprintf(stdout, "ERROR: Incomplete table 1 : all rows must have same number of columns for this option.\n"); fflush(stdout); exit(1); } } for(n=1;n= total_space) { while(stringstoresize >= total_space) total_space += space_step; if((stringstore = (char *)realloc((char *)stringstore,total_space))==NULL) { sprintf(errstr,"Out of Memory\n"); do_error(); } } strcpy(stringstore + stringstart,temp2); stringstart += strspace; stringscnt++; } } if(stringscnt <=0) { sprintf(errstr,"Invalid or missing data.\n"); do_error(); } if(strings == 0) { if((strings = (char **)malloc(stringscnt * sizeof(char *)))==NULL) { sprintf(errstr,"Out of Memory\n"); do_error(); } } else { if((strings = (char **)realloc((char *)strings,stringscnt * sizeof(char *)))==NULL) { sprintf(errstr,"Out of Memory\n"); do_error(); } } p = stringstore; n = 0; while(n < startstringscnt) { while(*p != ENDOFSTR) p++; p++; n++; } while(n < stringscnt) { strings[n] = p; while(*p != ENDOFSTR) p++; p++; n++; } fclose(fp[0]); } /************************* TEST_WARP_DATA ***************************/ void test_warp_data(void) { int n = 0; if(number[n] < 0) { sprintf(errstr,"Negative time in breakpoint file: Cannot proceed.\n"); do_error(); exit(1); } if(number[1] < 1.0/1024.0 || number[1] > 1024.0) { sprintf(errstr,"Dubious Timestretch value (%lf) in brkpnt file.\n",number[1]); do_error(); exit(1); } for(n = 2; n 1024.0) { sprintf(errstr,"Dubious Timestretch value (%lf) in brkpnt file.\n",number[n+1]); do_error(); exit(1); } } for(n = firstcnt; n