From 504d54e8ab65dc68d71ac1a77ad4a393fb69e3e9 Mon Sep 17 00:00:00 2001 From: Jerome Fisher Date: Sun, 14 Nov 2004 08:04:56 +0000 Subject: - Huge cleanup of LUT stuff. - No longer stores a sawtooth waveform twice, once with each sample negated. - More graceful handling of truncated waveformcache files. - Fixed tuning of patterns for non-32KHz sample rates (needs checking). - Note: Waveform cache format changed, will automatically be rebuilt. svn-id: r15804 --- backends/midi/mt32/partial.cpp | 106 +++++++------- backends/midi/mt32/partial.h | 5 + backends/midi/mt32/synth.cpp | 11 +- backends/midi/mt32/tables.cpp | 312 +++++++++++++++++++++-------------------- backends/midi/mt32/tables.h | 34 ++--- 5 files changed, 240 insertions(+), 228 deletions(-) (limited to 'backends') diff --git a/backends/midi/mt32/partial.cpp b/backends/midi/mt32/partial.cpp index b3b1fc66be..f785f22d67 100644 --- a/backends/midi/mt32/partial.cpp +++ b/backends/midi/mt32/partial.cpp @@ -128,6 +128,12 @@ void Partial::startPartial(dpoly *usePoly, PatchCache *useCache, Partial *pairPa play = true; initKeyFollow(poly->freqnum); // Initialises noteVal, filtVal and realVal + noteLookup = ¬eLookups[noteVal]; + + // FIXME:KG: I think most places should refer to noteVal/noteLookup instead of these. + keyVal = poly->freqnum; + keyLookup = ¬eLookups[poly->freqnum]; + lfoPos = 0; pulsewidth = patchCache->pulsewidth + pwveltable[patchCache->pwsens][poly->vel]; if (pulsewidth > 100) { @@ -243,7 +249,7 @@ Bit16s *Partial::generateSamples(long length) { pcmWave = &synth->PCMList[patchCache->pcm]; if (pcmWave->aggSound == -1) { - delta = wavtabler[pcmWave->pcmnum][noteVal]; + delta = noteLookup->wavTable[pcmWave->pcmnum]; pcmAddr = pcmWave->addr; len = pcmWave->len; if (partialOff.pcmplace >= len) { @@ -258,7 +264,7 @@ Bit16s *Partial::generateSamples(long length) { } } else { int tmppcm = LoopPatterns[pcmWave->aggSound][loopPos]; - delta = looptabler[pcmWave->aggSound][loopPos][noteVal]; + delta = noteLookup->loopTable[pcmWave->aggSound][loopPos]; pcmAddr = synth->PCM[tmppcm].addr; len = synth->PCM[tmppcm].len; if (partialOff.pcmplace >= len) { @@ -271,7 +277,7 @@ Bit16s *Partial::generateSamples(long length) { } else { // Synthesis partial delta = 0x10707; - partialOff.pcmplace %= (Bit16u)(divtable[noteVal] >> 15); + partialOff.pcmplace %= (Bit16u)(noteLookup->div << 1); } // Build delta for position of next sample @@ -291,10 +297,6 @@ Bit16s *Partial::generateSamples(long length) { if (delta < 0x10000) { // Linear sound interpolation taddr = pcmAddr + partialOff.pcmplace; - if (taddr >= ROMSIZE) { - synth->printDebug("Overflow ROMSIZE!"); - taddr = ROMSIZE - 1; - } ra = synth->romfile[taddr]; //FIXME:KG: Deal with condition that taddr + 1 is past PCM length rb = synth->romfile[taddr + 1]; @@ -313,7 +315,7 @@ Bit16s *Partial::generateSamples(long length) { } } else { // Render synthesised sample - int divis = divtable[noteVal] >> 15; + int div = noteLookup->div; int wf = patchCache->waveform; int toff = partialOff.pcmplace; int minorplace = partialOff.pcmoffset >> 14; @@ -327,25 +329,21 @@ Bit16s *Partial::generateSamples(long length) { // sawtooth waveforms // Pulse width is not yet correct int pa, pb; - int hdivis = divis >> 1; - int divmark = smalldivtable[noteVal]; + int divmark = div << 8; - if (hdivis == 0) { - synth->printDebug("ERROR: hdivis=0 generating square wave, this should never happen!"); - hdivis = 1; + if (div == 0) { + synth->printDebug("ERROR: div=0 generating square wave, this should never happen!"); + div = 1; } - int ofs = toff % hdivis; - - int ofs3 = toff + ((divmark * pulsetable[pulsewidth]) >> 16); - ofs3 = ofs3 % (hdivis); - - pa = waveforms[1][noteVal][(ofs << 2)+minorplace]; - pb = waveforms[0][noteVal][(ofs3 << 2)+minorplace]; - ptemp = (pa + pb) * 4; - + int ofsA = toff % div; + int ofsB = toff + ((divmark * pulsetable[pulsewidth]) >> 16); + ofsB = ofsB % div; + pa = noteLookup->waveforms[0][(ofsA << 2) + minorplace]; + pb = noteLookup->waveforms[0][(ofsB << 2) + minorplace]; + ptemp = (pa - pb) * 4; // Non-bandlimited squarewave /* - ofs = (divis*pulsetable[patchCache->pulsewidth])>>8; + ofs = ((div << 1) * pulsetable[patchCache->pulsewidth]) >> 8; if (toff < ofs) ptemp = 1 * WGAMP; else @@ -356,27 +354,26 @@ Bit16s *Partial::generateSamples(long length) { // to how it looks on the MT-32. What it really does it takes the // square wave and multiplies it by a full cosine int waveoff = (toff << 2) + minorplace; - if (toff < sawtable[noteVal][pulsewidth]) - ptemp = waveforms[2][noteVal][waveoff % waveformsize[2][noteVal]]; + if (toff < noteLookup->sawTable[pulsewidth]) + ptemp = noteLookup->waveforms[1][waveoff % noteLookup->waveformSize[1]]; else - ptemp = waveforms[3][noteVal][waveoff % waveformsize[3][noteVal]]; + ptemp = noteLookup->waveforms[2][waveoff % noteLookup->waveformSize[2]]; ptemp = ptemp * 4; // This is the correct way // Seems slow to me (though bandlimited) -- doesn't seem to // sound any better though /* - hdivis = divis >> 1; - int divmark = smalldivtable[noteVal]; + int divmark = noteLookup->div << 8; //int pw = (patchCache->pulsewidth * pulsemod[filtval]) >> 8; - ofs = toff % (hdivis); + int ofs = toff % div; - ofs3 = toff + ((divmark*pulsetable[patchCache->pulsewidth])>>16); - ofs3 = ofs3 % (hdivis); + int ofs3 = toff + ((divmark * pulsetable[patchCache->pulsewidth]) >> 16); + ofs3 = ofs3 % div; - pa = waveforms[0][noteVal][ofs]; - pb = waveforms[1][noteVal][ofs3]; - ptemp = ((pa+pb) * waveforms[3][noteVal][toff]) / WGAMP; + pa = noteLookup->waveforms[0][ofs]; + pb = noteLookup->waveforms[0][ofs3]; + ptemp = ((pa - pb) * noteLookup->waveforms[2][toff]) / WGAMP; ptemp = ptemp *4; */ } @@ -632,8 +629,6 @@ Bit32s Partial::getFiltEnvelope() { keyfollow = filtVal; realfollow = realVal; - int fr = poly->freqnum; - if (tStat->decaying) { reshigh = tStat->envbase; reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize)); @@ -653,10 +648,11 @@ Bit32s Partial::getFiltEnvelope() { tStat->envbase = patchCache->filtEnv.envlevel[tStat->envstat]; tStat->envstat++; tStat->envpos = 0; - if (tStat->envstat == 3) + if (tStat->envstat == 3) { tStat->envsize = lasttimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]]; - else - tStat->envsize = (envtimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]] * timekeytable[(int)patchCache->filtEnv.envtkf][poly->freqnum]) >> 8; + } else { + tStat->envsize = (envtimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]] * keyLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8; + } tStat->envsize++; tStat->envdist = patchCache->filtEnv.envlevel[tStat->envstat] - tStat->envbase; @@ -684,14 +680,14 @@ Bit32s Partial::getFiltEnvelope() { if (bias!=0) { //synth->printDebug("Cutoff before %d", cutoff); if (patchCache->tvfdir == 0) { - if (fr < bias) { - dist = bias - fr; + if (keyVal < bias) { + dist = bias - keyVal; cutoff = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8; } } else { // > Bias - if (fr > bias) { - dist = fr - bias; + if (keyVal > bias) { + dist = keyVal - bias; cutoff = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8; } @@ -699,7 +695,7 @@ Bit32s Partial::getFiltEnvelope() { //synth->printDebug("Cutoff after %d", cutoff); } - depth = (depth * fildeptable[patchCache->tvfdepth][fr]) >> 8; + depth = (depth * keyLookup->fildepTable[patchCache->tvfdepth]) >> 8; reshigh = (reshigh * depth) >> 7; Bit32s tmp; @@ -718,7 +714,7 @@ Bit32s Partial::getFiltEnvelope() { reshigh = 100; else if (reshigh<0) reshigh = 0; - tmp = nfilttable[fr][cutoff][reshigh]; + tmp = keyLookup->nfiltTable[cutoff][reshigh]; //tmp *= keyfollow; //tmp /= realfollow; @@ -784,7 +780,7 @@ Bit32s Partial::getAmpEnvelope() { default: //Spot for timekey follow //Only used in subsquent envelope parameters, including the decay - tStat->envsize = (envtimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]] * timekeytable[(int)patchCache->ampEnv.envtkf][poly->freqnum]) >> 8; + tStat->envsize = (envtimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]] * keyLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8; //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize); break; @@ -820,14 +816,14 @@ PastCalc: bias = patchCache->ampbias[i]; if (patchCache->ampdir[i]==0) { // < Bias - if (poly->freqnum < bias) { - dist = bias-poly->freqnum; + if (keyVal < bias) { + dist = bias - keyVal; tc = (tc * ampbiastable[patchCache->ampblevel[i]][dist]) >> 8; } } else { // > Bias - if (poly->freqnum > bias) { - dist = poly->freqnum-bias; + if (keyVal > bias) { + dist = keyVal - bias; tc = (tc * ampbiastable[patchCache->ampblevel[i]][dist]) >> 8; } } @@ -860,11 +856,11 @@ Bit32s Partial::getPitchEnvelope() { tStat->envstat++; tStat->envbase = patchCache->pitchEnv.level[tStat->envstat]; - tStat->envsize = (envtimetable[(int)patchCache->pitchEnv.time[tStat->envstat]] * timekeytable[(int)patchCache->pitchEnv.timekeyfollow][poly->freqnum]) >> 8; + tStat->envsize = (envtimetable[(int)patchCache->pitchEnv.time[tStat->envstat]] * keyLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8; tStat->envpos = 0; tStat->envsize++; - tStat->envdist = patchCache->pitchEnv.level[tStat->envstat+1] - tStat->envbase; + tStat->envdist = patchCache->pitchEnv.level[tStat->envstat + 1] - tStat->envbase; } tc = tStat->envbase; tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize)); @@ -891,15 +887,15 @@ void Partial::startDecay(EnvelopeType envnum, Bit32s startval) { switch(envnum) { case EnvelopeType_amp: - tStat->envsize = (decaytimetable[(int)patchCache->ampEnv.envtime[4]] * timekeytable[(int)patchCache->ampEnv.envtkf][poly->freqnum]) >> 8; + tStat->envsize = (decaytimetable[(int)patchCache->ampEnv.envtime[4]] * keyLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8; tStat->envdist = -startval; break; case EnvelopeType_filt: - tStat->envsize = (decaytimetable[(int)patchCache->filtEnv.envtime[4]] * timekeytable[(int)patchCache->filtEnv.envtkf][poly->freqnum]) >> 8; + tStat->envsize = (decaytimetable[(int)patchCache->filtEnv.envtime[4]] * keyLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8; tStat->envdist = -startval; break; case EnvelopeType_pitch: - tStat->envsize = (decaytimetable[(int)patchCache->pitchEnv.time[3]] * timekeytable[(int)patchCache->pitchEnv.timekeyfollow][poly->freqnum]) >> 8 ; + tStat->envsize = (decaytimetable[(int)patchCache->pitchEnv.time[3]] * keyLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8 ; tStat->envdist = patchCache->pitchEnv.level[4] - startval; break; default: diff --git a/backends/midi/mt32/partial.h b/backends/midi/mt32/partial.h index ad7cd65b0b..2760b8fa2a 100644 --- a/backends/midi/mt32/partial.h +++ b/backends/midi/mt32/partial.h @@ -25,6 +25,7 @@ namespace MT32Emu { class Synth; +struct NoteLookup; enum EnvelopeType { EnvelopeType_amp = 0, @@ -63,6 +64,10 @@ private: // Keyfollowed note value int noteVal; + NoteLookup *noteLookup; // Lookup stuff for this noteVal + + int keyVal; + NoteLookup *keyLookup; // Keyfollowed filter values int realVal; diff --git a/backends/midi/mt32/synth.cpp b/backends/midi/mt32/synth.cpp index 4c697b893f..9e11fb2bbf 100644 --- a/backends/midi/mt32/synth.cpp +++ b/backends/midi/mt32/synth.cpp @@ -117,7 +117,7 @@ Synth::Synth() { isOpen = false; reverbModel = NULL; partialManager = NULL; - memset(waveforms, 0, sizeof(waveforms)); + memset(noteLookups, 0, sizeof(noteLookups)); memset(parts, 0, sizeof(parts)); } @@ -690,11 +690,12 @@ void Synth::close(void) { if (!isOpen) return; - for (int t = 0; t < 4; t++) { + for (int t = 0; t < 3; t++) { for (int m = 0; m < NUM_NOTES; m++) { - if (waveforms[t][m]!=NULL) { - delete[] waveforms[t][m]; - waveforms[t][m] = NULL; + if (noteLookups[m].waveforms[t] != NULL) { + delete[] noteLookups[m].waveforms[t]; + noteLookups[m].waveforms[t] = NULL; + noteLookups[m].waveformSize[t] = 0; } } } diff --git a/backends/midi/mt32/tables.cpp b/backends/midi/mt32/tables.cpp index 3afd03e226..2f710e8e0e 100644 --- a/backends/midi/mt32/tables.cpp +++ b/backends/midi/mt32/tables.cpp @@ -89,24 +89,7 @@ Bit32s voltable[128]; float ResonFactor[31]; float ResonInv[31]; -// Per-note initialisation tables follow - -Bit16s freqtable[NUM_NOTES]; -Bit32s fildeptable[5][NUM_NOTES]; -Bit32s timekeytable[5][NUM_NOTES]; -int filttable[2][NUM_NOTES][201]; -int nfilttable[NUM_NOTES][101][101]; - -Bit32s divtable[NUM_NOTES]; -Bit32s smalldivtable[NUM_NOTES]; -Bit32u wavtabler[54][NUM_NOTES]; -Bit32u looptabler[9][10][NUM_NOTES]; -Bit32s sawtable[NUM_NOTES][101]; - -Bit16s *waveforms[4][NUM_NOTES]; -Bit32u waveformsize[4][NUM_NOTES]; -static Bit16s tmpforms[4][65536]; - +NoteLookup noteLookups[NUM_NOTES]; // Begin filter stuff @@ -236,10 +219,10 @@ static void initEnvelopes(float samplerate) { if (pt < 0) pt = 0; - pulsetable[lf] = (int)((pt) * 215.04f) + 128; + pulsetable[lf] = (int)(pt * 215.04f) + 128; // I am certain of this: Verified by hand LFO log - lfotable[lf] = (Bit32u)(((float)samplerate) / (pow(1.088883372f,(float)lf) * 0.021236044f)); + lfotable[lf] = (Bit32u)(((float)samplerate) / (powf(1.088883372f,(float)lf) * 0.021236044f)); //LOG(LOG_ERROR|LOG_MISC,"lf %d = lfo %d pulsetable %d", lf, lfotable[lf], pulsetable[lf]); } @@ -320,6 +303,9 @@ void TableInitialiser::initMT32ConstantTables(Synth *synth) { for (lf = 0; lf <= 200; lf++) { //FIXME:KG: I'm fairly sure this is wrong... lf=100 should yield no fine-tuning (4096)? finetable[lf] = (int)((powf(2.0f, (((float)lf / 200.0f) - 1.0f) / 12.0f)) * 4096.0f); + + // FIXME:KG: This now gives a range of -1 .. 1 semitone. Should be correct, but check + //finetable[lf] = (int)((powf(2.0f, (((float)lf / 100.0f) - 1.0f) / 12.0f)) * 4096.0f); } float lff; @@ -333,9 +319,9 @@ void TableInitialiser::initMT32ConstantTables(Synth *synth) { for (lf = 0; lf < 128; lf++) { // Converts MIDI velocity to volume. - voltable[lf] = (int)(127.0 * pow((float)lf / 127.0, DOUBLE_LN)); + voltable[lf] = (int)(127.0f * powf((float)lf / 127.0f, FLOAT_LN)); } - for (lf = 0; lf < MAX_SAMPLE_OUTPUT; lf++) { + for (unsigned int i = 0; i < MAX_SAMPLE_OUTPUT; i++) { int myRand; myRand = rand(); int origRand = myRand; @@ -343,7 +329,7 @@ void TableInitialiser::initMT32ConstantTables(Synth *synth) { // This one is slower but works with all values of RAND_MAX myRand = (int)((origRand - RAND_MAX / 2) / (float)RAND_MAX * (WGAMP / 2)); //FIXME:KG: Original ultimately set the lowest two bits to 0, for no obvious reason - smallnoise[lf] = (Bit16s)myRand; + smallnoise[i] = (Bit16s)myRand; } float tdist; @@ -469,115 +455,94 @@ void TableInitialiser::initMT32ConstantTables(Synth *synth) { // Per-note table initialisation follows -static void initSaw(int f, Bit32s div) { +static void initSaw(NoteLookup *noteLookup, Bit32s div) { for (int rsaw = 0; rsaw <= 100; rsaw++) { float fsaw; if (rsaw < 50) fsaw = 50.0f; else fsaw = (float)rsaw; - int tmpdiv = div << 1; + int tmpdiv = div << 17; //(66 - (((A8 - 50) / 50) ^ 0.63) * 50) / 132 float sawfact = (66.0f - (powf((fsaw - 50.0f) / 50.0f, 0.63f) * 50.0f)) / 132.0f; - sawtable[f][rsaw] = (int)(sawfact * (float)tmpdiv) >> 16; - //synth->printDebug("F %d divtable %d saw %d sawtable %d", f, div>>16, rsaw, sawtable[f][rsaw]); + noteLookup->sawTable[rsaw] = (int)(sawfact * (float)tmpdiv) >> 16; + //synth->printDebug("F %d divtable %d saw %d sawtable %d", f, div, rsaw, sawtable[f][rsaw]); } } -static void initDep(int f) { +static void initDep(NoteLookup *noteLookup, float f) { for (int dep = 0; dep < 5; dep++) { if (dep == 0) { - fildeptable[dep][f] = 256; - timekeytable[dep][f] = 256; + noteLookup->fildepTable[dep] = 256; + noteLookup->timekeyTable[dep] = 256; } else { float depfac = 3000.0f; float ff, tempdep; depfac = (float)depexp[dep]; - ff = ((float)f - (float)MIDDLEC) / depfac; + ff = (f - (float)MIDDLEC) / depfac; tempdep = powf(2, ff) * 256.0f; - fildeptable[dep][f] = (int)tempdep; + noteLookup->fildepTable[dep] = (int)tempdep; - ff = (float)(exp(tkcatconst[dep] * ((float)MIDDLEC-(float)f)) * tkcatmult[dep]); - timekeytable[dep][f] = (int)(ff * 256.0f); + ff = (float)(exp(tkcatconst[dep] * ((float)MIDDLEC - f)) * tkcatmult[dep]); + noteLookup->timekeyTable[dep] = (int)(ff * 256.0f); } } - //synth->printDebug("F %d d1 %x d2 %x d3 %x d4 %x d5 %x", f, fildeptable[0][f],fildeptable[1][f],fildeptable[2][f],fildeptable[3][f],fildeptable[4][f]); + //synth->printDebug("F %f d1 %x d2 %x d3 %x d4 %x d5 %x", f, noteLookup->fildepTable[0], noteLookup->fildepTable[1], noteLookup->fildepTable[2], noteLookup->fildepTable[3], noteLookup->fildepTable[4]); } -void TableInitialiser::initWave(Synth *synth, File *file, bool reading, bool writing, int f, float freq, float rate, double ampsize, Bit32s div) { - double sd = (2.0 * DOUBLE_PI) / ((((float)div / 65536.0)) * 4.0); - waveformsize[0][f] = div >> 14; - waveformsize[1][f] = div >> 14; - waveformsize[2][f] = div >> 14; - waveformsize[3][f] = div >> 13; - for (int i = 0; i < 4; i++) - waveforms[i][f] = new Bit16s[waveformsize[i][f]]; - if (reading) { - for (int i = 0; i < 4; i++) { - size_t len = waveformsize[i][f] * sizeof(Bit16s); - if (file->read(waveforms[i][f], len) != len) { - synth->printDebug("Error reading wave file cache!"); - break; - } +File *TableInitialiser::initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div, File *file) { + int iDiv = (int)div; + noteLookup->waveformSize[0] = iDiv << 2; + noteLookup->waveformSize[1] = iDiv << 2; + noteLookup->waveformSize[2] = iDiv << 3; + for (int i = 0; i < 3; i++) { + if (noteLookup->waveforms[i] == NULL) { + noteLookup->waveforms[i] = new Bit16s[noteLookup->waveformSize[i]]; } - } else { - float dumbfire; - double saw = 0.0f; - double sa = 0.0; - int fa = 0; - memset(tmpforms, 0,sizeof(tmpforms)); - - while (sa <= (2.0 * DOUBLE_PI)) { - float sqp; - - if (sa < DOUBLE_PI) { - sqp = -1; - sqp = (float)(sqp + (0.25 * (sa/DOUBLE_PI))); - } else { - sqp=1; - sqp = (float)(sqp - (0.25 * ((sa - DOUBLE_PI)/DOUBLE_PI))); + } + if (file != NULL) { + for (int i = 0; i < 3 && file != NULL; i++) { + size_t len = noteLookup->waveformSize[i]; + for (unsigned int j = 0; j < len; j++) { + if (!file->readBit16u((Bit16u *)¬eLookup->waveforms[i][j])) { + synth->printDebug("Error reading wave file cache!"); + file->close(); + file = NULL; + break; + } } - - saw = 0; - for (int sinus = 1; sinus * freq < rate; sinus++) { + } + } + if (file == NULL) { + double sd = DOUBLE_PI / (div * 2.0); + + for (int fa = 0; fa < (iDiv << 2); fa++) { + double sa = fa * sd; + +#if 0 + //FIXME:KG: Credit Timo Strunk (bastardo on #scummvm) for help with this! + float saw = 0.5f * FLOAT_PI - sa / 2; +#else + double saw = 0.0; + for (int sinus = 1; sinus < div; sinus++) { double fsinus = (double)sinus; - saw += (1 / fsinus) * sin(fsinus * sa); + saw += sin(fsinus * sa) / fsinus; } +#endif - dumbfire = (float)(sa / 2); - - //This works pretty good - tmpforms[0][fa] = (Bit16s)(saw * -ampsize / 2); - tmpforms[1][fa] = (Bit16s)(saw * ampsize / 2); - - tmpforms[2][fa] = (Bit16s)(cos(dumbfire) * -ampsize); - tmpforms[3][fa * 2] = (Bit16s)(cos(sa - DOUBLE_PI) * -ampsize); - tmpforms[3][fa * 2 + 1] = (Bit16s)(cos((sa + (sd / 2)) - DOUBLE_PI) * -ampsize); - - fa++; - sa += sd; - } - - //synth->printDebug("f num %d freq %f and fa %d, div>>13=%d", f, freq, fa, div>>13); - - for (int i = 0; i < 4; i++) - memcpy(waveforms[i][f], tmpforms[i], waveformsize[i][f] * sizeof (Bit16s)); - - if (writing) { - for (int i = 0; i < 4; i++) { - int len = waveformsize[i][f] * sizeof(Bit16s); - if (!file->write(waveforms[i][f], len)) { - synth->printDebug("Error writing waveform cache file"); - break; - } - } + // This works pretty well + noteLookup->waveforms[0][fa] = (Bit16s)(saw * -ampsize / 2); + noteLookup->waveforms[1][fa] = (Bit16s)(cos(sa / 2.0) * -ampsize); + noteLookup->waveforms[2][fa * 2] = (Bit16s)(cos(sa - DOUBLE_PI) * -ampsize); + noteLookup->waveforms[2][fa * 2 + 1] = (Bit16s)(cos((sa + (sd / 2)) - DOUBLE_PI) * -ampsize); } } + return file; } -static void initFiltTable(int f, float freq, float rate) { +static void initFiltTable(NoteLookup *noteLookup, float freq, float rate) { for (int tr = 0; tr <= 200; tr++) { float ftr = (float)tr; @@ -587,18 +552,18 @@ static void initFiltTable(int f, float freq, float rate) { // I think this is the one float brsq = powf(10.0f, (tr / 50.0f) - 1.0f); - filttable[0][f][tr] = (int)((freq * brsq) / (rate / 2) * FILTERGRAN); - if (filttable[0][f][tr]>=((FILTERGRAN*15)/16)) - filttable[0][f][tr] = ((FILTERGRAN*15)/16); + noteLookup->filtTable[0][tr] = (int)((freq * brsq) / (rate / 2) * FILTERGRAN); + if (noteLookup->filtTable[0][tr]>=((FILTERGRAN*15)/16)) + noteLookup->filtTable[0][tr] = ((FILTERGRAN*15)/16); float brsa = powf(10.0f, ((tr / 55.0f) - 1.0f)) / 2.0f; - filttable[1][f][tr] = (int)((freq * brsa) / (rate / 2) * FILTERGRAN); - if (filttable[1][f][tr]>=((FILTERGRAN*15)/16)) - filttable[1][f][tr] = ((FILTERGRAN*15)/16); + noteLookup->filtTable[1][tr] = (int)((freq * brsa) / (rate / 2) * FILTERGRAN); + if (noteLookup->filtTable[1][tr]>=((FILTERGRAN*15)/16)) + noteLookup->filtTable[1][tr] = ((FILTERGRAN*15)/16); } } -static void initNFiltTable(int f, float freq, float rate) { +static void initNFiltTable(NoteLookup *noteLookup, float freq, float rate) { for (int cf = 0; cf <= 100; cf++) { float cfmult = (float)cf; @@ -609,53 +574,127 @@ static void initNFiltTable(int f, float freq, float rate) { float freqsum = expf((cfmult + tfadd) / 30.0f) / 4.0f; - nfilttable[f][cf][tf] = (int)((freq * freqsum) / (rate / 2)*FILTERGRAN); - if (nfilttable[f][cf][tf] >= ((FILTERGRAN * 15) / 16)) - nfilttable[f][cf][tf] = ((FILTERGRAN * 15) / 16); + noteLookup->nfiltTable[cf][tf] = (int)((freq * freqsum) / (rate / 2) * FILTERGRAN); + if (noteLookup->nfiltTable[cf][tf] >= ((FILTERGRAN * 15) / 16)) + noteLookup->nfiltTable[cf][tf] = ((FILTERGRAN * 15) / 16); } } } -void TableInitialiser::initNotes(Synth *synth, PCMWave pcms[54], float rate) { - char filename[32]; +File *TableInitialiser::initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWave pcmWaves[54], File *file) { + float ampsize = WGAMP; + float freq = (float)(tuning * pow(2.0, ((double)note - MIDDLEA) / 12.0)); + float div = rate / freq; + noteLookup->div = (int)div; + + initSaw(noteLookup, noteLookup->div); + initDep(noteLookup, note); + + //synth->printDebug("Note %f; freq=%f, div=%f", note, freq, rate / freq); + file = initWave(synth, noteLookup, ampsize, div, file); + + // Create the pitch tables + + float rateMult = 32000.0f / rate; + float tuner = rateMult * freq * 65536.0f; + for (int pc = 0; pc < 54; pc++) { + noteLookup->wavTable[pc] = (int)(tuner / pcmWaves[pc].tune); + } + for (int lp = 0; lp < 9; lp++) { + for (int ln = 0; ln < 10; ln++) { + // FIXME:KG: Surely this needs to be adjusted for the rate? + // If not, remove rateMult * from below + // (Note: I'm assuming the LoopPatternTuning constants were intended for 32k rate) + noteLookup->loopTable[lp][ln] = (int)(rateMult * (float)LoopPatternTuning[lp][ln] * (freq / 220.0f)); + } + } + + initFiltTable(noteLookup, freq, rate); + initNFiltTable(noteLookup, freq, rate); + return file; +} + +void TableInitialiser::initNotes(Synth *synth, PCMWave pcmWaves[54], float rate, float tuning) { + char filename[64]; int intRate = (int)rate; - sprintf(filename, "waveformcache-%d.raw", intRate); + char version[4] = {0, 0, 0, 1}; + sprintf(filename, "waveformcache-%d-%.1f.raw", intRate, tuning); File *file = NULL; - bool reading = false, writing = false; - char header[16]; + char header[20]; strncpy(header, "MT32WAVE", 8); int pos = 8; // Version... for (int i = 0; i < 4; i++) - header[pos++] = 0; - header[pos++] = (char)(intRate & 0xFF); - header[pos++] = (char)((intRate >> 8) & 0xFF); + header[pos++] = version[i]; + header[pos++] = (char)((intRate >> 24) & 0xFF); header[pos++] = (char)((intRate >> 16) & 0xFF); - header[pos] = (char)((intRate >> 24) & 0xFF); + header[pos++] = (char)((intRate >> 8) & 0xFF); + header[pos++] = (char)(intRate & 0xFF); + int intTuning = (int)tuning; + header[pos++] = (char)((intTuning >> 8) & 0xFF); + header[pos++] = (char)(intTuning & 0xFF); + header[pos++] = 0; + header[pos] = (char)((tuning - intTuning) * 10); #if MT32EMU_WAVECACHEMODE < 2 + bool reading = false; file = synth->openFile(filename, File::OpenMode_read); if (file != NULL) { char fileHeader[16]; if (file->read(fileHeader, 16) == 16) { if (memcmp(fileHeader, header, 16) == 0) { - reading = true; + Bit16u endianCheck; + if (file->readBit16u(&endianCheck)) { + if (endianCheck == 1) { + reading = true; + } else { + synth->printDebug("Endian check in %s does not match expected - will generate", filename); + } + } else { + synth->printDebug("Unable to read endian check in %s - will generate", filename); + } } else { synth->printDebug("Header of %s does not match expected - will generate", filename); } } else { synth->printDebug("Error reading 16 bytes of %s - will generate", filename); } + if (!reading) { + file->close(); + file = NULL; + } } else { synth->printDebug("Unable to open %s for reading - will generate", filename); } #endif + + //FIXME:KG: may only need to do 12 to 108 + //12..108 is the range allowed by note on commands, but the key can be modified by pitch keyfollow + //and adjustment for timbre pitch, so the results can be outside that range. Do move it (by octave) into + // the 12..108 range, or keep it in 0..127 range, or something else altogether? + for (int f = 12; f < 109; f++) { + NoteLookup *noteLookup = ¬eLookups[f]; + file = initNote(synth, noteLookup, (float)f, rate, tuning, pcmWaves, file); + } + #if MT32EMU_WAVECACHEMODE == 0 || MT32EMU_WAVECACHEMODE == 2 - if (!reading) { + if (file == NULL) { file = synth->openFile(filename, File::OpenMode_write); if (file != NULL) { - if (file->write(header, 16) == 16) { - writing = true; + if (file->write(header, 16) == 16 && file->writeBit16u(1)) { + for (int f = 12; f < 109 && file != NULL; f++) { + for (int i = 0; i < 3 && file != NULL; i++) { + int len = noteLookups[f].waveformSize[i]; + for (int j = 0; j < len; j++) { + if (!file->writeBit16u(noteLookups[f].waveforms[i][j])) { + synth->printDebug("Error writing waveform cache file"); + file->close(); + file = NULL; + break; + } + } + } + } } else { synth->printDebug("Error writing 16-byte header to %s - won't continue saving", filename); } @@ -665,35 +704,6 @@ void TableInitialiser::initNotes(Synth *synth, PCMWave pcms[54], float rate) { } #endif - double ampsize = WGAMP; - for (int f = 12; f < 109; f++) { - float freq = (float)(TUNING * pow(2.0, ((double)f - 69.0) / 12.0)); - freqtable[f] = (Bit16s)freq; - divtable[f] = (int)(rate / freq); - smalldivtable[f] = divtable[f] << 8; - divtable[f] = divtable[f] << 16; - - initSaw(f, divtable[f]); - initDep(f); - - //synth->printDebug("F %d sd %f div %d", f, sd, divtable[f]); - initWave(synth, file, reading, writing, f, freq, rate, ampsize, divtable[f]); - - // Create the pitch tables - - float tuner = (32000.0f / rate) * 65536.0f; - for (int pc = 0; pc < 54; pc++) - wavtabler[pc][f] = (int)(tuner * (freq / pcms[pc].tune)); - for (int lp = 0; lp < 9; lp++) { - for (int ln = 0; ln < 10; ln++) { - // FIXME:KG: Surely this needs to be adjusted for the rate? - looptabler[lp][ln][f] = (int)((float)LoopPatternTuning[lp][ln] * (freq / 220.0f)); - } - } - - initFiltTable(f, freq, rate); - initNFiltTable(f, freq, rate); - } if (file != NULL) synth->closeFile(file); } @@ -710,7 +720,7 @@ bool TableInitialiser::initMT32Tables(Synth *synth, PCMWave pcms[54], float samp initialisedSampleRate = sampleRate; } // This always needs to be done, to allocate the waveforms - initNotes(synth, pcms, sampleRate); + initNotes(synth, pcms, sampleRate, TUNING); return true; } diff --git a/backends/midi/mt32/tables.h b/backends/midi/mt32/tables.h index 3e73400edf..46be3f1548 100644 --- a/backends/midi/mt32/tables.h +++ b/backends/midi/mt32/tables.h @@ -42,7 +42,7 @@ const int MIDDLEA = 69; // By this I mean "A above middle C" // FIXME:KG: Keeping it at 440.0f for now, as in original. Check with CC const float TUNING = 440.0f; -const int NUM_NOTES = 128; // MIDI supports 128 notes/keys +const int NUM_NOTES = 128; // Number of slots for note LUT (we actually only use 12..108) // Amplitude of waveform generator const int WGAMP = 7168; // 8192? @@ -55,17 +55,12 @@ extern Bit16s smallnoise[MAX_SAMPLE_OUTPUT]; // Some optimization stuff extern Bit32s keytable[217]; -extern Bit32s divtable[NUM_NOTES]; -extern Bit32s smalldivtable[NUM_NOTES]; -extern Bit32u wavtabler[54][NUM_NOTES]; -extern Bit32u looptabler[9][10][NUM_NOTES]; extern Bit16s sintable[65536]; extern Bit32u lfotable[101]; extern Bit32s penvtable[16][101]; extern Bit32s filveltable[128][101]; extern Bit32s veltkeytable[5][128]; extern Bit32s pulsetable[101]; -extern Bit32s sawtable[NUM_NOTES][101]; extern Bit32s ampbiastable[13][128]; extern Bit32s fbiastable[15][128]; extern float filtcoeff[FILTERGRAN][31][8]; @@ -79,23 +74,28 @@ extern Bit32s lasttimetable[101]; extern Bit32s voltable[128]; extern float ResonInv[31]; -extern Bit16s freqtable[NUM_NOTES]; -extern Bit32s fildeptable[5][NUM_NOTES]; -extern Bit32s timekeytable[5][NUM_NOTES]; -extern int filttable[2][NUM_NOTES][201]; -extern int nfilttable[NUM_NOTES][101][101]; - -extern const Bit8s LoopPatterns[9][10]; +struct NoteLookup { + Bit32s div; + Bit32u wavTable[54]; + Bit32u loopTable[9][10]; + Bit32s sawTable[101]; + Bit32s fildepTable[5]; + Bit32s timekeyTable[5]; + int filtTable[2][201]; + int nfiltTable[101][101]; + Bit16s *waveforms[3]; + Bit32u waveformSize[3]; +}; -extern Bit16s *waveforms[4][NUM_NOTES]; -extern Bit32u waveformsize[4][NUM_NOTES]; +extern NoteLookup noteLookups[NUM_NOTES]; class TableInitialiser { static void initMT32ConstantTables(Synth *synth); - static void initWave(Synth *synth, File *file, bool reading, bool writing, int f, float freq, float rate, double ampsize, Bit32s div); - static void initNotes(Synth *synth, PCMWave pcms[54], float rate); + static File *initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div, File *file); + static void initNotes(Synth *synth, PCMWave pcms[54], float rate, float tuning); public: static bool initMT32Tables(Synth *synth, PCMWave pcms[54], float sampleRate); + static File *initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWave pcmWaves[54], File *file); }; } -- cgit v1.2.3