DOSBox-X
|
00001 /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher 00002 * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev 00003 * 00004 * This program is free software: you can redistribute it and/or modify 00005 * it under the terms of the GNU Lesser General Public License as published by 00006 * the Free Software Foundation, either version 2.1 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU Lesser General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU Lesser General Public License 00015 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00016 */ 00017 00018 #include <cmath> 00019 #include "mt32emu.h" 00020 #include "mmath.h" 00021 #include "LegacyWaveGenerator.h" 00022 00023 #if MT32EMU_ACCURATE_WG == 1 00024 00025 namespace MT32Emu { 00026 00027 static const float MIDDLE_CUTOFF_VALUE = 128.0f; 00028 static const float RESONANCE_DECAY_THRESHOLD_CUTOFF_VALUE = 144.0f; 00029 static const float MAX_CUTOFF_VALUE = 240.0f; 00030 00031 float LA32WaveGenerator::getPCMSample(unsigned int position) { 00032 if (position >= pcmWaveLength) { 00033 if (!pcmWaveLooped) { 00034 return 0; 00035 } 00036 position = position % pcmWaveLength; 00037 } 00038 Bit16s pcmSample = pcmWaveAddress[position]; 00039 float sampleValue = EXP2F(((pcmSample & 32767) - 32787.0f) / 2048.0f); 00040 return ((pcmSample & 32768) == 0) ? sampleValue : -sampleValue; 00041 } 00042 00043 void LA32WaveGenerator::initSynth(const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) { 00044 this->sawtoothWaveform = sawtoothWaveform; 00045 this->pulseWidth = pulseWidth; 00046 this->resonance = resonance; 00047 00048 wavePos = 0.0f; 00049 lastFreq = 0.0f; 00050 00051 pcmWaveAddress = NULL; 00052 active = true; 00053 } 00054 00055 void LA32WaveGenerator::initPCM(const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped, const bool pcmWaveInterpolated) { 00056 this->pcmWaveAddress = pcmWaveAddress; 00057 this->pcmWaveLength = pcmWaveLength; 00058 this->pcmWaveLooped = pcmWaveLooped; 00059 this->pcmWaveInterpolated = pcmWaveInterpolated; 00060 00061 pcmPosition = 0.0f; 00062 active = true; 00063 } 00064 00065 float LA32WaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pitch, const Bit32u cutoffRampVal) { 00066 if (!active) { 00067 return 0.0f; 00068 } 00069 00070 this->amp = amp; 00071 this->pitch = pitch; 00072 00073 float sample = 0.0f; 00074 00075 // SEMI-CONFIRMED: From sample analysis: 00076 // (1) Tested with a single partial playing PCM wave 77 with pitchCoarse 36 and no keyfollow, velocity follow, etc. 00077 // This gives results within +/- 2 at the output (before any DAC bitshifting) 00078 // when sustaining at levels 156 - 255 with no modifiers. 00079 // (2) Tested with a special square wave partial (internal capture ID tva5) at TVA envelope levels 155-255. 00080 // This gives deltas between -1 and 0 compared to the real output. Note that this special partial only produces 00081 // positive amps, so negative still needs to be explored, as well as lower levels. 00082 // 00083 // Also still partially unconfirmed is the behaviour when ramping between levels, as well as the timing. 00084 00085 float amp = EXP2F(ampVal / -1024.0f / 4096.0f); 00086 float freq = EXP2F(pitch / 4096.0f - 16.0f) * SAMPLE_RATE; 00087 00088 if (isPCMWave()) { 00089 // Render PCM waveform 00090 int len = pcmWaveLength; 00091 int intPCMPosition = (int)pcmPosition; 00092 if (intPCMPosition >= len && !pcmWaveLooped) { 00093 // We're now past the end of a non-looping PCM waveform so it's time to die. 00094 deactivate(); 00095 return 0.0f; 00096 } 00097 float positionDelta = freq * 2048.0f / SAMPLE_RATE; 00098 00099 // Linear interpolation 00100 float firstSample = getPCMSample(intPCMPosition); 00101 // We observe that for partial structures with ring modulation the interpolation is not applied to the slave PCM partial. 00102 // It's assumed that the multiplication circuitry intended to perform the interpolation on the slave PCM partial 00103 // is borrowed by the ring modulation circuit (or the LA32 chip has a similar lack of resources assigned to each partial pair). 00104 if (pcmWaveInterpolated) { 00105 sample = firstSample + (getPCMSample(intPCMPosition + 1) - firstSample) * (pcmPosition - intPCMPosition); 00106 } else { 00107 sample = firstSample; 00108 } 00109 00110 float newPCMPosition = pcmPosition + positionDelta; 00111 if (pcmWaveLooped) { 00112 newPCMPosition = fmod(newPCMPosition, (float)pcmWaveLength); 00113 } 00114 pcmPosition = newPCMPosition; 00115 } else { 00116 // Render synthesised waveform 00117 wavePos *= lastFreq / freq; 00118 lastFreq = freq; 00119 00120 float resAmp = EXP2F(1.0f - (32 - resonance) / 4.0f); 00121 { 00122 //static const float resAmpFactor = EXP2F(-7); 00123 //resAmp = EXP2I(resonance << 10) * resAmpFactor; 00124 } 00125 00126 // The cutoffModifier may not be supposed to be directly added to the cutoff - 00127 // it may for example need to be multiplied in some way. 00128 // The 240 cutoffVal limit was determined via sample analysis (internal Munt capture IDs: glop3, glop4). 00129 // More research is needed to be sure that this is correct, however. 00130 float cutoffVal = cutoffRampVal / 262144.0f; 00131 if (cutoffVal > MAX_CUTOFF_VALUE) { 00132 cutoffVal = MAX_CUTOFF_VALUE; 00133 } 00134 00135 // Wave length in samples 00136 float waveLen = SAMPLE_RATE / freq; 00137 00138 // Init cosineLen 00139 float cosineLen = 0.5f * waveLen; 00140 if (cutoffVal > MIDDLE_CUTOFF_VALUE) { 00141 cosineLen *= EXP2F((cutoffVal - MIDDLE_CUTOFF_VALUE) / -16.0f); // found from sample analysis 00142 } 00143 00144 // Start playing in center of first cosine segment 00145 // relWavePos is shifted by a half of cosineLen 00146 float relWavePos = wavePos + 0.5f * cosineLen; 00147 if (relWavePos > waveLen) { 00148 relWavePos -= waveLen; 00149 } 00150 00151 // Ratio of positive segment to wave length 00152 float pulseLen = 0.5f; 00153 if (pulseWidth > 128) { 00154 pulseLen = EXP2F((64 - pulseWidth) / 64.0f); 00155 //static const float pulseLenFactor = EXP2F(-192 / 64); 00156 //pulseLen = EXP2I((256 - pulseWidthVal) << 6) * pulseLenFactor; 00157 } 00158 pulseLen *= waveLen; 00159 00160 float hLen = pulseLen - cosineLen; 00161 00162 // Ignore pulsewidths too high for given freq 00163 if (hLen < 0.0f) { 00164 hLen = 0.0f; 00165 } 00166 00167 // Ignore pulsewidths too high for given freq and cutoff 00168 float lLen = waveLen - hLen - 2 * cosineLen; 00169 if (lLen < 0.0f) { 00170 lLen = 0.0f; 00171 } 00172 00173 // Correct resAmp for cutoff in range 50..66 00174 if ((cutoffVal >= 128.0f) && (cutoffVal < 144.0f)) { 00175 resAmp *= sin(FLOAT_PI * (cutoffVal - 128.0f) / 32.0f); 00176 } 00177 00178 // Produce filtered square wave with 2 cosine waves on slopes 00179 00180 // 1st cosine segment 00181 if (relWavePos < cosineLen) { 00182 sample = -cos(FLOAT_PI * relWavePos / cosineLen); 00183 } else 00184 00185 // high linear segment 00186 if (relWavePos < (cosineLen + hLen)) { 00187 sample = 1.f; 00188 } else 00189 00190 // 2nd cosine segment 00191 if (relWavePos < (2 * cosineLen + hLen)) { 00192 sample = cos(FLOAT_PI * (relWavePos - (cosineLen + hLen)) / cosineLen); 00193 } else { 00194 00195 // low linear segment 00196 sample = -1.f; 00197 } 00198 00199 if (cutoffVal < 128.0f) { 00200 00201 // Attenuate samples below cutoff 50 00202 // Found by sample analysis 00203 sample *= EXP2F(-0.125f * (128.0f - cutoffVal)); 00204 } else { 00205 00206 // Add resonance sine. Effective for cutoff > 50 only 00207 float resSample = 1.0f; 00208 00209 // Resonance decay speed factor 00210 float resAmpDecayFactor = Tables::getInstance().resAmpDecayFactor[resonance >> 2]; 00211 00212 // Now relWavePos counts from the middle of first cosine 00213 relWavePos = wavePos; 00214 00215 // negative segments 00216 if (!(relWavePos < (cosineLen + hLen))) { 00217 resSample = -resSample; 00218 relWavePos -= cosineLen + hLen; 00219 00220 // From the digital captures, the decaying speed of the resonance sine is found a bit different for the positive and the negative segments 00221 resAmpDecayFactor += 0.25f; 00222 } 00223 00224 // Resonance sine WG 00225 resSample *= sin(FLOAT_PI * relWavePos / cosineLen); 00226 00227 // Resonance sine amp 00228 float resAmpFadeLog2 = -0.125f * resAmpDecayFactor * (relWavePos / cosineLen); // seems to be exact 00229 float resAmpFade = EXP2F(resAmpFadeLog2); 00230 00231 // Now relWavePos set negative to the left from center of any cosine 00232 relWavePos = wavePos; 00233 00234 // negative segment 00235 if (!(wavePos < (waveLen - 0.5f * cosineLen))) { 00236 relWavePos -= waveLen; 00237 } else 00238 00239 // positive segment 00240 if (!(wavePos < (hLen + 0.5f * cosineLen))) { 00241 relWavePos -= cosineLen + hLen; 00242 } 00243 00244 // To ensure the output wave has no breaks, two different windows are appied to the beginning and the ending of the resonance sine segment 00245 if (relWavePos < 0.5f * cosineLen) { 00246 float syncSine = sin(FLOAT_PI * relWavePos / cosineLen); 00247 if (relWavePos < 0.0f) { 00248 // The window is synchronous square sine here 00249 resAmpFade *= syncSine * syncSine; 00250 } else { 00251 // The window is synchronous sine here 00252 resAmpFade *= syncSine; 00253 } 00254 } 00255 00256 sample += resSample * resAmp * resAmpFade; 00257 } 00258 00259 // sawtooth waves 00260 if (sawtoothWaveform) { 00261 sample *= cos(FLOAT_2PI * wavePos / waveLen); 00262 } 00263 00264 wavePos++; 00265 00266 // wavePos isn't supposed to be > waveLen 00267 if (wavePos > waveLen) { 00268 wavePos -= waveLen; 00269 } 00270 } 00271 00272 // Multiply sample with current TVA value 00273 sample *= amp; 00274 return sample; 00275 } 00276 00277 void LA32WaveGenerator::deactivate() { 00278 active = false; 00279 } 00280 00281 bool LA32WaveGenerator::isActive() const { 00282 return active; 00283 } 00284 00285 bool LA32WaveGenerator::isPCMWave() const { 00286 return pcmWaveAddress != NULL; 00287 } 00288 00289 void LA32PartialPair::init(const bool ringModulated, const bool mixed) { 00290 this->ringModulated = ringModulated; 00291 this->mixed = mixed; 00292 masterOutputSample = 0.0f; 00293 slaveOutputSample = 0.0f; 00294 } 00295 00296 void LA32PartialPair::initSynth(const PairType useMaster, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) { 00297 if (useMaster == MASTER) { 00298 master.initSynth(sawtoothWaveform, pulseWidth, resonance); 00299 } else { 00300 slave.initSynth(sawtoothWaveform, pulseWidth, resonance); 00301 } 00302 } 00303 00304 void LA32PartialPair::initPCM(const PairType useMaster, const Bit16s *pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped) { 00305 if (useMaster == MASTER) { 00306 master.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, true); 00307 } else { 00308 slave.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, !ringModulated); 00309 } 00310 } 00311 00312 void LA32PartialPair::generateNextSample(const PairType useMaster, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff) { 00313 if (useMaster == MASTER) { 00314 masterOutputSample = master.generateNextSample(amp, pitch, cutoff); 00315 } else { 00316 slaveOutputSample = slave.generateNextSample(amp, pitch, cutoff); 00317 } 00318 } 00319 00320 Bit16s LA32PartialPair::nextOutSample() { 00321 float outputSample; 00322 if (ringModulated) { 00323 float ringModulatedSample = masterOutputSample * slaveOutputSample; 00324 outputSample = mixed ? masterOutputSample + ringModulatedSample : ringModulatedSample; 00325 } else { 00326 outputSample = masterOutputSample + slaveOutputSample; 00327 } 00328 return Bit16s(outputSample * 8192.0f); 00329 } 00330 00331 void LA32PartialPair::deactivate(const PairType useMaster) { 00332 if (useMaster == MASTER) { 00333 master.deactivate(); 00334 masterOutputSample = 0.0f; 00335 } else { 00336 slave.deactivate(); 00337 slaveOutputSample = 0.0f; 00338 } 00339 } 00340 00341 bool LA32PartialPair::isActive(const PairType useMaster) const { 00342 return useMaster == MASTER ? master.isActive() : slave.isActive(); 00343 } 00344 00345 } 00346 00347 #endif // #if MT32EMU_ACCURATE_WG == 1