| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545 |
- /*
- * 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
- *
- */
- /*
- * CREATE STANDARD MIDI FILE FROM CDP FRQ BRKPNT FILE FOR STABLE PITCHES (WHICH HAS NOTE STARTS AND ENDS)
- *
- * Assumes a single voice, using a sigle MIDI channel (0)
- * and note-off occurs before following note-on
- *
- * If "staccato" is set to 0, program reads note on and note off times.
- * If "staccato" > 0, note-offs are generated from note ons, and note-ends in input file are ignored.
- *
- * CALL WITH
- * convert_to_midi(seqfile,envfile,infiledatalen,staccato,outfilename,system);
- *
- * where infiledatalen is no of midi notes in input = 1/4 of entries in frq-brkpnt file.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h>
- #include <string.h>
- #include <math.h>
- #include <sfsys.h> // RWD for macro defs
- //#if defined unix || defined __GNUC__
- #define round(x) lround((x))
- //#else
- //# ifndef round
- //static int round(double d);
- //static int twround(double d); //RWD
- //#define round(x) twround((x)) //RWD
- //# endif
- //#endif
- #define MIDI_NOTE_ON (unsigned char)144 /* 0x90 = 1001 0000 = 1001 is Note-on : 0000 is channel number */
- #define MIDI_NOTE_OFF (unsigned char)128 /* 0x80 = 1000 0000 = 1001 is Note-on : 0000 is channel number */
- #define LOW_A 6.875 /* Frequency of A below MIDI 0 */
- #define CONVERT_LOG10_TO_LOG2 3.321928 /* MiditoHz conversions */
- #define TRUE 1
- #define FALSE 0
- #define NEWLINE ('\n')
- #define ENDOFSTR ('\0')
- static void Write_ThisIsAMIDI_MThdChunk_with_mS_timings (char format_type,char track_cnt,FILE *fp);
- static void WriteTrackID (unsigned int midi_track_data_bytes,FILE *fp);
- static void WriteVarLen (unsigned int value,FILE *fp);
- static void WriteNormalLong (unsigned int value,FILE *fp);
- static void Write_TrackEnd (FILE *fp);
- static void create_note(double timestep,char midi,double thislevel,int on,FILE *fp);
- static char HzToMidi (double frq);
- static int get_float_from_within_string(char **str,double *val);
- static int midi_data_bytes_in_midi_track (int infiledatalen,double *seqdata,double staccato);
- static int CalcVarLen (unsigned int value);
- static void Write_ThisIsAMIDI_PC_header(unsigned int total_bytes_in_file,FILE *fp);
- #ifndef round
- static int round(double d);
- #endif
- int main(int argc,char *argv[])
- {
- FILE *fp;
- double *seqdata, *envdata, *p, *end;
- char *q;
- double lasttime, thistime, nexttime, timestep, thislevel, stacdur;
- int on, padbyte;
- int n,m,k;
- unsigned int midi_track_data_bytes, total_bytes_in_file;
- char temp[200];
- int system;
- char seqfile[64];
- char envfile[64];
- int infiledatalen;
- double staccato;
- char outfilename[64];
- char thismidi;
- if(argc == 1) {
- fprintf(stdout,"Converts frqequency-brkpnt and peakdata textfiles to a standard midi data file.\n");
- fprintf(stdout,"\n");
- fprintf(stdout,"USAGE:\n");
- fprintf(stdout,"convert_to_midi frqbrkpnt peakvals datalen staccato outfilename system\n");
- fprintf(stdout,"\n");
- fprintf(stdout,"FRQBRKPNT File of time-frq pairs for note starts and ends.\n");
- fprintf(stdout,"PEAKVALS Levels of each note (Range 0-1).\n");
- fprintf(stdout,"DATALEN Number of peaks (no. of vals in 'frqbrkpnt' should be datalen * 4)\n");
- fprintf(stdout,"STACCATO Force staccato output.\n");
- fprintf(stdout,"OUTFILENAME (File extension will be forced to '.mid' or '.rmi')\n");
- fprintf(stdout,"SYSTEM 0 gives Standard Midi File, with extension '.mid'\n");
- fprintf(stdout," 1 gives PC Midi File, with extension '.rmi'\n");
- fflush(stdout);
- return 1;
- }
- if(argc != 7) {
- fprintf(stdout,"ERROR: WRONG NUMBER OF ARGUMENTS\n");
- fflush(stdout);
- return 0;
- }
- strcpy(seqfile,argv[1]);
- strcpy(envfile,argv[2]);
- if(sscanf(argv[3],"%d",&infiledatalen)<1) {
- fprintf(stdout,"ERROR: CANNOT READ INFILE DATA LENGTH [%s].\n",argv[3]);
- fflush(stdout);
- return 0;
- }
- if(sscanf(argv[4],"%lf",&staccato)<1) {
- fprintf(stdout,"ERROR: CANNOT READ STACCATO DURATION [%s].\n",argv[4]);
- fflush(stdout);
- return 0;
- }
- strcpy(outfilename,argv[5]);
- q = outfilename + strlen(outfilename);
- q--;
- while(*q != '.') {
- q--;
- if(q <= outfilename)
- break;
- }
- if(q > outfilename)
- *q = ENDOFSTR;
- if(sscanf(argv[6],"%d",&system)<1) {
- fprintf(stdout,"ERROR: CANNOT READ HEADER TYPE [%s].\n",argv[4]);
- fflush(stdout);
- return 0;
- }
- if(system == 1)
- strcat(outfilename,".rmi");
- else
- strcat(outfilename,".mid");
- if((seqdata = (double *)malloc((infiledatalen * 4) * sizeof(double)))==NULL) {
- fprintf(stdout,"ERROR: INSUFFICIENT MEMORY TO CREATE INPUT DATA ARRAY.\n");
- fflush(stdout);
- return 0;
- }
- if((envdata = (double *)malloc(infiledatalen * sizeof(double)))==NULL) {
- fprintf(stdout,"ERROR: INSUFFICIENT MEMORY TO CREATE LOUDNESS DATA ARRAY.\n");
- fflush(stdout);
- return 0;
- }
- p = seqdata;
- if((fp = fopen(seqfile,"r"))==NULL) {
- fprintf(stdout,"ERROR: FAILED TO OPEN FILE %s TO READ SEQUENCE DATA\n",seqfile);
- fflush(stdout);
- return 0;
- }
- end = p + (infiledatalen * 4);
- while(fgets(temp,200,fp)!=NULL) { /* READ AND TEST MIDI DATA INFO, ASSUMING IT TO BE IN CORRECT FORMAT!! */
- q = temp;
- while(get_float_from_within_string(&q,p)) {
- p++;
- if (p >= end)
- break;
- }
- }
- fclose(fp);
- if(p - seqdata != infiledatalen * 4) {
- fprintf(stdout,"ERROR: COUNT OF MIDI DATA ITEMS DOES NOT CORRESPOND TO PARAM FOR INPUT DATA LENGTH\n");
- fflush(stdout);
- return 0;
- }
- p = envdata;
- end = p + infiledatalen;
- if((fp = fopen(envfile,"r"))==NULL) {
- fprintf(stdout,"ERROR: FAILED TO OPEN FILE %s TO READ LOUDNESS DATA\n",envfile);
- fflush(stdout);
- return 0;
- }
- while(fgets(temp,200,fp)!=NULL) { /* READ AND TEST MIDI DATA INFO, ASSUMING IT TO BE IN CORRECT FORMAT!! */
- q = temp;
- while(get_float_from_within_string(&q,p)) {
- p++;
- if (p >= end)
- break;
- }
- }
- fclose(fp);
- if(p - envdata != infiledatalen) {
- fprintf(stdout,"ERROR: COUNT OF LOUDNESS ITEMS DOES NOT CORRESPOND TO PARAM FOR INPUT DATA LENGTH\n");
- fflush(stdout);
- return 0;
- }
- if((fp = fopen(outfilename,"w"))==NULL) {
- fprintf(stdout,"ERROR: FAILED TO OPEN MIDI DATAFILE %s TO WRITE DATA\n",outfilename);
- fflush(stdout);
- return 0;
- }
- total_bytes_in_file = 14 /* MThd Chunk */ + 4 /*MTrk track identifier */ ;
- midi_track_data_bytes = midi_data_bytes_in_midi_track(infiledatalen,seqdata,staccato);
- total_bytes_in_file += CalcVarLen(midi_track_data_bytes) + midi_track_data_bytes;
-
- if((total_bytes_in_file/2)*2 != total_bytes_in_file) { /* Does (single) chunk have EVEN number of bytes? */
- padbyte = 1;
- } else {
- padbyte = 0;
- }
- if (system == 1)
- total_bytes_in_file += padbyte; /* For PC, force an EVEN chunk-size */
- switch(system) {
- case(0):
- break;
- case(1): /* PC */
- Write_ThisIsAMIDI_PC_header(total_bytes_in_file,fp);
- break;
- }
- Write_ThisIsAMIDI_MThdChunk_with_mS_timings ((char)0,(char)1,fp);
- /* Assuming format type 0, 1 track only */
- WriteTrackID (midi_track_data_bytes,fp);
- /* Assuming there is 1 track, numbered 0 */
- lasttime = 0.0;
- on = 1;
- k = 0;
- for(n=0,m=1;n < infiledatalen * 4;n+=2,m+=2) {
- thistime = seqdata[n];
- thismidi = (char)round(HzToMidi(seqdata[m]));
- timestep = thistime - lasttime;
- if (on) {
- thislevel = envdata[k++];
- create_note(timestep,thismidi,thislevel,1,fp);
- lasttime = thistime;
- if (staccato > 0.0) { /* If staccato, note off determined by staccato dur */
- if(m+4 < infiledatalen) {
- nexttime = seqdata[m+4];
- stacdur = (nexttime - thistime)/2.0;
- stacdur = min(stacdur,staccato);
- } else {
- stacdur = staccato;
- }
- create_note(stacdur,thismidi,thislevel,0,fp);
- lasttime = thistime + stacdur;
- }
- on = 0;
- } else {
- if (staccato <= 0.0) { /* IF not staccato, note-off taken from input data */
- create_note(timestep,thismidi,thislevel,0,fp);
- lasttime = thistime; /* But, if staccato, ignore the note off input */
- }
- on = 1;
- }
- }
- Write_TrackEnd(fp);
- if(system == 1) { /* PC riff format */
- if(padbyte)
- putc(0,fp);
- }
- fclose(fp);
- return 1;
- }
- /*********************** WRITE A 32-BIT REPRESENTATION OF "THIS is MIDI data = ascii MThd" ***********/
- void Write_ThisIsAMIDI_MThdChunk_with_mS_timings (char format_type,char track_cnt,FILE *fp)
- {
- register char buffer;
- buffer = 77; /* M */
- putc(buffer,fp);
- buffer = 84; /* T */
- putc(buffer,fp);
- buffer = 104; /* h */
- putc(buffer,fp);
- buffer = 100; /* d */
- putc(buffer,fp);
- buffer = 0; /* sizeof MThd chunk is 6 = 0006 */
- putc(buffer,fp);
- putc(buffer,fp);
- putc(buffer,fp);
- buffer = 6; /* sizeof MThd chunk is 6 = 0006 */
- putc(buffer,fp);
- buffer = 0; /* format_type 00 0x */
- putc(buffer,fp);
- buffer = format_type;
- putc(buffer,fp);
- buffer = 0; /* track_cnt 00 0x */
- putc(buffer,fp);
- buffer = track_cnt;
- putc(buffer,fp);
- buffer = (char)-25; /* -25 means 25 frames per sec */
- putc(buffer,fp);
- buffer = 40; /* 40 units per frame -> 1 unit per ms */
- putc(buffer,fp);
- }
- /*********************** WRITE TRACK ID ***********/
- void WriteTrackID (unsigned int midi_track_data_bytes,FILE *fp)
- {
- register char buffer;
- buffer = 77; /* M */
- putc(buffer,fp);
- buffer = 84; /* T */
- putc(buffer,fp);
- buffer = 114; /* r */
- putc(buffer,fp);
- buffer = 107; /* k */
- putc(buffer,fp);
- WriteVarLen(midi_track_data_bytes,fp);
- }
- /*********************** WRITE A 32-BIT int, SPLIT INTO 7-bit WORDS IN REVERSE BYTE ORDER = MIDI TIME REPRESENTATION ***********/
- void WriteVarLen (unsigned int value,FILE *fp)
- {
- register unsigned int buffer;
- buffer = value & 0x7F;
- while (value >>= 7) {
- buffer <<= 8;
- buffer |= ((value & 0x7F) | 0x80);
- }
- for (;;) {
- putc(buffer,fp);
- if(buffer & 0x80)
- buffer >>= 8;
- else
- break;
- }
- }
- /*********************** WRITE A 32-BIT int IN NORMAL ORDER ***********/
- void WriteNormalLong (unsigned int value,FILE *fp)
- {
- unsigned int buffer;
- int n = 0;
- while (n < 4) {
- buffer = value & 0xFF;
- putc(buffer,fp);
- value >>= 8;
- n++;
- }
- }
- /*********************** ASCERTAIN BYTE LENGTH OF MIDI TIME REPRESENTATION ***********/
- int CalcVarLen (unsigned int value)
- {
- register unsigned int buffer;
- int cnt = 0;
- buffer = value & 0x7F;
- while (value >>= 7) {
- buffer <<= 8;
- buffer |= ((value & 0x7F) | 0x80);
- }
- for (;;) {
- cnt++;
- if(buffer & 0x80)
- buffer >>= 8;
- else
- break;
- }
- return cnt;
- }
- /*********************** WRITE "END OF TRACK" ***********/
- void Write_TrackEnd (FILE *fp)
- {
- register char buffer;
- buffer = (char)-1; /* FF = |1111|1111| = -128 */
- putc(buffer,fp);
- buffer = (char)0x2F;
- putc(buffer,fp);
- buffer = 0;
- putc(buffer,fp);
- }
- /*********************** CREATE MIDI NOTE ON OR NOTE OFF DATA ***********/
- void create_note(double timestep,char midi,double thislevel,int on,FILE *fp)
- {
- register unsigned char buffer;
- unsigned int timedata;
- char velocity;
- timedata = (unsigned int)round(timestep * 1000.0); /* TIME IN mS */
- velocity = (char)round(thislevel * 127.0);
- WriteVarLen(timedata,fp);
- if (on)
- buffer = MIDI_NOTE_ON;
- else
- buffer = MIDI_NOTE_OFF;
- putc(buffer,fp);
- buffer = midi;
- putc(buffer,fp);
- buffer = velocity;
- putc(buffer,fp);
- }
- /*********************** CONVERT FRQ TO MIDI ***********/
- char HzToMidi (double frq) {
- double dmidi;
- char midi;
- if (frq < LOW_A) {
- frq = LOW_A;
- }
- dmidi = frq / LOW_A;
- dmidi = (log10(dmidi) * CONVERT_LOG10_TO_LOG2 * 12.0) - 3.0;
- midi = (char)round(dmidi);
- if (midi > 127) {
- midi = 127;
- } else if(midi < 0) {
- midi = 0;
- }
- return midi;
- }
- /************************** GET_FLOAT_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_from_within_string(char **str,double *val)
- {
- char *p, *valstart;
- int decimal_point_cnt = 0, has_digits = 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 == '.') {
- if(++decimal_point_cnt>1)
- return(FALSE);
- } else
- return(FALSE);
- p++;
- }
- if(!has_digits || sscanf(valstart,"%lf",val)!=1)
- return(FALSE);
- *str = p;
- return(TRUE);
- }
- /*********************** CALCULATE NUMBER OF DATA BYTES TO FOLLOW ***********/
- int midi_data_bytes_in_midi_track(int infiledatalen,double *seqdata,double staccato)
- {
- double thistime, nexttime, timestep, stacdur;
- double lasttime = 0.0;
- unsigned int timedata;
- unsigned int cnt = 0;
- int on = 1;
- int n,m;
- for(n=0,m=1;n < infiledatalen * 4;n+=2,m+=2) {
- thistime = seqdata[n];
- timestep = thistime - lasttime;
- lasttime = thistime;
- if (on) {
- timedata = (unsigned int)round(timestep * 1000.0); /* TIME IN mS */
- cnt += CalcVarLen(timedata);
- cnt += 3; /* MIDI status + key + velocity */
- if (staccato > 0.0) {
- if(m+4 < infiledatalen) {
- nexttime = seqdata[m+4];
- stacdur = (nexttime - thistime)/2.0;
- stacdur = min(stacdur,staccato);
- } else {
- stacdur = staccato;
- }
- timedata = (unsigned int)round(stacdur * 1000.0);
- cnt += CalcVarLen(timedata);
- cnt += 3;
- lasttime = thistime + stacdur;
- }
- on = 0;
- } else {
- if (staccato <= 0.0) {
- timedata = (unsigned int)round(timestep * 1000.0);
- cnt += CalcVarLen(timedata);
- cnt += 3;
- lasttime = thistime;
- }
- on = 1;
- }
- }
- cnt += 3; /* Add Track end bytes */
- return cnt;
- }
- void Write_ThisIsAMIDI_PC_header(unsigned int total_bytes_in_file,FILE *fp)
- {
- register char buffer;
- buffer = 82; /* R */
- putc(buffer,fp);
- buffer = 73; /* I */
- putc(buffer,fp);
- buffer = 70; /* F */
- putc(buffer,fp);
- buffer = 70; /* F */
- putc(buffer,fp);
- WriteNormalLong(total_bytes_in_file,fp);
- buffer = 82; /* R */
- putc(buffer,fp);
- buffer = 77; /* M */
- putc(buffer,fp);
- buffer = 73; /* I */
- putc(buffer,fp);
- buffer = 68; /* D */
- putc(buffer,fp);
- }
- //#ifndef round
- #if 0
- static int twround(double d)
- {
- d += 0.5;
- return (int)floor(d);
- }
- #endif
- //endif
|