logistic.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sfsys.h>
  4. #include <math.h>
  5. #include <ctype.h>
  6. #include <logic.h>
  7. #include <Osbind.h>
  8. #ifdef unix
  9. #define round(x) lround((x))
  10. #else
  11. #define round(x) cdp_round((x))
  12. #endif
  13. //logistic_cycle_min(1) 0
  14. //logistic_cycle_max(1) 2.893
  15. //logistic_cycle_min(2) 2.894
  16. //logistic_cycle_max(2) 3.398
  17. //logistic_cycle_min(4) 3.399
  18. //logistic_cycle_max(4) 3.527
  19. //logistic_cycle_min(8) 3.528
  20. //logistic_cycle_max(8) 3.560
  21. //logistic_cycle_min(16) 3.561
  22. //logistic_cycle_max(16) 3.567
  23. //logistic_cycle_min(32) 3.568
  24. //logistic_cycle_max(32) 3.569
  25. //logistic_cycle_max(64) 3.570
  26. //logistic_cycle_max(64) 3.571
  27. //logistic_cycle_max(ch) 3.572
  28. //logistic_cycle_max(ch) 4.000
  29. #define CONTINUE (1)
  30. #define FINISHED (0)
  31. #define DATA_ERROR (-3) /* Data is unsuitable, incorrect or missing */
  32. #define FALSE 0
  33. #define TRUE 1
  34. #define PERROR 0.0002
  35. #define FLTERR 0.000002
  36. #define XSTEP 0.001
  37. #define ENDOFSTR ('\0')
  38. #define NEWLINE ('\n')
  39. // For a 3 8va range, we have 36 semitones and 3600 cents
  40. // We will assume that tunings less than 1 cent apart or in fact equal ... this gives an error bound of 0.0002
  41. // FLTERR is considerably more demanding than that
  42. static int cyclic(double *iterates,double *setareti,int *cnt,int tailend);
  43. static void usage(void);
  44. static int flteq(double f1,double f2);
  45. static int get_r_range(char *str,double *rlim);
  46. static int get_tstep_range(char *str,double *tstep);
  47. static int get_float_from_within_string(char **str,double *val);
  48. int main(int argc,char *argv[])
  49. {
  50. int exit_status, cflag = 0;
  51. int cnt, tailend, limitend;
  52. char temp[200], filename[2000], thisfilename[2000];
  53. int n, m, cycles, use_limitdur = 0;
  54. int do_output, iterating;
  55. double r, rr, x, y, rlim[2], tstep[2],rrange, rstep, tsteprange =0.0;
  56. double iterates[32768], setareti[32768];
  57. double timestep, timerand, taillen, limitdur, time, thistime, trand, pmin, pmax, prang, pval;
  58. int datacount, seed;
  59. FILE *fp;
  60. if(argc < 11 || argc > 12)
  61. usage();
  62. if(sscanf(argv[1],"%s",filename)!=1) {
  63. fprintf(stdout,"INFO: Invalid generic output filename.\n");
  64. fflush(stdout);
  65. exit(1);
  66. }
  67. if((exit_status = get_r_range(argv[2],rlim)) == CONTINUE) {
  68. if(sscanf(argv[2],"%lf",&r)!=1) {
  69. fprintf(stdout,"INFO: Invalid logistic constant.\n");
  70. fflush(stdout);
  71. exit(1);
  72. }
  73. if(r <= 0.0 || r > 4.0) {
  74. fprintf(stdout,"INFO: Logistic constant out of range (>0 - 4).\n");
  75. fflush(stdout);
  76. exit(1);
  77. }
  78. if(r > 3.571)
  79. use_limitdur = 1;
  80. rlim[0] = r;
  81. rlim[1] = r;
  82. } else if(exit_status < 0)
  83. exit(1);
  84. if(rlim[0] > rlim[1]) {
  85. rr = rlim[0];
  86. rlim[0] = rlim[1];
  87. rlim[1] = rr;
  88. }
  89. if((exit_status = get_tstep_range(argv[3],tstep)) == CONTINUE) {
  90. if(sscanf(argv[3],"%lf",&timestep)!=1) {
  91. fprintf(stdout,"INFO: Invalid timestep.\n");
  92. fflush(stdout);
  93. exit(1);
  94. }
  95. if(timestep <= 0.02 || timestep > 1.0) {
  96. fprintf(stdout,"INFO: Timestep out of range (0.02 to 1)\n");
  97. fflush(stdout);
  98. exit(1);
  99. }
  100. tstep[0] = timestep;
  101. tstep[1] = timestep;
  102. } else if(exit_status < 0)
  103. exit(1);
  104. tsteprange = tstep[1] - tstep[0];
  105. if(sscanf(argv[4],"%lf",&timerand)!=1) {
  106. fprintf(stdout,"INFO: Invalid timerand\n");
  107. fflush(stdout);
  108. exit(1);
  109. }
  110. if(timerand < 0.0 || timerand >= 1) {
  111. fprintf(stdout,"INFO: Timerand out of range (0 to <1)\n");
  112. fflush(stdout);
  113. exit(1);
  114. }
  115. if(sscanf(argv[5],"%lf",&taillen)!=1) {
  116. fprintf(stdout,"INFO: Invalid tail length\n");
  117. fflush(stdout);
  118. exit(1);
  119. }
  120. if(taillen < 0.0 || taillen > 60.0) {
  121. fprintf(stdout,"INFO: taillength out of range (0-60)\n");
  122. fflush(stdout);
  123. exit(1);
  124. }
  125. if(taillen/tstep[0] > 32767.0) {
  126. fprintf(stdout,"INFO: This taillength with this timestep generates too many output values to store.\n");
  127. fflush(stdout);
  128. exit(1);
  129. }
  130. if(sscanf(argv[6],"%lf",&limitdur)!=1) {
  131. fprintf(stdout,"INFO: Invalid tail length\n");
  132. fflush(stdout);
  133. exit(1);
  134. }
  135. if(limitdur <= 0.0) {
  136. fprintf(stdout,"INFO: Invalid limit (must be > 0)\n");
  137. fflush(stdout);
  138. exit(1);
  139. }
  140. if(limitdur/tstep[0] > 32767.0) {
  141. fprintf(stdout,"INFO: This limit with this timestep generates too many output values to store.\n");
  142. fflush(stdout);
  143. exit(1);
  144. }
  145. if(sscanf(argv[7],"%d",&datacount)!=1) {
  146. fprintf(stdout,"INFO: Invalid datacount\n");
  147. fflush(stdout);
  148. exit(1);
  149. }
  150. if(datacount <= 0) {
  151. fprintf(stdout,"INFO: Invalid output datacount (must be > 0)\n");
  152. fflush(stdout);
  153. exit(1);
  154. }
  155. if(sscanf(argv[8],"%d",&seed)!=1) {
  156. fprintf(stdout,"INFO: Invalid seed value\n");
  157. fflush(stdout);
  158. exit(1);
  159. }
  160. if(seed <= 0) {
  161. fprintf(stdout,"INFO: Invalid sedd value (must be > 0)\n");
  162. fflush(stdout);
  163. exit(1);
  164. }
  165. if(sscanf(argv[9],"%lf",&pmin)!=1) {
  166. fprintf(stdout,"INFO: Invalid minimum MIDI pitch\n");
  167. fflush(stdout);
  168. exit(1);
  169. }
  170. if(pmin < 0 || pmin > 127) {
  171. fprintf(stdout,"INFO: Invalid minimum MIDI pitch\n");
  172. fflush(stdout);
  173. exit(1);
  174. }
  175. if(sscanf(argv[10],"%lf",&pmax)!=1) {
  176. fprintf(stdout,"INFO: Invalid maximum MIDI pitch\n");
  177. fflush(stdout);
  178. exit(1);
  179. }
  180. if(pmax < 0 || pmax > 127) {
  181. fprintf(stdout,"INFO: Invalid maximum MIDI pitch\n");
  182. fflush(stdout);
  183. exit(1);
  184. }
  185. if(pmin > pmax) {
  186. trand = pmin;
  187. pmin = pmax;
  188. pmax = trand;
  189. }
  190. prang = pmax - pmin;
  191. if(prang < 1) {
  192. fprintf(stdout,"INFO: MIDI pitch range less than a semitone.\n");
  193. fflush(stdout);
  194. exit(1);
  195. }
  196. if(argc == 12) {
  197. if(strcmp(argv[11],"-c")) {
  198. fprintf(stdout,"INFO: Invalid last parameter\n");
  199. fflush(stdout);
  200. exit(1);
  201. } else
  202. cflag = 1;
  203. }
  204. tailend = (int)round(taillen/tstep[0]);
  205. limitend = (int)round(limitdur/tstep[0]);
  206. // "USAGE: logistic outfilename R timestep timerand taillen limitdur datacount seed\n"
  207. srand(seed);
  208. rrange = rlim[1] - rlim[0];
  209. rstep = rrange/(double)datacount;
  210. rr = rlim[0];
  211. for(n = 0; n < datacount;n++) {
  212. sprintf(temp,"%d.txt",n+1);
  213. strcpy(thisfilename,filename);
  214. strcat(thisfilename,temp);
  215. x = drand48();
  216. cycles = 0;
  217. cnt = 0;
  218. iterating = 1;
  219. do_output = 0;
  220. timestep = (drand48() * tsteprange) + tstep[0];
  221. while(iterating) {
  222. iterates[cnt] = x;
  223. y = 1 - x;
  224. x = rr * x * y;
  225. cnt++;
  226. if(cnt >= limitend) {
  227. if(use_limitdur)
  228. do_output = 1;
  229. else {
  230. if(cflag < 2) {
  231. fprintf(stdout,"WARNING: Encountered non-cyclic data unexpectedly for r = %lf: Is limit too short??\n",rr);
  232. fflush(stdout);
  233. }
  234. if(cflag == 0)
  235. exit(1);
  236. else {
  237. cflag = 2;
  238. do_output = 1;
  239. }
  240. }
  241. } else if(cnt > 2) {
  242. if((cycles = cyclic(iterates,setareti,&cnt,tailend)) > 0)
  243. do_output = 1;
  244. }
  245. if(do_output) {
  246. fp = fopen(thisfilename,"w");
  247. time = 0.0;
  248. thistime = time;
  249. for(m = 0; m < cnt;m++) {
  250. pval = (iterates[m] * prang) + pmin;
  251. sprintf(temp,"%lf\t%lf\n",thistime,pval);
  252. fputs(temp,fp);
  253. time += timestep;
  254. thistime = time;
  255. if(timerand > 0.0) {
  256. trand = ((drand48() * 2.0) - 1.0); // Range +- 1
  257. trand *= timerand/2.0; // Range +- 1/2 * timerand (= max rand deviation)
  258. trand *= timestep; // fraction of actual step between times
  259. thistime += trand;
  260. }
  261. }
  262. fclose(fp);
  263. iterating = 0;
  264. }
  265. }
  266. rr += rstep;
  267. rr = min(rr,rlim[1]);
  268. if(rr > 3.571)
  269. use_limitdur = 1;
  270. }
  271. }
  272. /**************************** CYCLIC *******************************/
  273. int cyclic(double *iterates,double *setareti,int *cnt,int tailend)
  274. {
  275. int k, m, j = 0, OK = 0, precyc;
  276. double nowval;
  277. for(k = *cnt - 1,m= 0; k >= 0;k--,m++)
  278. setareti[m] = iterates[k]; // Copy the sequence in reverse order (the last value becomes first etc)
  279. for(m = 1; m < 64; m*=2) { // For every possible step between outputs (step by 1, by 2, by 4 etc)
  280. if(m > (*cnt)/2) // Cycle can involve max of half total number of entries
  281. return 0;
  282. OK = 1; // It's period doubling were looking for, so each group of2,4,8 etc items should cycle
  283. for(j = 0;j < m;j++) { // For every item 0,1,2 ... M-1
  284. nowval = setareti[j]; // get the (start)value
  285. for(k = j+m; k < m*2; k += m) { // For every item that is step-m away, compare it with startvalue
  286. if(!flteq(nowval,setareti[k])) {
  287. OK = 0; // If they're not equal, we don't have a cycle of length m, so set OK to 0
  288. break;
  289. }
  290. }
  291. if(!OK) // If we don't have a cycle of length m, go to next value of m (2 times bigger)
  292. break;
  293. }
  294. if(OK) // If all the j cycles checked out, we DO have a cycle of length m, so quit
  295. break;
  296. }
  297. if(m >= 64) // If we never found any cycle, m will have become >= 128: so return 0
  298. return 0;
  299. if(!OK) // Should be redundant
  300. return 0;
  301. // Find start of cycling behaviour Using the found cycle value (m)
  302. precyc = 0; // move through the array, finding where the cycling really began
  303. while(precyc <= *cnt - (m*2)) {
  304. for(j = precyc;j < m+precyc;j++) { // For every item precyc + 0,1,2 ... M-1
  305. nowval = setareti[j]; // get the (start)value
  306. for(k = j+m; k < precyc+(m*2); k += m) { // For every item that is step-m away, compare it with startvalue
  307. if(!flteq(nowval,setareti[k])) {
  308. OK = 0; // If they're not equal, we don't have a cycle of length m, so set OK to 0
  309. break;
  310. }
  311. }
  312. if(!OK)
  313. break;
  314. }
  315. if(!OK)
  316. break;
  317. precyc += m; // precyc gets set at point in (reverse) array where cycling stops
  318. } // So (cnt - precyc) is where cycliung begins
  319. if(precyc > tailend) // If cycling is longer than required tail ... shorten it
  320. *cnt -= (precyc - tailend);
  321. else if(precyc < tailend) { // Else if tailend needs to be longer ...
  322. k = tailend - precyc; // Additional length
  323. if((k += *cnt) >= 32768) // New end-length
  324. k = 32768; // Set true end of output array, with tail added
  325. j = *cnt - m; // Copy the endcycle to beyond current data end
  326. while(*cnt < k)
  327. iterates[(*cnt)++] = iterates[j++];
  328. }
  329. return m;
  330. }
  331. /**************************** USAGE *******************************/
  332. void usage()
  333. {
  334. fprintf(stdout,
  335. "USAGE: \n"
  336. "logistic outname R timestep timerand taillen limit datacount seed pmin pmax [-c]\n"
  337. "\n"
  338. "Calculate the iterations of the logistic equation,\n"
  339. "for a specific value (or range of vals) of the logistic equation constant 'R'.\n"
  340. "Associate the successive iterated vals with (untempered) MIDI-pitches,\n"
  341. "and with times, which start at zero, calculating further time values\n"
  342. "using \"timestep\" and \"timerand\".\n"
  343. "\n"
  344. "The output (of one pass) is a textfile with a list of time:MIDIval pairs.\n"
  345. "The process runs \"DATACOUNT\" times, outputting \"datacount\" textfiles,\n"
  346. "with different (random) starting values for each iteration.\n"
  347. "\n"
  348. "Once cyclical behaviour sets in,\n"
  349. "the process continue outputting values for a specified duration (\"TAILLEN\").\n"
  350. "If \"R\" is in the chaotic range (>=3.571), cyclical behaviour is unlikely,\n"
  351. "so an output of duration \"LIMIT\" is produced.\n"
  352. "\n"
  353. "\"R\" can be a number or a textfile with 2 numbers - bottom and top of range.\n"
  354. "If input as range, successive iterations increase \"R\" from bottom to top of range.\n"
  355. "\n"
  356. "\"timestep\" can be numeric or textfile with 2 numbers - bottom & top of range.\n"
  357. "If input as range, successive iterations choose timestep at random within range.\n"
  358. "\n"
  359. "The output values in the iteration process lie in the range 0 to 1.\n"
  360. "These will be mapped into the MIDI pitch range defined by PMIN and PMAX.\n"
  361. "\n"
  362. "OUTNAME generic name for output data files.\n"
  363. " These will take names \"myname1\",\"myname2\" etc.\n"
  364. "R The constant in the logistic equation (Range 0-4).\n"
  365. "TIMESTEP (average) timestep between output values (Range 0.02 to 1).\n"
  366. "TIMERAND Randomisation of times: Range 0 - 1.\n"
  367. "TAILLEN Length of output once cycling begins.\n"
  368. "LIMIT Maximum duration of output (for chaotic behaviour).\n"
  369. " NB If set too short, some of \"DATACOUNT\" outputs may not be generated.\n"
  370. "DATACOUNT Number of output sets to produce.\n"
  371. "SEED Set seed value to initialise the rand selection.\n"
  372. "PMIN Minimum MIDI pitch of output.\n"
  373. "PMAX Maximum MIDI pitch of output.\n"
  374. "-c If noncyclic data encountered unexpectedly, warn but continue.\n"
  375. " (Default, unexpected non-cyclic data causes process to stop).\n");
  376. fflush(stdout);
  377. exit(1);
  378. }
  379. /**************************** FLTEQ *******************************/
  380. int flteq(double f1,double f2)
  381. {
  382. double upperbnd, lowerbnd;
  383. upperbnd = f2 + PERROR;
  384. lowerbnd = f2 - PERROR;
  385. if((f1>upperbnd) || (f1<lowerbnd))
  386. return(FALSE);
  387. return(TRUE);
  388. }
  389. /**************************** GET_R_RANGE ****************************/
  390. int get_r_range(char *str,double *rlim)
  391. {
  392. FILE *fp;
  393. double dummy;
  394. char temp[200], *p;
  395. int cnt = 0;
  396. if((fp = fopen(str,"r"))==NULL)
  397. return(CONTINUE);
  398. while(fgets(temp,200,fp)!=NULL) {
  399. p = temp;
  400. while(isspace(*p))
  401. p++;
  402. if(*p == ';' || *p == ENDOFSTR) // Allow comments in file
  403. continue;
  404. while(get_float_from_within_string(&p,&dummy)) {
  405. if(cnt >= 2) {
  406. fprintf(stdout,"INFO: Data in \"r\" file should be just 2 values between 0 and 4\n");
  407. fflush(stdout);
  408. return(DATA_ERROR);
  409. }
  410. if(dummy < 0 || dummy > 4) {
  411. fprintf(stdout,"INFO: Data in \"r\" file should be just 2 values between 0 and 4\n");
  412. fflush(stdout);
  413. return(DATA_ERROR);
  414. }
  415. rlim[cnt] = dummy;
  416. cnt++;
  417. }
  418. }
  419. fclose(fp);
  420. if(cnt != 2) {
  421. fprintf(stdout,"INFO: Data in \"r\" file should be 2 values between 0 and 4\n");
  422. fflush(stdout);
  423. return(DATA_ERROR);
  424. }
  425. return FINISHED;
  426. }
  427. /**************************** GET_TSTEP_RANGE ****************************/
  428. int get_tstep_range(char *str,double *tstep)
  429. {
  430. FILE *fp;
  431. double dummy;
  432. char temp[200], *p;
  433. int cnt = 0;
  434. if((fp = fopen(str,"r"))==NULL)
  435. return(CONTINUE);
  436. while(fgets(temp,200,fp)!=NULL) {
  437. p = temp;
  438. while(isspace(*p))
  439. p++;
  440. if(*p == ';' || *p == ENDOFSTR) // Allow comments in file
  441. continue;
  442. while(get_float_from_within_string(&p,&dummy)) {
  443. if(cnt >= 2) {
  444. fprintf(stdout,"INFO: Data in \"tiemstep\" file should be just 2 values between 0.02 and 1\n");
  445. fflush(stdout);
  446. return(DATA_ERROR);
  447. }
  448. if(dummy < 0.02 || dummy > 1) {
  449. fprintf(stdout,"INFO: Data in \"tiemstep\" file should be just 2 values between 0.02 and 1\n");
  450. fflush(stdout);
  451. return(DATA_ERROR);
  452. }
  453. tstep[cnt] = dummy;
  454. cnt++;
  455. }
  456. }
  457. fclose(fp);
  458. if(cnt != 2) {
  459. fprintf(stdout,"INFO: Data in \"timestep\" file should be 2 values between 0.02 and 1\n");
  460. fflush(stdout);
  461. return(DATA_ERROR);
  462. }
  463. if(tstep[0] > tstep[1]) {
  464. dummy = tstep[0];
  465. tstep[0] = tstep[1];
  466. tstep[1] = dummy;
  467. }
  468. return FINISHED;
  469. }
  470. /************************** GET_FLOAT_FROM_WITHIN_STRING **************************
  471. * takes a pointer TO A POINTER to a string. If it succeeds in finding
  472. * a float it returns the float value (*val), and it's new position in the
  473. * string (*str).
  474. */
  475. int get_float_from_within_string(char **str,double *val)
  476. {
  477. char *p, *valstart;
  478. int decimal_point_cnt = 0, has_digits = 0;
  479. p = *str;
  480. while(isspace(*p))
  481. p++;
  482. valstart = p;
  483. switch(*p) {
  484. case('-'): break;
  485. case('.'): decimal_point_cnt=1; break;
  486. default:
  487. if(!isdigit(*p))
  488. return(FALSE);
  489. has_digits = TRUE;
  490. break;
  491. }
  492. p++;
  493. while(!isspace(*p) && *p!=NEWLINE && *p!=ENDOFSTR) {
  494. if(isdigit(*p))
  495. has_digits = TRUE;
  496. else if(*p == '.') {
  497. if(++decimal_point_cnt>1)
  498. return(FALSE);
  499. } else
  500. return(FALSE);
  501. p++;
  502. }
  503. if(!has_digits || sscanf(valstart,"%lf",val)!=1)
  504. return(FALSE);
  505. *str = p;
  506. return(TRUE);
  507. }