/* * 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 * */ #include #include #include #include #include #include #include #include #include #include #include #include //#if defined unix || defined __GNUC__ #define round(x) lround((x)) //#endif #define NOTEMIN ("A-4") #define NOTEMAX ("G5") #define FRQMAX (12543) /* Approx frequency at MIDI 127 */ #define CONVERT_LOG10_TO_LOG2 (3.321928) #define my_LOG2(x) (log10(x) * CONVERT_LOG10_TO_LOG2) #define my_ANTILOG2(x) (pow(2.0,x)) #define LOG_SEMITONE_INTERVAL (0.0250858329719984) #define QTONE_PER_OCT (24) //static double readnote(char *note,dataptr dz); static int midi_to_frq(double midi); static int frq_to_midi(double frq); static void note_to_frq(double midi); //static void note_to_midi(int argc,char *argv[]); static int ratio_to_semitones(double ratio); static int ratio_to_interval(double ratio); static int r_to_i(double ratio); static void semitones_to_ratio(double semitone_cnt); //static int interval_to_semitones(char *str,double *semitiones); static void semitones_to_oct(double semitone_cnt); static void oct_to_semitones(double oct); static void oct_to_ratio(double oct); static int ratio_to_oct(double ratio); static int do_fraction(double val); static int ratio_to_time(double ratio); static int time_to_ratio(double timestr); static void semitones_to_time(double timestr); static void oct_to_time(double oct); static int time_to_semitones(double timestr); static int time_to_intervals(double timestr); static int time_to_oct(double timestr); static int semitones_to_interval(double sc); static void gain_to_db(double gain); static void db_to_gain(double dbgain); static int midi_to_note(double val,dataptr dz); static void delay_to_frq(double val); static int delay_to_midi(double val); static void frq_to_delay(double val); static int midi_to_delay(double val); static int tempo_to_delay(double val); static int delay_to_tempo(double val); /************************ DO_MUSUNITS *************************/ int do_musunits(dataptr dz) { int exit_status = FINISHED, k; switch(dz->mode) { case(MU_MIDI_TO_FRQ): exit_status = midi_to_frq(dz->param[MUSUNIT]); break; case(MU_FRQ_TO_MIDI): exit_status = frq_to_midi(dz->param[MUSUNIT]); break; case(MU_NOTE_TO_FRQ): note_to_frq(dz->scalefact); break; case(MU_NOTE_TO_MIDI): k = round(dz->scalefact); if(!flteq((double)k,dz->scalefact)) sprintf(errstr,"MIDI value = %lf or approx %d\n",dz->scalefact,k); else sprintf(errstr,"MIDI value = %d\n",k); break; case(MU_FRQ_TO_NOTE): case(MU_MIDI_TO_NOTE): midi_to_note(dz->param[MUSUNIT],dz); break; case(MU_FRQRATIO_TO_SEMIT): exit_status = ratio_to_semitones(dz->param[MUSUNIT]); break; case(MU_FRQRATIO_TO_INTVL): exit_status = ratio_to_interval(dz->param[MUSUNIT]); break; case(MU_INTVL_TO_FRQRATIO): semitones_to_ratio(dz->scalefact); break; case(MU_SEMIT_TO_FRQRATIO): semitones_to_ratio(dz->param[MUSUNIT]); break; case(MU_OCTS_TO_FRQRATIO): oct_to_ratio(dz->param[MUSUNIT]); break; case(MU_OCTS_TO_SEMIT): oct_to_semitones(dz->param[MUSUNIT]); break; case(MU_FRQRATIO_TO_OCTS): exit_status = ratio_to_oct(dz->param[MUSUNIT]); break; case(MU_SEMIT_TO_OCTS): semitones_to_oct(dz->param[MUSUNIT]); break; case(MU_SEMIT_TO_INTVL): exit_status = semitones_to_interval(dz->param[MUSUNIT]); break; case(MU_FRQRATIO_TO_TSTRETH): exit_status = ratio_to_time(dz->param[MUSUNIT]); break; case(MU_SEMIT_TO_TSTRETCH): semitones_to_time(dz->param[MUSUNIT]); break; case(MU_OCTS_TO_TSTRETCH): oct_to_time(dz->param[MUSUNIT]); break; case(MU_INTVL_TO_TSTRETCH): semitones_to_time(dz->scalefact); break; case(MU_TSTRETCH_TO_FRQRATIO): exit_status = time_to_ratio(dz->param[MUSUNIT]); break; case(MU_TSTRETCH_TO_SEMIT): exit_status = time_to_semitones(dz->param[MUSUNIT]); break; case(MU_TSTRETCH_TO_OCTS): exit_status = time_to_oct(dz->param[MUSUNIT]); break; case(MU_TSTRETCH_TO_INTVL): exit_status = time_to_intervals(dz->param[MUSUNIT]); break; case(MU_GAIN_TO_DB): gain_to_db(dz->param[MUSUNIT]); break; case(MU_DB_TO_GAIN): db_to_gain(dz->param[MUSUNIT]); break; case(MU_DELAY_TO_FRQ): delay_to_frq(dz->param[MUSUNIT]); break; case(MU_DELAY_TO_MIDI): exit_status = delay_to_midi(dz->param[MUSUNIT]); break; case(MU_FRQ_TO_DELAY): frq_to_delay(dz->param[MUSUNIT]); break; case(MU_MIDI_TO_DELAY): exit_status = midi_to_delay(dz->param[MUSUNIT]); break; case(MU_NOTE_TO_DELAY): exit_status = midi_to_delay(dz->scalefact); break; case(MU_TEMPO_TO_DELAY): exit_status = tempo_to_delay(dz->param[MUSUNIT]); break; case(MU_DELAY_TO_TEMPO): exit_status = delay_to_tempo(dz->param[MUSUNIT]); break; default: sprintf(errstr,"Unknown mode in do_musunits()\n"); return(PROGRAM_ERROR); } // sprintf(errstr,"%s",errstr); //RWD 2025 think this is probably unintended print_outmessage_flush(errstr); return(exit_status); } /**************************** MIDI_TO_FRQ **************************/ int midi_to_frq(double midi) { double frq; if(midi<(double)MIDIMIN || midi >(double)MIDIMAX) { sprintf(errstr,"MIDI value out of range %d - %d\n",MIDIMIN,MIDIMAX); return(GOAL_FAILED); } frq = midi; frq += 3.0; frq /= SEMITONES_PER_OCTAVE; frq = pow((double)2,frq); frq *= LOW_A; sprintf(errstr,"frequency = %lf\n",frq); return(FINISHED); } /************************ FRQ_TO_MIDI *****************/ int frq_to_midi(double frq) { double midi; if(frq(double)FRQMAX) { sprintf(errstr,"Frq value out of range %ld - %d\n",round(MINPITCH),FRQMAX); return(GOAL_FAILED); } midi = frq; midi /= LOW_A; midi = my_LOG2(midi); midi *= SEMITONES_PER_OCTAVE; midi -= 3.0; sprintf(errstr,"MIDI value = %lf or approx %ld\n",midi,round(midi)); return(FINISHED); } /************************** NOTE_TO_FRQ *************************/ void note_to_frq(double midi) { double frq; frq = midi; frq += 3.0; frq /= SEMITONES_PER_OCTAVE; frq = pow((double)2,frq); frq *= LOW_A; sprintf(errstr,"Frequency = %lf\n",frq); } /************************* RATIO_TO_SEMITONES **********************/ int ratio_to_semitones(double ratio) { double semitone_cnt; if(ratio <= 0.0) { sprintf(errstr,"Freq ratio <= 0.0 : impossible.\n"); return(GOAL_FAILED); } semitone_cnt = fabs(log10(ratio)/(double)LOG_SEMITONE_INTERVAL); //TW UPDATE if(ratio < 1.0) semitone_cnt = -semitone_cnt; sprintf(errstr,"%lf semitones\n",semitone_cnt); return(FINISHED); } /************************* RATIO_TO_SEMITONES **********************/ int ratio_to_interval(double ratio) { if(ratio <= 0.0) { sprintf(errstr,"Freq ratio <= 0.0 : impossible.\n"); return(GOAL_FAILED); } return r_to_i(ratio); } /************************* R_TO_I **********************/ int r_to_i(double ratio) { double semitone_cnt; int oct_cnt, qtone_cnt; semitone_cnt = fabs(log10(ratio)/(double)LOG_SEMITONE_INTERVAL); qtone_cnt = round(semitone_cnt * 2); oct_cnt = qtone_cnt/QTONE_PER_OCT; qtone_cnt -= oct_cnt * QTONE_PER_OCT; switch(oct_cnt) { case(1): sprintf(errstr,"1 octave"); break; case(0): break; default: sprintf(errstr,"%d octaves",oct_cnt); break; } if(qtone_cnt && oct_cnt) strcat(errstr," and a "); switch(qtone_cnt) { case(0): if(oct_cnt==0) { sprintf(errstr,"Unison.\n"); return(FINISHED); } break; case(1): strcat(errstr,"minor 2nd-\n"); break; case(2): strcat(errstr,"minor 2nd\n"); break; case(3): strcat(errstr,"minor 2nd+\n"); break; case(4): strcat(errstr,"major 2nd\n"); break; case(5): strcat(errstr,"major 2nd+\n"); break; case(6): strcat(errstr,"minor 3rd\n"); break; case(7): strcat(errstr,"minor 3rd+\n"); break; case(8): strcat(errstr,"major 3rd\n"); break; case(9): strcat(errstr,"major 3rd+\n"); break; case(10): strcat(errstr,"4th\n"); break; case(11): strcat(errstr,"4th+\n"); break; case(12): strcat(errstr,"augmented 4th\n"); break; case(13): strcat(errstr,"augmented 4th+\n"); break; case(14): strcat(errstr,"5th\n"); break; case(15): strcat(errstr,"5th+\n"); break; case(16): strcat(errstr,"minor 6th\n"); break; case(17): strcat(errstr,"minor 6th+\n"); break; case(18): strcat(errstr,"major 6th\n"); break; case(19): strcat(errstr,"major 6th+\n"); break; case(20): strcat(errstr,"minor 7th\n"); break; case(21): strcat(errstr,"minor 7th+\n"); break; case(22): strcat(errstr,"major 7th\n"); break; case(23): strcat(errstr,"major 7th+\n"); break; } return(FINISHED); } /************************ SEMITONES_TO_RATIO *******************/ void semitones_to_ratio(double semitone_cnt) { double ratio; ratio = pow((double)SEMITONE_INTERVAL,semitone_cnt); sprintf(errstr,"frequency ratio is %.8lf\n",ratio); } /******************************** SEMITONES_TO_OCT ***********************/ void semitones_to_oct(double semitone_cnt) { double oct; oct = (double)semitone_cnt/SEMITONES_PER_OCTAVE; sprintf(errstr,"octave_distance is %.8lf\n",oct); } /******************************** OCT_TO_SEMITONES ***********************/ void oct_to_semitones(double oct) { double semitone_cnt; semitone_cnt = oct * SEMITONES_PER_OCTAVE; sprintf(errstr,"semitone count is %.8lf\n",semitone_cnt); } /******************************** OCT_TO_RATIO ***********************/ void oct_to_ratio(double oct) { double ratio = my_ANTILOG2(oct); sprintf(errstr,"Frequency ratio is %.8lf\n",ratio); } /******************************** RATIO_TO_OCT ***********************/ int ratio_to_oct(double ratio) { int exit_status; double oct; if(ratio <= FLTERR) { sprintf(errstr,"Interval ratio too small or negative (Impossible).\n"); return(GOAL_FAILED); } oct = my_LOG2(ratio); sprintf(errstr,"Octave distance is "); if((exit_status = do_fraction(oct))<0) return(exit_status); strcat(errstr,"\n"); return(FINISHED); } /*********************************** DO_FRACTION ******************************/ int do_fraction(double val) { int n, m, k = 0; int is_neg = 0; double d, test, thisval = val; char temp[64]; if(val <0.0) { thisval = -thisval; is_neg = 1; } for(n=1;n<5;n*=2) { d = 1.0/(double)n; if((test = fmod(thisval,d))< FLTERR || d - test < FLTERR) { m = (int)(thisval/d); /* truncate */ if(!flteq((double)m * d,thisval)) m++; if(!flteq((double)m * d,thisval)) { m-=2; if(!flteq((double)m * d,thisval)) { sprintf(errstr,"Problem in fractions\n"); return(PROGRAM_ERROR); } } k = 0L; if(m >= n) { k = m/n; /* truncate */ m -= k * n; } if(is_neg) strcat(errstr,"-"); if(k) { sprintf(temp,"%d",k); strcat(errstr,temp); } switch(n) { case(2): if(m) strcat(errstr,".5"); break; case(4): switch(m) { case(1): strcat(errstr,".25"); break; case(2): strcat(errstr,".5"); break; case(3): strcat(errstr,".75"); break; } } return(FINISHED); } } sprintf(temp,"%lf",val); strcat(errstr,temp); return(FINISHED); } /******************************** RATIO_TO_TIME ********************************/ int ratio_to_time(double ratio) { if(ratio <= 0.0) { sprintf(errstr,"Freq ratio <= 0.0 : impossible.\n"); return(GOAL_FAILED); } sprintf(errstr,"Timestretch is %lf\n",1.0/ratio); return(FINISHED); } /******************************** TIME_TO_RATIO ********************************/ int time_to_ratio(double timestr) { if(timestr <= 0.0) { sprintf(errstr,"timestretch <= 0.0 : impossible.\n"); return(GOAL_FAILED); } sprintf(errstr,"Timestretch is %lf\n",1.0/timestr); return(FINISHED); } /******************************** SEMITONES_TO_TIME ********************************/ void semitones_to_time(double semitone_cnt) { double ratio; ratio = pow((double)SEMITONE_INTERVAL,semitone_cnt); sprintf(errstr,"Timestretch is %lf\n",1.0/ratio); } /******************************** OCT_TO_TIME ********************************/ void oct_to_time(double oct) { double ratio; ratio = my_ANTILOG2(oct); sprintf(errstr,"Timestretch is %lf\n",1.0/ratio); } /******************************** TIME_TO_SEMITONES ********************************/ int time_to_semitones(double timestr) { double semitone_cnt; double ratio; if(timestr <= 0.0) { sprintf(errstr,"timestretch <= 0.0 : impossible.\n"); return(GOAL_FAILED); } ratio = 1.0/timestr; semitone_cnt = log10(ratio)/(double)LOG_SEMITONE_INTERVAL; sprintf(errstr,"%lf semitones\n",semitone_cnt); return(FINISHED); } /******************************** TIME_TO_INTERVALS ********************************/ int time_to_intervals(double timestr) { int exit_status; double ratio; char *p; if(timestr <= 0.0) { sprintf(errstr,"timestretch <= 0.0 : impossible.\n"); return(GOAL_FAILED); } ratio = 1.0/timestr; if((exit_status = r_to_i(ratio))<0) return(exit_status); if(ratio<1.0) { p = errstr; while(*p != ENDOFSTR) { if(*p == NEWLINE) { *p = ENDOFSTR; break; } p++; } strcat(errstr," Downwards.\n"); } return(FINISHED); } /******************************** TIME_TO_OCT ********************************/ int time_to_oct(double timestr) { int exit_status; double oct; if(timestr <= 0.0) { sprintf(errstr,"timestretch <= 0.0 : impossible.\n"); return(GOAL_FAILED); } //TW UPDATE if(flteq((oct = my_LOG2(1.0/timestr)),0.0)) { sprintf(errstr,"Octave distance is 0\n"); return(FINISHED); } sprintf(errstr,"Octave distance is "); if((exit_status = do_fraction(oct))<0) return(exit_status); strcat(errstr,"\n"); return(FINISHED); } /********************** SEMITONES_TO_INTERVAL **********************/ int semitones_to_interval(double sc) { int ocnt = 0, qcnt, scnt, neg = 0; char temp[200]; errstr[0] = ENDOFSTR; if(sc < 0.0) neg = 1; sc = fabs(sc); ocnt = round(floor(sc/SEMITONES_PER_OCTAVE)); sc = fmod(sc,SEMITONES_PER_OCTAVE); qcnt = round(sc * 2.0); scnt = qcnt/2; if(scnt*2 != qcnt) qcnt = 1; else qcnt = 0; switch(scnt) { case(0): break; case(1): sprintf(errstr,"minor 2nd"); break; case(2): sprintf(errstr,"2ns"); break; case(3): sprintf(errstr,"minor 3rd"); break; case(4): sprintf(errstr,"3rd"); break; case(5): sprintf(errstr,"4th"); break; case(6): sprintf(errstr,"#4th"); break; case(7): sprintf(errstr,"5th"); break; case(8): sprintf(errstr,"minor 6th"); break; case(9): sprintf(errstr,"6th"); break; case(10): sprintf(errstr,"minor 7th"); break; case(11): sprintf(errstr,"7th"); break; default: sprintf(errstr,"Unknown interval.\n"); exit(PROGRAM_ERROR); } if(qcnt) { if(scnt==0) strcat(errstr,"1 quartertone "); else strcat(errstr," + quartertone"); } if(ocnt) { if(scnt > 0 || qcnt > 0) { sprintf(temp," plus %d octave",ocnt); strcat(errstr,temp); } else { sprintf(temp,"%d octave",ocnt); strcat(errstr,temp); } if(ocnt > 1) { sprintf(temp,"s"); strcat(errstr,temp); } } if(strlen(errstr) <= 0) sprintf(errstr,"Less than a quarter-tone"); if(neg) strcat(errstr," down"); strcat(errstr,"\n"); return(FINISHED); } /********************** GAIN_TO_DB **********************/ void gain_to_db(double gain) { double dbgain = log10(gain) * 20.0; if(dbgain <= MIN_DB_ON_16_BIT) sprintf(errstr,"-96dB: i.e. zero level\n"); else sprintf(errstr,"%.3lfdB\n",dbgain); } /********************** DB_TO_GAIN **********************/ void db_to_gain(double dbgain) { double gain = dbtogain(dbgain); sprintf(errstr,"Gain = %lf\n",gain); } /************************ MIDI_TO_NOTE *************************/ int midi_to_note(double val,dataptr dz) { int oct, imidi, midinote, is_qtone = FALSE, is_approx = FALSE; char temp[6]; double midival; if(dz->mode==MU_FRQ_TO_NOTE) { if(val(double)FRQMAX) { sprintf(errstr,"Frq value out of range %ld - %d\n",round(MINPITCH),FRQMAX); return(GOAL_FAILED); } midival = val; midival /= LOW_A; midival = my_LOG2(midival); midival *= SEMITONES_PER_OCTAVE; midival -= 3.0; } else midival = val; midival *= 2.0; imidi = round(midival); if(!flteq((double)imidi,midival)) is_approx = TRUE; if(ODD(imidi)) is_qtone = TRUE; imidi /= 2; midinote = imidi%12; oct = imidi/12; oct -= 5; sprintf(errstr,"Note is "); if(is_approx) strcat(errstr,"(approx) "); switch(midinote) { case(0): strcat(errstr,"C"); break; case(1): strcat(errstr,"C#"); break; case(2): strcat(errstr,"D"); break; case(3): strcat(errstr,"Eb"); break; case(4): strcat(errstr,"E"); break; case(5): strcat(errstr,"F"); break; case(6): strcat(errstr,"F#"); break; case(7): strcat(errstr,"G"); break; case(8): strcat(errstr,"Ab"); break; case(9): strcat(errstr,"A"); break; case(10): strcat(errstr,"Bb"); break; case(11): strcat(errstr,"B"); break; } if(is_qtone) strcat(errstr,"u"); sprintf(temp,"%d",oct); strcat(errstr,temp); strcat(errstr,"\n"); return(FINISHED); } void delay_to_frq(double val) { sprintf(errstr,"Frequency is %.5lf Hz\n",SECS_TO_MS/val); } int delay_to_midi(double val) { int exit_status = FINISHED; val = SECS_TO_MS/val; exit_status = frq_to_midi(val); return(exit_status); } void frq_to_delay(double frq) { sprintf(errstr,"Delay is %.5lf milliseconds\n",SECS_TO_MS/frq); } int midi_to_delay(double midi) { double frq, del; if(midi<(double)MIDIMIN || midi >(double)MIDIMAX) { sprintf(errstr,"MIDI value out of range %d - %d\n",MIDIMIN,MIDIMAX); return(GOAL_FAILED); } frq = midi; frq += 3.0; frq /= SEMITONES_PER_OCTAVE; frq = pow((double)2,frq); frq *= LOW_A; del = SECS_TO_MS/frq; sprintf(errstr,"Delay is %lf milliseconds\n",del); return(FINISHED); } int tempo_to_delay(double tempo) { double del = (60 * SECS_TO_MS)/(double)tempo; sprintf(errstr,"Delay is %lf milliseconds\n",del); return(FINISHED); } int delay_to_tempo(double del) { double tempo = (60 * SECS_TO_MS)/(double)del; sprintf(errstr,"Tempo : crotchet = %lf\n",tempo); return(FINISHED); }