diff options
Diffstat (limited to 'apps/plugins/midi/synth.c')
-rw-r--r-- | apps/plugins/midi/synth.c | 650 |
1 files changed, 319 insertions, 331 deletions
diff --git a/apps/plugins/midi/synth.c b/apps/plugins/midi/synth.c index bafaf1c7ca..50becf32a4 100644 --- a/apps/plugins/midi/synth.c +++ b/apps/plugins/midi/synth.c @@ -20,158 +20,164 @@ extern struct plugin_api * rb; struct Event * getEvent(struct Track * tr, int evNum) { - return tr->dataBlock + (evNum*sizeof(struct Event)); + return tr->dataBlock + (evNum*sizeof(struct Event)); } void readTextBlock(int file, char * buf) { - char c = 0; - do - { - c = readChar(file); - } while(c == '\n' || c == ' ' || c=='\t'); - - rb->lseek(file, -1, SEEK_CUR); - int cp = 0; - do - { - c = readChar(file); - buf[cp] = c; - cp++; - } while (c != '\n' && c != ' ' && c != '\t' && !eof(file)); - buf[cp-1]=0; - rb->lseek(file, -1, SEEK_CUR); + char c = 0; + do + { + c = readChar(file); + } while(c == '\n' || c == ' ' || c=='\t'); + + rb->lseek(file, -1, SEEK_CUR); + int cp = 0; + do + { + c = readChar(file); + buf[cp] = c; + cp++; + } while (c != '\n' && c != ' ' && c != '\t' && !eof(file)); + buf[cp-1]=0; + rb->lseek(file, -1, SEEK_CUR); } -//Filename is the name of the config file -//The MIDI file should have been loaded at this point +/* Filename is the name of the config file */ +/* The MIDI file should have been loaded at this point */ int initSynth(struct MIDIfile * mf, char * filename, char * drumConfig) { - char patchUsed[128]; - char drumUsed[128]; - int a=0; - for(a=0; a<MAX_VOICES; a++) - { - voices[a].cp=0; - voices[a].vol=0; - voices[a].ch=0; - voices[a].isUsed=0; - voices[a].note=0; - } - - for(a=0; a<16; a++) - { - chVol[a]=100; //Default, not quite full blast.. - chPanLeft[a]=64; //Center - chPanRight[a]=64; //Center - chPat[a]=0; //Ac Gr Piano - chPW[a]=64; // .. not .. bent ? - } - for(a=0; a<128; a++) - { - patchSet[a]=NULL; - drumSet[a]=NULL; - patchUsed[a]=0; - drumUsed[a]=0; - } - - //Always load the piano. - //Some files will assume its loaded without specifically - //issuing a Patch command... then we wonder why we can't hear anything - patchUsed[0]=1; - - //Scan the file to see what needs to be loaded - for(a=0; a<mf->numTracks; a++) - { - int ts=0; - - if(mf->tracks[a] == NULL) - { - printf("\nNULL TRACK !!!"); - rb->splash(HZ*2, true, "Null Track in loader."); - return -1; - } - - for(ts=0; ts<mf->tracks[a]->numEvents; ts++) - { - - if((getEvent(mf->tracks[a], ts)->status) == (MIDI_NOTE_ON+9)) - drumUsed[getEvent(mf->tracks[a], ts)->d1]=1; - - if( (getEvent(mf->tracks[a], ts)->status & 0xF0) == MIDI_PRGM) - { - if(patchUsed[getEvent(mf->tracks[a], ts)->d1]==0) - printf("\nI need to load patch %d.", getEvent(mf->tracks[a], ts)->d1); - patchUsed[getEvent(mf->tracks[a], ts)->d1]=1; - } - } - } - - int file = rb->open(filename, O_RDONLY); - if(file == -1) - { - rb->splash(HZ*2, true, "Bad patch config.\nDid you install the patchset?"); - return -1; - } - - char name[40]; - char fn[40]; - - //Scan our config file and load the right patches as needed - int c = 0; - rb->snprintf(name, 40, ""); - for(a=0; a<128; a++) - { - while(readChar(file)!=' ' && !eof(file)); - readTextBlock(file, name); - - rb->snprintf(fn, 40, "/.rockbox/patchset/%s.pat", name); - printf("\nLOADING: <%s> ", fn); - - if(patchUsed[a]==1) - patchSet[a]=gusload(fn); - -// if(patchSet[a] == NULL) -// return -1; - - while((c != '\n')) - c = readChar(file); - } - rb->close(file); - - file = rb->open(drumConfig, O_RDONLY); - if(file == -1) - { - rb->splash(HZ*2, true, "Bad drum config.\nDid you install the patchset?"); - return -1; - } - - //Scan our config file and load the drum data - int idx=0; - char number[30]; - while(!eof(file)) - { - readTextBlock(file, number); - readTextBlock(file, name); - rb->snprintf(fn, 40, "/.rockbox/patchset/%s.pat", name); - - idx = rb->atoi(number); - if(idx == 0) - break; - - if(drumUsed[idx]==1) - drumSet[idx]=gusload(fn); - -// if(drumSet[idx] == NULL) -// return -1; - - while((c != '\n') && (c != 255) && (!eof(file))) - c = readChar(file); - } - rb->close(file); - return 0; + char patchUsed[128]; + char drumUsed[128]; + int a=0; + for(a=0; a<MAX_VOICES; a++) + { + voices[a].cp=0; + voices[a].vol=0; + voices[a].ch=0; + voices[a].isUsed=0; + voices[a].note=0; + } + + for(a=0; a<16; a++) + { + chVol[a]=100; /* Default, not quite full blast.. */ + chPanLeft[a]=64; /* Center */ + chPanRight[a]=64; /* Center */ + chPat[a]=0; /* Ac Gr Piano */ + chPW[a]=64; /* .. not .. bent ? */ + } + for(a=0; a<128; a++) + { + patchSet[a]=NULL; + drumSet[a]=NULL; + patchUsed[a]=0; + drumUsed[a]=0; + } + + /* + * Always load the piano. + * Some files will assume its loaded without specifically + * issuing a Patch command... then we wonder why we can't hear anything + */ + patchUsed[0]=1; + + /* Scan the file to see what needs to be loaded */ + for(a=0; a<mf->numTracks; a++) + { + unsigned int ts=0; + + if(mf->tracks[a] == NULL) + { + printf("\nNULL TRACK !!!"); + rb->splash(HZ*2, true, "Null Track in loader."); + return -1; + } + + for(ts=0; ts<mf->tracks[a]->numEvents; ts++) + { + + if((getEvent(mf->tracks[a], ts)->status) == (MIDI_NOTE_ON+9)) + drumUsed[getEvent(mf->tracks[a], ts)->d1]=1; + + if( (getEvent(mf->tracks[a], ts)->status & 0xF0) == MIDI_PRGM) + { + if(patchUsed[getEvent(mf->tracks[a], ts)->d1]==0) + printf("\nI need to load patch %d.", getEvent(mf->tracks[a], ts)->d1); + patchUsed[getEvent(mf->tracks[a], ts)->d1]=1; + } + } + } + + int file = rb->open(filename, O_RDONLY); + if(file == -1) + { + rb->splash(HZ*2, true, "Bad patch config.\nDid you install the patchset?"); + return -1; + } + + char name[40]; + char fn[40]; + + /* Scan our config file and load the right patches as needed */ + int c = 0; + rb->snprintf(name, 40, ""); + for(a=0; a<128; a++) + { + while(readChar(file)!=' ' && !eof(file)); + readTextBlock(file, name); + + rb->snprintf(fn, 40, "/.rockbox/patchset/%s.pat", name); + printf("\nLOADING: <%s> ", fn); + + if(patchUsed[a]==1) + { + patchSet[a]=gusload(fn); + + if(patchSet[a] == NULL) /* There was an error loading it */ + return -1; + } + + while((c != '\n')) + c = readChar(file); + } + rb->close(file); + + file = rb->open(drumConfig, O_RDONLY); + if(file == -1) + { + rb->splash(HZ*2, true, "Bad drum config.\nDid you install the patchset?"); + return -1; + } + + /* Scan our config file and load the drum data */ + int idx=0; + char number[30]; + while(!eof(file)) + { + readTextBlock(file, number); + readTextBlock(file, name); + rb->snprintf(fn, 40, "/.rockbox/patchset/%s.pat", name); + + idx = rb->atoi(number); + if(idx == 0) + break; + + if(drumUsed[idx]==1) + { + drumSet[idx]=gusload(fn); + + if(drumSet[idx] == NULL) /* Error loading patch */ + return -1; + } + + while((c != '\n') && (c != 255) && (!eof(file))) + c = readChar(file); + } + rb->close(file); + return 0; } @@ -182,7 +188,7 @@ struct GWaveform * wf IDATA_ATTR; int s IDATA_ATTR; short s1 IDATA_ATTR; short s2 IDATA_ATTR; -short sample IDATA_ATTR; //For synthSample +short sample IDATA_ATTR; /* For synthSample */ unsigned int cpShifted IDATA_ATTR; unsigned char b1 IDATA_ATTR; @@ -191,31 +197,9 @@ unsigned char b2 IDATA_ATTR; inline int getSample(int s) { - - //16 bit samples - if(wf->mode&1) - { - - if(s<<1 >= wf->wavSize) - { - printf("\n!!!!! %d \t %d", s<<1, wf->wavSize); - return 0; - } -// signed short a = ((short *)wf->data)[s]; - - //Sign conversion moved into guspat.c - b1=wf->data[s<<1]+((wf->mode & 2) << 6); - b2=wf->data[(s<<1)|1]+((wf->mode & 2) << 6); - return (b1 | (b2<<8)) ; - - //Get rid of this sometime - } - else - { //8-bit samples - //Do we even have anything 8-bit in our set? - int b1=wf->data[s]+((wf->mode & 2) << 6); - return b1<<8; - } + /* Sign conversion moved to guspat.c */ + /* 8bit conversion NOT YET IMPLEMENTED in guspat.c */ + return ((short *) wf->data)[s]; } @@ -223,190 +207,194 @@ inline int getSample(int s) inline void setPoint(struct SynthObject * so, int pt) { - if(so->ch==9) //Drums, no ADSR - { - so->curOffset = 1<<27; - so->curRate = 1; - return; - } - - if(so->wf==NULL) - { - printf("\nCrap... null waveform..."); - exit(1); - } - if(so->wf->envRate==NULL) - { - printf("\nWaveform has no envelope set"); - exit(1); - } - - so->curPoint = pt; - - int r=0; - int rate = so->wf->envRate[pt]; - - r=3-((rate>>6) & 0x3); // Some blatant Timidity code for rate conversion... - r*=3; - r = (rate & 0x3f) << r; - - /* - Okay. This is the rate shift. Timidity defaults to 9, and sets - it to 10 if you use the fast decay option. Slow decay sounds better - on some files, except on some other files... you get chords that aren't - done decaying yet.. and they dont harmonize with the next chord and it - sounds like utter crap. Yes, even Timitidy does that. So I'm going to - default this to 10, and maybe later have an option to set it to 9 - for longer decays. - */ - so->curRate = r<<10; - - //Do this here because the patches assume a 44100 sampling rate - //We've halved our sampling rate, ergo the ADSR code will be - //called half the time. Ergo, double the rate to keep stuff - //sounding right. - so->curRate = so->curRate << 1; - - - so->targetOffset = so->wf->envOffset[pt]<<(20); - if(pt==0) - so->curOffset = 0; + if(so->ch==9) /* Drums, no ADSR */ + { + so->curOffset = 1<<27; + so->curRate = 1; + return; + } + + if(so->wf==NULL) + { + printf("\nCrap... null waveform..."); + exit(1); + } + if(so->wf->envRate==NULL) + { + printf("\nWaveform has no envelope set"); + exit(1); + } + + so->curPoint = pt; + + int r=0; + int rate = so->wf->envRate[pt]; + + r=3-((rate>>6) & 0x3); /* Some blatant Timidity code for rate conversion... */ + r*=3; + r = (rate & 0x3f) << r; + + /* + * Okay. This is the rate shift. Timidity defaults to 9, and sets + * it to 10 if you use the fast decay option. Slow decay sounds better + * on some files, except on some other files... you get chords that aren't + * done decaying yet.. and they dont harmonize with the next chord and it + * sounds like utter crap. Yes, even Timitidy does that. So I'm going to + * default this to 10, and maybe later have an option to set it to 9 + * for longer decays. + */ + so->curRate = r<<10; + + /* + * Do this here because the patches assume a 44100 sampling rate + * We've halved our sampling rate, ergo the ADSR code will be + * called half the time. Ergo, double the rate to keep stuff + * sounding right. + */ + so->curRate = so->curRate << 1; + + + so->targetOffset = so->wf->envOffset[pt]<<(20); + if(pt==0) + so->curOffset = 0; } inline void stopVoice(struct SynthObject * so) { - if(so->state == STATE_RAMPDOWN) - return; - so->state = STATE_RAMPDOWN; - so->decay = 255; + if(so->state == STATE_RAMPDOWN) + return; + so->state = STATE_RAMPDOWN; + so->decay = 255; } inline signed short int synthVoice() { - so = &voices[currentVoice]; - wf = so->wf; + so = &voices[currentVoice]; + wf = so->wf; - if(so->state != STATE_RAMPDOWN) - { - so->cp += so->delta; - } + if(so->state != STATE_RAMPDOWN) + { + so->cp += so->delta; + } - cpShifted = so->cp >> 10; + cpShifted = so->cp >> 10; - if( (cpShifted >= (wf->wavSize>>1)) && (so->state != STATE_RAMPDOWN)) - stopVoice(so); + if( (cpShifted > (wf->numSamples) && (so->state != STATE_RAMPDOWN))) + { + stopVoice(so); + } s2 = getSample((cpShifted)+1); - if((wf->mode & (LOOP_REVERSE|LOOP_PINGPONG)) && so->loopState == STATE_LOOPING && (cpShifted <= (wf->startLoop>>1))) - { - if(wf->mode & LOOP_REVERSE) - { - so->cp = (wf->endLoop)<<9; - cpShifted = so->cp >> 10; - s2=getSample((cpShifted)); - } else - { - so->delta = -so->delta; - so->loopDir = LOOPDIR_FORWARD; - } - } - - if((wf->mode & 28) && (so->cp>>10 >= wf->endLoop>>1)) - { - so->loopState = STATE_LOOPING; - if((wf->mode & (24)) == 0) - { - so->cp = (wf->startLoop)<<9; - cpShifted = so->cp >> 10; - s2=getSample((cpShifted)); - } else - { - so->delta = -so->delta; - so->loopDir = LOOPDIR_REVERSE; - } - } - - //Better, working, linear interpolation - s1=getSample((cpShifted)); - s = s1 + ((signed)((s2 - s1) * (so->cp & 1023))>>10); - - -//ADSR COMMENT WOULD GO FROM HERE......... - - if(so->curRate == 0) - stopVoice(so); - - - if(so->ch != 9) //Stupid ADSR code... and don't do ADSR for drums - { - if(so->curOffset < so->targetOffset) - { - so->curOffset += (so->curRate); - if(so -> curOffset > so->targetOffset && so->curPoint != 2) - { - if(so->curPoint != 5) - setPoint(so, so->curPoint+1); - else - stopVoice(so); - } - } else - { - so->curOffset -= (so->curRate); - if(so -> curOffset < so->targetOffset && so->curPoint != 2) - { - - if(so->curPoint != 5) - setPoint(so, so->curPoint+1); - else - stopVoice(so); - - } - } - } - - if(so->curOffset < 0) - so->isUsed=0; //This is OK - - - s = (s * (so->curOffset >> 22) >> 6); - -// ............. TO HERE - - - if(so->state == STATE_RAMPDOWN) - { - so->decay--; - if(so->decay == 0) - so->isUsed=0; - } - - s = s * so->decay; s = s >> 10; - - return s*((signed short int)so->vol*(signed short int)chVol[so->ch])>>14; + /* LOOP_REVERSE|LOOP_PINGPONG = 24 */ + if((wf->mode & (24)) && so->loopState == STATE_LOOPING && (cpShifted <= (wf->startLoop))) + { + if(wf->mode & LOOP_REVERSE) + { + so->cp = (wf->endLoop)<<10; + cpShifted = wf->endLoop; + s2=getSample((cpShifted)); + } else + { + so->delta = -so->delta; + so->loopDir = LOOPDIR_FORWARD; + } + } + + if((wf->mode & 28) && (cpShifted >= wf->endLoop)) + { + so->loopState = STATE_LOOPING; + if((wf->mode & (24)) == 0) + { + so->cp = (wf->startLoop)<<10; + cpShifted = wf->startLoop; + s2=getSample((cpShifted)); + } else + { + so->delta = -so->delta; + so->loopDir = LOOPDIR_REVERSE; + } + } + + /* Better, working, linear interpolation */ + s1=getSample((cpShifted)); + s = s1 + ((signed)((s2 - s1) * (so->cp & 1023))>>10); + + +/* ADSR COMMENT WOULD GO FROM HERE.........*/ + + if(so->curRate == 0) + stopVoice(so); + + + if(so->ch != 9) /* Stupid ADSR code... and don't do ADSR for drums */ + { + if(so->curOffset < so->targetOffset) + { + so->curOffset += (so->curRate); + if(so -> curOffset > so->targetOffset && so->curPoint != 2) + { + if(so->curPoint != 5) + setPoint(so, so->curPoint+1); + else + stopVoice(so); + } + } else + { + so->curOffset -= (so->curRate); + if(so -> curOffset < so->targetOffset && so->curPoint != 2) + { + + if(so->curPoint != 5) + setPoint(so, so->curPoint+1); + else + stopVoice(so); + + } + } + } + + if(so->curOffset < 0) + so->isUsed=0; /* This is OK because offset faded it out already */ + + + s = (s * (so->curOffset >> 22) >> 8); + +/* ............. TO HERE */ + + + if(so->state == STATE_RAMPDOWN) + { + so->decay--; + if(so->decay == 0) + so->isUsed=0; + s = (s * so->decay) >> 8; + } + + return s*((signed short int)so->vol*(signed short int)chVol[so->ch])>>14; } inline void synthSample(int * mixL, int * mixR) { -// signed int leftMix=0, rightMix=0, - *mixL = 0; - *mixR = 0; - for(currentVoice=0; currentVoice<MAX_VOICES; currentVoice++) - { - if(voices[currentVoice].isUsed==1) - { - sample = synthVoice(currentVoice); - *mixL += (sample*chPanLeft[voices[currentVoice].ch])>>7; - *mixR += (sample*chPanRight[voices[currentVoice].ch])>>7; - } - } - - //TODO: Automatic Gain Control, anyone? - //Or, should this be implemented on the DSP's output volume instead? - return; //No more ghetto lowpass filter.. linear intrpolation works well. + *mixL = 0; + *mixR = 0; + for(currentVoice=0; currentVoice<MAX_VOICES; currentVoice++) + { + if(voices[currentVoice].isUsed==1) + { + sample = synthVoice(currentVoice); + *mixL += (sample*chPanLeft[voices[currentVoice].ch])>>7; + *mixR += (sample*chPanRight[voices[currentVoice].ch])>>7; + } + } + + /* TODO: Automatic Gain Control, anyone? */ + /* Or, should this be implemented on the DSP's output volume instead? */ + + return; /* No more ghetto lowpass filter.. linear intrpolation works well. */ } |