DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/mt32/LegacyWaveGenerator.cpp
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