convert_to_midi.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  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. * CREATE STANDARD MIDI FILE FROM CDP FRQ BRKPNT FILE FOR STABLE PITCHES (WHICH HAS NOTE STARTS AND ENDS)
  23. *
  24. * Assumes a single voice, using a sigle MIDI channel (0)
  25. * and note-off occurs before following note-on
  26. *
  27. * If "staccato" is set to 0, program reads note on and note off times.
  28. * If "staccato" > 0, note-offs are generated from note ons, and note-ends in input file are ignored.
  29. *
  30. * CALL WITH
  31. * convert_to_midi(seqfile,envfile,infiledatalen,staccato,outfilename,system);
  32. *
  33. * where infiledatalen is no of midi notes in input = 1/4 of entries in frq-brkpnt file.
  34. */
  35. #include <stdio.h>
  36. #include <stdlib.h>
  37. #include <ctype.h>
  38. #include <string.h>
  39. #include <math.h>
  40. #include <sfsys.h> // RWD for macro defs
  41. //#if defined unix || defined __GNUC__
  42. #define round(x) lround((x))
  43. //#else
  44. //# ifndef round
  45. //static int round(double d);
  46. //static int twround(double d); //RWD
  47. //#define round(x) twround((x)) //RWD
  48. //# endif
  49. //#endif
  50. #define MIDI_NOTE_ON (unsigned char)144 /* 0x90 = 1001 0000 = 1001 is Note-on : 0000 is channel number */
  51. #define MIDI_NOTE_OFF (unsigned char)128 /* 0x80 = 1000 0000 = 1001 is Note-on : 0000 is channel number */
  52. #define LOW_A 6.875 /* Frequency of A below MIDI 0 */
  53. #define CONVERT_LOG10_TO_LOG2 3.321928 /* MiditoHz conversions */
  54. #define TRUE 1
  55. #define FALSE 0
  56. #define NEWLINE ('\n')
  57. #define ENDOFSTR ('\0')
  58. static void Write_ThisIsAMIDI_MThdChunk_with_mS_timings (char format_type,char track_cnt,FILE *fp);
  59. static void WriteTrackID (unsigned int midi_track_data_bytes,FILE *fp);
  60. static void WriteVarLen (unsigned int value,FILE *fp);
  61. static void WriteNormalLong (unsigned int value,FILE *fp);
  62. static void Write_TrackEnd (FILE *fp);
  63. static void create_note(double timestep,char midi,double thislevel,int on,FILE *fp);
  64. static char HzToMidi (double frq);
  65. static int get_float_from_within_string(char **str,double *val);
  66. static int midi_data_bytes_in_midi_track (int infiledatalen,double *seqdata,double staccato);
  67. static int CalcVarLen (unsigned int value);
  68. static void Write_ThisIsAMIDI_PC_header(unsigned int total_bytes_in_file,FILE *fp);
  69. #ifndef round
  70. static int round(double d);
  71. #endif
  72. int main(int argc,char *argv[])
  73. {
  74. FILE *fp;
  75. double *seqdata, *envdata, *p, *end;
  76. char *q;
  77. double lasttime, thistime, nexttime, timestep, thislevel, stacdur;
  78. int on, padbyte;
  79. int n,m,k;
  80. unsigned int midi_track_data_bytes, total_bytes_in_file;
  81. char temp[200];
  82. int system;
  83. char seqfile[64];
  84. char envfile[64];
  85. int infiledatalen;
  86. double staccato;
  87. char outfilename[64];
  88. char thismidi;
  89. if(argc == 1) {
  90. fprintf(stdout,"Converts frqequency-brkpnt and peakdata textfiles to a standard midi data file.\n");
  91. fprintf(stdout,"\n");
  92. fprintf(stdout,"USAGE:\n");
  93. fprintf(stdout,"convert_to_midi frqbrkpnt peakvals datalen staccato outfilename system\n");
  94. fprintf(stdout,"\n");
  95. fprintf(stdout,"FRQBRKPNT File of time-frq pairs for note starts and ends.\n");
  96. fprintf(stdout,"PEAKVALS Levels of each note (Range 0-1).\n");
  97. fprintf(stdout,"DATALEN Number of peaks (no. of vals in 'frqbrkpnt' should be datalen * 4)\n");
  98. fprintf(stdout,"STACCATO Force staccato output.\n");
  99. fprintf(stdout,"OUTFILENAME (File extension will be forced to '.mid' or '.rmi')\n");
  100. fprintf(stdout,"SYSTEM 0 gives Standard Midi File, with extension '.mid'\n");
  101. fprintf(stdout," 1 gives PC Midi File, with extension '.rmi'\n");
  102. fflush(stdout);
  103. return 1;
  104. }
  105. if(argc != 7) {
  106. fprintf(stdout,"ERROR: WRONG NUMBER OF ARGUMENTS\n");
  107. fflush(stdout);
  108. return 0;
  109. }
  110. strcpy(seqfile,argv[1]);
  111. strcpy(envfile,argv[2]);
  112. if(sscanf(argv[3],"%d",&infiledatalen)<1) {
  113. fprintf(stdout,"ERROR: CANNOT READ INFILE DATA LENGTH [%s].\n",argv[3]);
  114. fflush(stdout);
  115. return 0;
  116. }
  117. if(sscanf(argv[4],"%lf",&staccato)<1) {
  118. fprintf(stdout,"ERROR: CANNOT READ STACCATO DURATION [%s].\n",argv[4]);
  119. fflush(stdout);
  120. return 0;
  121. }
  122. strcpy(outfilename,argv[5]);
  123. q = outfilename + strlen(outfilename);
  124. q--;
  125. while(*q != '.') {
  126. q--;
  127. if(q <= outfilename)
  128. break;
  129. }
  130. if(q > outfilename)
  131. *q = ENDOFSTR;
  132. if(sscanf(argv[6],"%d",&system)<1) {
  133. fprintf(stdout,"ERROR: CANNOT READ HEADER TYPE [%s].\n",argv[4]);
  134. fflush(stdout);
  135. return 0;
  136. }
  137. if(system == 1)
  138. strcat(outfilename,".rmi");
  139. else
  140. strcat(outfilename,".mid");
  141. if((seqdata = (double *)malloc((infiledatalen * 4) * sizeof(double)))==NULL) {
  142. fprintf(stdout,"ERROR: INSUFFICIENT MEMORY TO CREATE INPUT DATA ARRAY.\n");
  143. fflush(stdout);
  144. return 0;
  145. }
  146. if((envdata = (double *)malloc(infiledatalen * sizeof(double)))==NULL) {
  147. fprintf(stdout,"ERROR: INSUFFICIENT MEMORY TO CREATE LOUDNESS DATA ARRAY.\n");
  148. fflush(stdout);
  149. return 0;
  150. }
  151. p = seqdata;
  152. if((fp = fopen(seqfile,"r"))==NULL) {
  153. fprintf(stdout,"ERROR: FAILED TO OPEN FILE %s TO READ SEQUENCE DATA\n",seqfile);
  154. fflush(stdout);
  155. return 0;
  156. }
  157. end = p + (infiledatalen * 4);
  158. while(fgets(temp,200,fp)!=NULL) { /* READ AND TEST MIDI DATA INFO, ASSUMING IT TO BE IN CORRECT FORMAT!! */
  159. q = temp;
  160. while(get_float_from_within_string(&q,p)) {
  161. p++;
  162. if (p >= end)
  163. break;
  164. }
  165. }
  166. fclose(fp);
  167. if(p - seqdata != infiledatalen * 4) {
  168. fprintf(stdout,"ERROR: COUNT OF MIDI DATA ITEMS DOES NOT CORRESPOND TO PARAM FOR INPUT DATA LENGTH\n");
  169. fflush(stdout);
  170. return 0;
  171. }
  172. p = envdata;
  173. end = p + infiledatalen;
  174. if((fp = fopen(envfile,"r"))==NULL) {
  175. fprintf(stdout,"ERROR: FAILED TO OPEN FILE %s TO READ LOUDNESS DATA\n",envfile);
  176. fflush(stdout);
  177. return 0;
  178. }
  179. while(fgets(temp,200,fp)!=NULL) { /* READ AND TEST MIDI DATA INFO, ASSUMING IT TO BE IN CORRECT FORMAT!! */
  180. q = temp;
  181. while(get_float_from_within_string(&q,p)) {
  182. p++;
  183. if (p >= end)
  184. break;
  185. }
  186. }
  187. fclose(fp);
  188. if(p - envdata != infiledatalen) {
  189. fprintf(stdout,"ERROR: COUNT OF LOUDNESS ITEMS DOES NOT CORRESPOND TO PARAM FOR INPUT DATA LENGTH\n");
  190. fflush(stdout);
  191. return 0;
  192. }
  193. if((fp = fopen(outfilename,"w"))==NULL) {
  194. fprintf(stdout,"ERROR: FAILED TO OPEN MIDI DATAFILE %s TO WRITE DATA\n",outfilename);
  195. fflush(stdout);
  196. return 0;
  197. }
  198. total_bytes_in_file = 14 /* MThd Chunk */ + 4 /*MTrk track identifier */ ;
  199. midi_track_data_bytes = midi_data_bytes_in_midi_track(infiledatalen,seqdata,staccato);
  200. total_bytes_in_file += CalcVarLen(midi_track_data_bytes) + midi_track_data_bytes;
  201. if((total_bytes_in_file/2)*2 != total_bytes_in_file) { /* Does (single) chunk have EVEN number of bytes? */
  202. padbyte = 1;
  203. } else {
  204. padbyte = 0;
  205. }
  206. if (system == 1)
  207. total_bytes_in_file += padbyte; /* For PC, force an EVEN chunk-size */
  208. switch(system) {
  209. case(0):
  210. break;
  211. case(1): /* PC */
  212. Write_ThisIsAMIDI_PC_header(total_bytes_in_file,fp);
  213. break;
  214. }
  215. Write_ThisIsAMIDI_MThdChunk_with_mS_timings ((char)0,(char)1,fp);
  216. /* Assuming format type 0, 1 track only */
  217. WriteTrackID (midi_track_data_bytes,fp);
  218. /* Assuming there is 1 track, numbered 0 */
  219. lasttime = 0.0;
  220. on = 1;
  221. k = 0;
  222. for(n=0,m=1;n < infiledatalen * 4;n+=2,m+=2) {
  223. thistime = seqdata[n];
  224. thismidi = (char)round(HzToMidi(seqdata[m]));
  225. timestep = thistime - lasttime;
  226. if (on) {
  227. thislevel = envdata[k++];
  228. create_note(timestep,thismidi,thislevel,1,fp);
  229. lasttime = thistime;
  230. if (staccato > 0.0) { /* If staccato, note off determined by staccato dur */
  231. if(m+4 < infiledatalen) {
  232. nexttime = seqdata[m+4];
  233. stacdur = (nexttime - thistime)/2.0;
  234. stacdur = min(stacdur,staccato);
  235. } else {
  236. stacdur = staccato;
  237. }
  238. create_note(stacdur,thismidi,thislevel,0,fp);
  239. lasttime = thistime + stacdur;
  240. }
  241. on = 0;
  242. } else {
  243. if (staccato <= 0.0) { /* IF not staccato, note-off taken from input data */
  244. create_note(timestep,thismidi,thislevel,0,fp);
  245. lasttime = thistime; /* But, if staccato, ignore the note off input */
  246. }
  247. on = 1;
  248. }
  249. }
  250. Write_TrackEnd(fp);
  251. if(system == 1) { /* PC riff format */
  252. if(padbyte)
  253. putc(0,fp);
  254. }
  255. fclose(fp);
  256. return 1;
  257. }
  258. /*********************** WRITE A 32-BIT REPRESENTATION OF "THIS is MIDI data = ascii MThd" ***********/
  259. void Write_ThisIsAMIDI_MThdChunk_with_mS_timings (char format_type,char track_cnt,FILE *fp)
  260. {
  261. register char buffer;
  262. buffer = 77; /* M */
  263. putc(buffer,fp);
  264. buffer = 84; /* T */
  265. putc(buffer,fp);
  266. buffer = 104; /* h */
  267. putc(buffer,fp);
  268. buffer = 100; /* d */
  269. putc(buffer,fp);
  270. buffer = 0; /* sizeof MThd chunk is 6 = 0006 */
  271. putc(buffer,fp);
  272. putc(buffer,fp);
  273. putc(buffer,fp);
  274. buffer = 6; /* sizeof MThd chunk is 6 = 0006 */
  275. putc(buffer,fp);
  276. buffer = 0; /* format_type 00 0x */
  277. putc(buffer,fp);
  278. buffer = format_type;
  279. putc(buffer,fp);
  280. buffer = 0; /* track_cnt 00 0x */
  281. putc(buffer,fp);
  282. buffer = track_cnt;
  283. putc(buffer,fp);
  284. buffer = (char)-25; /* -25 means 25 frames per sec */
  285. putc(buffer,fp);
  286. buffer = 40; /* 40 units per frame -> 1 unit per ms */
  287. putc(buffer,fp);
  288. }
  289. /*********************** WRITE TRACK ID ***********/
  290. void WriteTrackID (unsigned int midi_track_data_bytes,FILE *fp)
  291. {
  292. register char buffer;
  293. buffer = 77; /* M */
  294. putc(buffer,fp);
  295. buffer = 84; /* T */
  296. putc(buffer,fp);
  297. buffer = 114; /* r */
  298. putc(buffer,fp);
  299. buffer = 107; /* k */
  300. putc(buffer,fp);
  301. WriteVarLen(midi_track_data_bytes,fp);
  302. }
  303. /*********************** WRITE A 32-BIT int, SPLIT INTO 7-bit WORDS IN REVERSE BYTE ORDER = MIDI TIME REPRESENTATION ***********/
  304. void WriteVarLen (unsigned int value,FILE *fp)
  305. {
  306. register unsigned int buffer;
  307. buffer = value & 0x7F;
  308. while (value >>= 7) {
  309. buffer <<= 8;
  310. buffer |= ((value & 0x7F) | 0x80);
  311. }
  312. for (;;) {
  313. putc(buffer,fp);
  314. if(buffer & 0x80)
  315. buffer >>= 8;
  316. else
  317. break;
  318. }
  319. }
  320. /*********************** WRITE A 32-BIT int IN NORMAL ORDER ***********/
  321. void WriteNormalLong (unsigned int value,FILE *fp)
  322. {
  323. unsigned int buffer;
  324. int n = 0;
  325. while (n < 4) {
  326. buffer = value & 0xFF;
  327. putc(buffer,fp);
  328. value >>= 8;
  329. n++;
  330. }
  331. }
  332. /*********************** ASCERTAIN BYTE LENGTH OF MIDI TIME REPRESENTATION ***********/
  333. int CalcVarLen (unsigned int value)
  334. {
  335. register unsigned int buffer;
  336. int cnt = 0;
  337. buffer = value & 0x7F;
  338. while (value >>= 7) {
  339. buffer <<= 8;
  340. buffer |= ((value & 0x7F) | 0x80);
  341. }
  342. for (;;) {
  343. cnt++;
  344. if(buffer & 0x80)
  345. buffer >>= 8;
  346. else
  347. break;
  348. }
  349. return cnt;
  350. }
  351. /*********************** WRITE "END OF TRACK" ***********/
  352. void Write_TrackEnd (FILE *fp)
  353. {
  354. register char buffer;
  355. buffer = (char)-1; /* FF = |1111|1111| = -128 */
  356. putc(buffer,fp);
  357. buffer = (char)0x2F;
  358. putc(buffer,fp);
  359. buffer = 0;
  360. putc(buffer,fp);
  361. }
  362. /*********************** CREATE MIDI NOTE ON OR NOTE OFF DATA ***********/
  363. void create_note(double timestep,char midi,double thislevel,int on,FILE *fp)
  364. {
  365. register unsigned char buffer;
  366. unsigned int timedata;
  367. char velocity;
  368. timedata = (unsigned int)round(timestep * 1000.0); /* TIME IN mS */
  369. velocity = (char)round(thislevel * 127.0);
  370. WriteVarLen(timedata,fp);
  371. if (on)
  372. buffer = MIDI_NOTE_ON;
  373. else
  374. buffer = MIDI_NOTE_OFF;
  375. putc(buffer,fp);
  376. buffer = midi;
  377. putc(buffer,fp);
  378. buffer = velocity;
  379. putc(buffer,fp);
  380. }
  381. /*********************** CONVERT FRQ TO MIDI ***********/
  382. char HzToMidi (double frq) {
  383. double dmidi;
  384. char midi;
  385. if (frq < LOW_A) {
  386. frq = LOW_A;
  387. }
  388. dmidi = frq / LOW_A;
  389. dmidi = (log10(dmidi) * CONVERT_LOG10_TO_LOG2 * 12.0) - 3.0;
  390. midi = (char)round(dmidi);
  391. if (midi > 127) {
  392. midi = 127;
  393. } else if(midi < 0) {
  394. midi = 0;
  395. }
  396. return midi;
  397. }
  398. /************************** GET_FLOAT_FROM_WITHIN_STRING **************************
  399. * takes a pointer TO A POINTER to a string. If it succeeds in finding
  400. * a float it returns the float value (*val), and it's new position in the
  401. * string (*str).
  402. */
  403. int get_float_from_within_string(char **str,double *val)
  404. {
  405. char *p, *valstart;
  406. int decimal_point_cnt = 0, has_digits = 0;
  407. p = *str;
  408. while(isspace(*p))
  409. p++;
  410. valstart = p;
  411. switch(*p) {
  412. case('-'): break;
  413. case('.'): decimal_point_cnt=1; break;
  414. default:
  415. if(!isdigit(*p))
  416. return(FALSE);
  417. has_digits = TRUE;
  418. break;
  419. }
  420. p++;
  421. while(!isspace(*p) && *p!=NEWLINE && *p!=ENDOFSTR) {
  422. if(isdigit(*p))
  423. has_digits = TRUE;
  424. else if(*p == '.') {
  425. if(++decimal_point_cnt>1)
  426. return(FALSE);
  427. } else
  428. return(FALSE);
  429. p++;
  430. }
  431. if(!has_digits || sscanf(valstart,"%lf",val)!=1)
  432. return(FALSE);
  433. *str = p;
  434. return(TRUE);
  435. }
  436. /*********************** CALCULATE NUMBER OF DATA BYTES TO FOLLOW ***********/
  437. int midi_data_bytes_in_midi_track(int infiledatalen,double *seqdata,double staccato)
  438. {
  439. double thistime, nexttime, timestep, stacdur;
  440. double lasttime = 0.0;
  441. unsigned int timedata;
  442. unsigned int cnt = 0;
  443. int on = 1;
  444. int n,m;
  445. for(n=0,m=1;n < infiledatalen * 4;n+=2,m+=2) {
  446. thistime = seqdata[n];
  447. timestep = thistime - lasttime;
  448. lasttime = thistime;
  449. if (on) {
  450. timedata = (unsigned int)round(timestep * 1000.0); /* TIME IN mS */
  451. cnt += CalcVarLen(timedata);
  452. cnt += 3; /* MIDI status + key + velocity */
  453. if (staccato > 0.0) {
  454. if(m+4 < infiledatalen) {
  455. nexttime = seqdata[m+4];
  456. stacdur = (nexttime - thistime)/2.0;
  457. stacdur = min(stacdur,staccato);
  458. } else {
  459. stacdur = staccato;
  460. }
  461. timedata = (unsigned int)round(stacdur * 1000.0);
  462. cnt += CalcVarLen(timedata);
  463. cnt += 3;
  464. lasttime = thistime + stacdur;
  465. }
  466. on = 0;
  467. } else {
  468. if (staccato <= 0.0) {
  469. timedata = (unsigned int)round(timestep * 1000.0);
  470. cnt += CalcVarLen(timedata);
  471. cnt += 3;
  472. lasttime = thistime;
  473. }
  474. on = 1;
  475. }
  476. }
  477. cnt += 3; /* Add Track end bytes */
  478. return cnt;
  479. }
  480. void Write_ThisIsAMIDI_PC_header(unsigned int total_bytes_in_file,FILE *fp)
  481. {
  482. register char buffer;
  483. buffer = 82; /* R */
  484. putc(buffer,fp);
  485. buffer = 73; /* I */
  486. putc(buffer,fp);
  487. buffer = 70; /* F */
  488. putc(buffer,fp);
  489. buffer = 70; /* F */
  490. putc(buffer,fp);
  491. WriteNormalLong(total_bytes_in_file,fp);
  492. buffer = 82; /* R */
  493. putc(buffer,fp);
  494. buffer = 77; /* M */
  495. putc(buffer,fp);
  496. buffer = 73; /* I */
  497. putc(buffer,fp);
  498. buffer = 68; /* D */
  499. putc(buffer,fp);
  500. }
  501. //#ifndef round
  502. #if 0
  503. static int twround(double d)
  504. {
  505. d += 0.5;
  506. return (int)floor(d);
  507. }
  508. #endif
  509. //endif