diff options
author | Jerome Fisher | 2004-11-14 08:04:56 +0000 |
---|---|---|
committer | Jerome Fisher | 2004-11-14 08:04:56 +0000 |
commit | 504d54e8ab65dc68d71ac1a77ad4a393fb69e3e9 (patch) | |
tree | 010ff1888a14d19f860fd99252fc910421188d03 /backends/midi/mt32/tables.cpp | |
parent | 0ed79dfad78aeb61961c7f865e8cc7f45840fe28 (diff) | |
download | scummvm-rg350-504d54e8ab65dc68d71ac1a77ad4a393fb69e3e9.tar.gz scummvm-rg350-504d54e8ab65dc68d71ac1a77ad4a393fb69e3e9.tar.bz2 scummvm-rg350-504d54e8ab65dc68d71ac1a77ad4a393fb69e3e9.zip |
- 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
Diffstat (limited to 'backends/midi/mt32/tables.cpp')
-rw-r--r-- | backends/midi/mt32/tables.cpp | 312 |
1 files changed, 161 insertions, 151 deletions
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; } |