aboutsummaryrefslogtreecommitdiff
path: root/backends/midi
diff options
context:
space:
mode:
authorJerome Fisher2004-11-14 08:04:56 +0000
committerJerome Fisher2004-11-14 08:04:56 +0000
commit504d54e8ab65dc68d71ac1a77ad4a393fb69e3e9 (patch)
tree010ff1888a14d19f860fd99252fc910421188d03 /backends/midi
parent0ed79dfad78aeb61961c7f865e8cc7f45840fe28 (diff)
downloadscummvm-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')
-rw-r--r--backends/midi/mt32/partial.cpp106
-rw-r--r--backends/midi/mt32/partial.h5
-rw-r--r--backends/midi/mt32/synth.cpp11
-rw-r--r--backends/midi/mt32/tables.cpp312
-rw-r--r--backends/midi/mt32/tables.h34
5 files changed, 240 insertions, 228 deletions
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 = &noteLookups[noteVal];
+
+ // FIXME:KG: I think most places should refer to noteVal/noteLookup instead of these.
+ keyVal = poly->freqnum;
+ keyLookup = &noteLookups[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 *)&noteLookup->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 = &noteLookups[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);
};
}