/* ZynAddSubFX - a software synthesizer Microtonal.C - Tuning settings and microtonal capabilities Copyright (C) 2002-2005 Nasca Octavian Paul Author: Nasca Octavian Paul This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program 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 General Public License (version 2) for more details. You should have received a copy of the GNU General Public License (version 2) along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "Microtonal.h" #define MAX_LINE_SIZE 80 Microtonal::Microtonal(){ Pname=new unsigned char[MICROTONAL_MAX_NAME_LEN]; Pcomment=new unsigned char[MICROTONAL_MAX_NAME_LEN]; defaults(); }; void Microtonal::defaults(){ Pinvertupdown=0; Pinvertupdowncenter=60; octavesize=12; Penabled=0; PAnote=69; PAfreq=440.0; Pscaleshift=64; Pfirstkey=0;Plastkey=127; Pmiddlenote=60;Pmapsize=12; Pmappingenabled=0; for (int i=0;i<128;i++) Pmapping[i]=i; for (int i=0;iPlastkey)) return (-1.0); //Compute how many mapped keys are from middle note to reference note //and find out the proportion between the freq. of middle note and "A" note int tmp=PAnote-Pmiddlenote,minus=0; if (tmp<0) { tmp=-tmp; minus=1; }; int deltanote=0; for (int i=0;i=0) deltanote++; REALTYPE rap_anote_middlenote=(deltanote==0) ? (1.0) : (octave[(deltanote-1)%octavesize].tuning); if (deltanote!=0) rap_anote_middlenote*=pow(octave[octavesize-1].tuning,(deltanote-1)/octavesize); if (minus!=0) rap_anote_middlenote=1.0/rap_anote_middlenote; //Convert from note (midi) to degree (note from the tunning) int degoct=(note-(int)Pmiddlenote+(int) Pmapsize*200)/(int)Pmapsize-200; int degkey=(note-Pmiddlenote+(int)Pmapsize*100)%Pmapsize; degkey=Pmapping[degkey]; if (degkey<0) return(-1.0);//this key is not mapped //invert the keyboard upside-down if it is asked for //TODO: do the right way by using Pinvertupdowncenter if (Pinvertupdown!=0){ degkey=octavesize-degkey-1; degoct=-degoct; }; //compute the frequency of the note degkey=degkey+scaleshift; degoct+=degkey/octavesize; degkey%=octavesize; REALTYPE freq=(degkey==0) ? (1.0):octave[degkey-1].tuning; freq*=pow(octave[octavesize-1].tuning,degoct); freq*=PAfreq/rap_anote_middlenote; freq*=globalfinedetunerap; if (scaleshift!=0) freq/=octave[scaleshift-1].tuning; return(freq*rap_keyshift); } else {//if the mapping is disabled int nt=note-PAnote+scaleshift; int ntkey=(nt+(int)octavesize*100)%octavesize; int ntoct=(nt-ntkey)/octavesize; REALTYPE oct=octave[octavesize-1].tuning; REALTYPE freq=octave[(ntkey+octavesize-1)%octavesize].tuning*pow(oct,ntoct)*PAfreq; if (ntkey==0) freq/=oct; if (scaleshift!=0) freq/=octave[scaleshift-1].tuning; // fprintf(stderr,"note=%d freq=%.3f cents=%d\n",note,freq,(int)floor(log(freq/PAfreq)/log(2.0)*1200.0+0.5)); freq*=globalfinedetunerap; return(freq*rap_keyshift); }; }; /* * Convert a line to tunings; returns -1 if it ok */ int Microtonal::linetotunings(unsigned int nline,const char *line){ int x1=-1,x2=-1,type=-1; REALTYPE x=-1.0,tmp,tuning=1.0; if (strstr(line,"/")==NULL){ if (strstr(line,".")==NULL){// M case (M=M/1) sscanf(line,"%d",&x1); x2=1; type=2;//division } else {// float number case sscanf(line,"%f",&x); if (x<0.000001) return(1); type=1;//float type(cents) }; } else {// M/N case sscanf(line,"%d/%d",&x1,&x2); if ((x1<0)||(x2<0)) return(1); if (x2==0) x2=1; type=2;//division }; if (x1<=0) x1=1;//not allow zero frequency sounds (consider 0 as 1) //convert to float if the number are too big if ((type==2)&&((x1>(128*128*128-1))||(x2>(128*128*128-1)))){ type=1; x=((REALTYPE) x1)/x2; }; switch (type){ case 1: x1=(int) floor(x); tmp=fmod(x,1.0); x2=(int) (floor (tmp*1e6)); tuning=pow(2.0,x/1200.0); break; case 2: x=((REALTYPE)x1)/x2; tuning=x; break; }; tmpoctave[nline].tuning=tuning; tmpoctave[nline].type=type; tmpoctave[nline].x1=x1; tmpoctave[nline].x2=x2; return(-1);//ok }; /* * Convert the text to tunnings */ int Microtonal::texttotunings(const char *text){ unsigned int i,k=0,nl=0; char *lin; lin=new char[MAX_LINE_SIZE+1]; while (kMAX_OCTAVE_SIZE) nl=MAX_OCTAVE_SIZE; if (nl==0) return(-2);//the input is empty octavesize=nl; for (i=0;i127) break; }; delete [] lin; if (tx==0) tx=1; Pmapsize=tx; }; /* * Convert tunning to text line */ void Microtonal::tuningtoline(int n,char *line,int maxn){ if ((n>octavesize) || (n>MAX_OCTAVE_SIZE)) { line[0]='\0'; return; }; if (octave[n].type==1) snprintf(line,maxn,"%d.%d",octave[n].x1,octave[n].x2); if (octave[n].type==2) snprintf(line,maxn,"%d/%d",octave[n].x1,octave[n].x2); }; int Microtonal::loadline(FILE *file,char *line){ do { if (fgets(line,500,file)==0) return(1); } while (line[0]=='!'); return(0); }; /* * Loads the tunnings from a scl file */ int Microtonal::loadscl(const char *filename){ FILE *file=fopen(filename, "r"); char tmp[500]; fseek(file,0,SEEK_SET); //loads the short description if (loadline(file,&tmp[0])!=0) return(2); for (int i=0;i<500;i++) if (tmp[i]<32) tmp[i]=0; snprintf((char *) Pname,MICROTONAL_MAX_NAME_LEN,"%s",tmp); snprintf((char *) Pcomment,MICROTONAL_MAX_NAME_LEN,"%s",tmp); //loads the number of the notes if (loadline(file,&tmp[0])!=0) return(2); int nnotes=MAX_OCTAVE_SIZE; sscanf(&tmp[0],"%d",&nnotes); if (nnotes>MAX_OCTAVE_SIZE) return (2); //load the tunnings for (int nline=0;nline127) x=127;//just in case... Pmapsize=x; //loads first MIDI note to retune if (loadline(file,&tmp[0])!=0) return(2); if (sscanf(&tmp[0],"%d",&x)==0) return(2); if (x<1) x=0;if (x>127) x=127;//just in case... Pfirstkey=x; //loads last MIDI note to retune if (loadline(file,&tmp[0])!=0) return(2); if (sscanf(&tmp[0],"%d",&x)==0) return(2); if (x<1) x=0;if (x>127) x=127;//just in case... Plastkey=x; //loads last the middle note where scale fro scale degree=0 if (loadline(file,&tmp[0])!=0) return(2); if (sscanf(&tmp[0],"%d",&x)==0) return(2); if (x<1) x=0;if (x>127) x=127;//just in case... Pmiddlenote=x; //loads the reference note if (loadline(file,&tmp[0])!=0) return(2); if (sscanf(&tmp[0],"%d",&x)==0) return(2); if (x<1) x=0;if (x>127) x=127;//just in case... PAnote=x; //loads the reference freq. if (loadline(file,&tmp[0])!=0) return(2); REALTYPE tmpPAfreq=440.0; if (sscanf(&tmp[0],"%f",&tmpPAfreq)==0) return(2); PAfreq=tmpPAfreq; //the scale degree(which is the octave) is not loaded, it is obtained by the tunnings with getoctavesize() method if (loadline(file,&tmp[0])!=0) return(2); //load the mappings if (Pmapsize!=0){ for (int nline=0;nlineaddparstr("name",(char *) Pname); xml->addparstr("comment",(char *) Pcomment); xml->addparbool("invert_up_down",Pinvertupdown); xml->addparbool("invert_up_down_center",Pinvertupdowncenter); xml->addparbool("enabled",Penabled); xml->addpar("global_fine_detune",Pglobalfinedetune); xml->addpar("a_note",PAnote); xml->addparreal("a_freq",PAfreq); if ((Penabled==0)&&(xml->minimal)) return; xml->beginbranch("SCALE"); xml->addpar("scale_shift",Pscaleshift); xml->addpar("first_key",Pfirstkey); xml->addpar("last_key",Plastkey); xml->addpar("middle_note",Pmiddlenote); xml->beginbranch("OCTAVE"); xml->addpar("octave_size",octavesize); for (int i=0;ibeginbranch("DEGREE",i); if (octave[i].type==1){ xml->addparreal("cents",octave[i].tuning); }; if (octave[i].type==2){ xml->addpar("numerator",octave[i].x1); xml->addpar("denominator",octave[i].x2); }; xml->endbranch(); }; xml->endbranch(); xml->beginbranch("KEYBOARD_MAPPING"); xml->addpar("map_size",Pmapsize); xml->addpar("mapping_enabled",Pmappingenabled); for (int i=0;ibeginbranch("KEYMAP",i); xml->addpar("degree",Pmapping[i]); xml->endbranch(); }; xml->endbranch(); xml->endbranch(); }; void Microtonal::getfromXML(XMLwrapper *xml){ xml->getparstr("name",(char *) Pname,MICROTONAL_MAX_NAME_LEN); xml->getparstr("comment",(char *) Pcomment,MICROTONAL_MAX_NAME_LEN); Pinvertupdown=xml->getparbool("invert_up_down",Pinvertupdown); Pinvertupdowncenter=xml->getparbool("invert_up_down_center",Pinvertupdowncenter); Penabled=xml->getparbool("enabled",Penabled); Pglobalfinedetune=xml->getpar127("global_fine_detune",Pglobalfinedetune); PAnote=xml->getpar127("a_note",PAnote); PAfreq=xml->getparreal("a_freq",PAfreq,1.0,10000.0); if (xml->enterbranch("SCALE")){ Pscaleshift=xml->getpar127("scale_shift",Pscaleshift); Pfirstkey=xml->getpar127("first_key",Pfirstkey); Plastkey=xml->getpar127("last_key",Plastkey); Pmiddlenote=xml->getpar127("middle_note",Pmiddlenote); if (xml->enterbranch("OCTAVE")){ octavesize=xml->getpar127("octave_size",octavesize); for (int i=0;ienterbranch("DEGREE",i)==0) continue; octave[i].x2=0; octave[i].tuning=xml->getparreal("cents",octave[i].tuning); octave[i].x1=xml->getpar127("numerator",octave[i].x1); octave[i].x2=xml->getpar127("denominator",octave[i].x2); if (octave[i].x2!=0) octave[i].type=2; else octave[i].type=1; xml->exitbranch(); }; xml->exitbranch(); }; if (xml->enterbranch("KEYBOARD_MAPPING")){ Pmapsize=xml->getpar127("map_size",Pmapsize); Pmappingenabled=xml->getpar127("mapping_enabled",Pmappingenabled); for (int i=0;ienterbranch("KEYMAP",i)==0) continue; Pmapping[i]=xml->getpar127("degree",Pmapping[i]); xml->exitbranch(); }; xml->exitbranch(); }; xml->exitbranch(); }; }; int Microtonal::saveXML(char *filename){ XMLwrapper *xml=new XMLwrapper(); xml->beginbranch("MICROTONAL"); add2XML(xml); xml->endbranch(); int result=xml->saveXMLfile(filename); delete (xml); return(result); }; int Microtonal::loadXML(char *filename){ XMLwrapper *xml=new XMLwrapper(); if (xml->loadXMLfile(filename)<0) { delete(xml); return(-1); }; if (xml->enterbranch("MICROTONAL")==0) return(-10); getfromXML(xml); xml->exitbranch(); delete(xml); return(0); };