DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/mt32/TVF.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 
00020 #include "mt32emu.h"
00021 #include "mmath.h"
00022 
00023 namespace MT32Emu {
00024 
00025 // Note that when entering nextPhase(), newPhase is set to phase + 1, and the descriptions/names below refer to
00026 // newPhase's value.
00027 enum {
00028         // When this is the target phase, level[0] is targeted within time[0]
00029         // Note that this phase is always set up in reset(), not nextPhase()
00030         PHASE_ATTACK = 1,
00031 
00032         // When this is the target phase, level[1] is targeted within time[1]
00033         PHASE_2 = 2,
00034 
00035         // When this is the target phase, level[2] is targeted within time[2]
00036         PHASE_3 = 3,
00037 
00038         // When this is the target phase, level[3] is targeted within time[3]
00039         PHASE_4 = 4,
00040 
00041         // When this is the target phase, immediately goes to PHASE_RELEASE unless the poly is set to sustain.
00042         // Otherwise level[3] is continued with increment 0 - no phase change will occur until some external influence (like pedal release)
00043         PHASE_SUSTAIN = 5,
00044 
00045         // 0 is targeted within time[4] (the time calculation is quite different from the other phases)
00046         PHASE_RELEASE = 6,
00047 
00048         // 0 is targeted with increment 0 (thus theoretically staying that way forever)
00049         PHASE_DONE = 7
00050 };
00051 
00052 static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u basePitch, unsigned int key) {
00053         // This table matches the values used by a real LAPC-I.
00054         static const Bit8s biasLevelToBiasMult[] = {85, 42, 21, 16, 10, 5, 2, 0, -2, -5, -10, -16, -21, -74, -85};
00055         // These values represent unique options with no consistent pattern, so we have to use something like a table in any case.
00056         // The table entries, when divided by 21, match approximately what the manual claims:
00057         // -1, -1/2, -1/4, 0, 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, 1, 5/4, 3/2, 2, s1, s2
00058         // Note that the entry for 1/8 is rounded to 2 (from 1/8 * 21 = 2.625), which seems strangely inaccurate compared to the others.
00059         static const Bit8s keyfollowMult21[] = {-21, -10, -5, 0, 2, 5, 8, 10, 13, 16, 18, 21, 26, 32, 42, 21, 21};
00060         int baseCutoff = keyfollowMult21[partialParam->tvf.keyfollow] - keyfollowMult21[partialParam->wg.pitchKeyfollow];
00061         // baseCutoff range now: -63 to 63
00062         baseCutoff *= (int)key - 60;
00063         // baseCutoff range now: -3024 to 3024
00064         int biasPoint = partialParam->tvf.biasPoint;
00065         if ((biasPoint & 0x40) == 0) {
00066                 // biasPoint range here: 0 to 63
00067                 int bias = biasPoint + 33 - key; // bias range here: -75 to 84
00068                 if (bias > 0) {
00069                         bias = -bias; // bias range here: -1 to -84
00070                         baseCutoff += bias * biasLevelToBiasMult[partialParam->tvf.biasLevel]; // Calculation range: -7140 to 7140
00071                         // baseCutoff range now: -10164 to 10164
00072                 }
00073         } else {
00074                 // biasPoint range here: 64 to 127
00075                 int bias = biasPoint - 31 - key; // bias range here: -75 to 84
00076                 if (bias < 0) {
00077                         baseCutoff += bias * biasLevelToBiasMult[partialParam->tvf.biasLevel]; // Calculation range: -6375 to 6375
00078                         // baseCutoff range now: -9399 to 9399
00079                 }
00080         }
00081         // baseCutoff range now: -10164 to 10164
00082         baseCutoff += ((partialParam->tvf.cutoff << 4) - 800);
00083         // baseCutoff range now: -10964 to 10964
00084         if (baseCutoff >= 0) {
00085                 // FIXME: Potentially bad if baseCutoff ends up below -2056?
00086                 int pitchDeltaThing = (basePitch >> 4) + baseCutoff - 3584;
00087                 if (pitchDeltaThing > 0) {
00088                         baseCutoff -= pitchDeltaThing;
00089                 }
00090         } else if (baseCutoff < -2048) {
00091                 baseCutoff = -2048;
00092         }
00093         baseCutoff += 2056;
00094         baseCutoff >>= 4; // PORTABILITY NOTE: Hmm... Depends whether it could've been below -2056, but maybe arithmetic shift assumed?
00095         if (baseCutoff > 255) {
00096                 baseCutoff = 255;
00097         }
00098         return (Bit8u)baseCutoff;
00099 }
00100 
00101 TVF::TVF(const Partial *usePartial, LA32Ramp *useCutoffModifierRamp) :
00102         partial(usePartial), cutoffModifierRamp(useCutoffModifierRamp) {
00103 
00104 
00105         // init ptr warnings (load state crashes)
00106         partialParam = NULL;
00107 }
00108 
00109 void TVF::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) {
00110         target = newTarget;
00111         phase = newPhase;
00112         cutoffModifierRamp->startRamp(newTarget, newIncrement);
00113 #if MT32EMU_MONITOR_TVF >= 1
00114         partial->getSynth()->printDebug("[+%lu] [Partial %d] TVF,ramp,%d,%d,%d,%d", partial->debugGetSampleNum(), partial->debugGetPartialNum(), newTarget, (newIncrement & 0x80) ? -1 : 1, (newIncrement & 0x7F), newPhase);
00115 #endif
00116 }
00117 
00118 void TVF::reset(const TimbreParam::PartialParam *newPartialParam, unsigned int basePitch) {
00119         partialParam = newPartialParam;
00120 
00121         unsigned int key = partial->getPoly()->getKey();
00122         unsigned int velocity = partial->getPoly()->getVelocity();
00123 
00124         const Tables *tables = &Tables::getInstance();
00125 
00126         baseCutoff = calcBaseCutoff(newPartialParam, basePitch, key);
00127 #if MT32EMU_MONITOR_TVF >= 1
00128         partial->getSynth()->printDebug("[+%lu] [Partial %d] TVF,base,%d", partial->debugGetSampleNum(), partial->debugGetPartialNum(), baseCutoff);
00129 #endif
00130 
00131         int newLevelMult = velocity * newPartialParam->tvf.envVeloSensitivity;
00132         newLevelMult >>= 6;
00133         newLevelMult += 109 - newPartialParam->tvf.envVeloSensitivity;
00134         newLevelMult += ((signed)key - 60) >> (4 - newPartialParam->tvf.envDepthKeyfollow);
00135         if (newLevelMult < 0) {
00136                 newLevelMult = 0;
00137         }
00138         newLevelMult *= newPartialParam->tvf.envDepth;
00139         newLevelMult >>= 6;
00140         if (newLevelMult > 255) {
00141                 newLevelMult = 255;
00142         }
00143         levelMult = newLevelMult;
00144 
00145         if (newPartialParam->tvf.envTimeKeyfollow != 0) {
00146                 keyTimeSubtraction = ((signed)key - 60) >> (5 - newPartialParam->tvf.envTimeKeyfollow);
00147         } else {
00148                 keyTimeSubtraction = 0;
00149         }
00150 
00151         int newTarget = (newLevelMult * newPartialParam->tvf.envLevel[0]) >> 8;
00152         int envTimeSetting = newPartialParam->tvf.envTime[0] - keyTimeSubtraction;
00153         int newIncrement;
00154         if (envTimeSetting <= 0) {
00155                 newIncrement = (0x80 | 127);
00156         } else {
00157                 newIncrement = tables->envLogarithmicTime[newTarget] - envTimeSetting;
00158                 if (newIncrement <= 0) {
00159                         newIncrement = 1;
00160                 }
00161         }
00162         cutoffModifierRamp->reset();
00163         startRamp(newTarget, newIncrement, PHASE_2 - 1);
00164 }
00165 
00166 Bit8u TVF::getBaseCutoff() const {
00167         return baseCutoff;
00168 }
00169 
00170 void TVF::handleInterrupt() {
00171         nextPhase();
00172 }
00173 
00174 void TVF::startDecay() {
00175         if (phase >= PHASE_RELEASE) {
00176                 return;
00177         }
00178         if (partialParam->tvf.envTime[4] == 0) {
00179                 startRamp(0, 1, PHASE_DONE - 1);
00180         } else {
00181                 startRamp(0, -partialParam->tvf.envTime[4], PHASE_DONE - 1);
00182         }
00183 }
00184 
00185 void TVF::nextPhase() {
00186         const Tables *tables = &Tables::getInstance();
00187         int newPhase = phase + 1;
00188 
00189         switch (newPhase) {
00190         case PHASE_DONE:
00191                 startRamp(0, 0, newPhase);
00192                 return;
00193         case PHASE_SUSTAIN:
00194         case PHASE_RELEASE:
00195                 // FIXME: Afaict newPhase should never be PHASE_RELEASE here. And if it were, this is an odd way to handle it.
00196                 if (!partial->getPoly()->canSustain()) {
00197                         phase = newPhase; // FIXME: Correct?
00198                         startDecay(); // FIXME: This should actually start decay even if phase is already 6. Does that matter?
00199                         return;
00200                 }
00201                 startRamp((levelMult * partialParam->tvf.envLevel[3]) >> 8, 0, newPhase);
00202                 return;
00203         }
00204 
00205         int envPointIndex = phase;
00206         int envTimeSetting = partialParam->tvf.envTime[envPointIndex] - keyTimeSubtraction;
00207 
00208         int newTarget = (levelMult * partialParam->tvf.envLevel[envPointIndex]) >> 8;
00209         int newIncrement;
00210         if (envTimeSetting > 0) {
00211                 int targetDelta = newTarget - target;
00212                 if (targetDelta == 0) {
00213                         if (newTarget == 0) {
00214                                 targetDelta = 1;
00215                                 newTarget = 1;
00216                         } else {
00217                                 targetDelta = -1;
00218                                 newTarget--;
00219                         }
00220                 }
00221                 newIncrement = tables->envLogarithmicTime[targetDelta < 0 ? -targetDelta : targetDelta] - envTimeSetting;
00222                 if (newIncrement <= 0) {
00223                         newIncrement = 1;
00224                 }
00225                 if (targetDelta < 0) {
00226                         newIncrement |= 0x80;
00227                 }
00228         } else {
00229                 newIncrement = newTarget >= target ? (0x80 | 127) : 127;
00230         }
00231         startRamp(newTarget, newIncrement, newPhase);
00232 }
00233 
00234 
00235 #ifdef WIN32_DEBUG
00236 void TVF::rawVerifyState( char *name, Synth *useSynth )
00237 {
00238         TVF *ptr1, *ptr2;
00239         TVF tvf_temp(partial,cutoffModifierRamp);
00240 
00241 
00242 #ifndef WIN32_DUMP
00243         return;
00244 #endif
00245 
00246         ptr1 = this;
00247         ptr2 = &tvf_temp;
00248         useSynth->rawLoadState( name, ptr2, sizeof(*this) );
00249 
00250 
00251         if( ptr1->partial != ptr2->partial ) __asm int 3
00252         if( ptr1->cutoffModifierRamp != ptr2->cutoffModifierRamp ) __asm int 3
00253         if( ptr1->partialParam != ptr2->partialParam ) __asm int 3
00254         if( ptr1->baseCutoff != ptr2->baseCutoff ) __asm int 3
00255         if( ptr1->keyTimeSubtraction != ptr2->keyTimeSubtraction ) __asm int 3
00256         if( ptr1->levelMult != ptr2->levelMult ) __asm int 3
00257         if( ptr1->target != ptr2->target ) __asm int 3
00258         if( ptr1->phase != ptr2->phase ) __asm int 3
00259 
00260 
00261 
00262         // avoid destructor problems
00263         memset( ptr2, 0, sizeof(*ptr2) );
00264 }
00265 #endif
00266 
00267 
00268 void TVF::saveState( std::ostream &stream )
00269 {
00270         Bit16u partialParam_idx1, partialParam_idx2;
00271 
00272         // - static fastptr
00273         //const Partial * const partial;
00274         //LA32Ramp *cutoffModifierRamp;
00275 
00276 
00277         // - reloc fastptr (!!)
00278         //const TimbreParam::PartialParam *partialParam;
00279         partial->getSynth()->findPartialParam( partialParam, &partialParam_idx1, &partialParam_idx2 );
00280 
00281         stream.write(reinterpret_cast<const char*>(&partialParam_idx1), sizeof(partialParam_idx1) );
00282         stream.write(reinterpret_cast<const char*>(&partialParam_idx2), sizeof(partialParam_idx2) );
00283 
00284 
00285         stream.write(reinterpret_cast<const char*>(&baseCutoff), sizeof(baseCutoff) );
00286         stream.write(reinterpret_cast<const char*>(&keyTimeSubtraction), sizeof(keyTimeSubtraction) );
00287         stream.write(reinterpret_cast<const char*>(&levelMult), sizeof(levelMult) );
00288         stream.write(reinterpret_cast<const char*>(&target), sizeof(target) );
00289         stream.write(reinterpret_cast<const char*>(&phase), sizeof(phase) );
00290 
00291 
00292 #ifdef WIN32_DEBUG
00293         // DEBUG
00294         partial->getSynth()->rawDumpState( "temp-save", this, sizeof(*this) );
00295         partial->getSynth()->rawDumpNo++;
00296 #endif
00297 }
00298 
00299 
00300 void TVF::loadState( std::istream &stream )
00301 {
00302         Bit16u partialParam_idx1, partialParam_idx2;
00303 
00304         // - static fastptr
00305         //const Partial * const partial;
00306         //LA32Ramp *cutoffModifierRamp;
00307 
00308 
00309         // - reloc fastptr (!!)
00310         //const TimbreParam::PartialParam *partialParam;
00311         stream.read(reinterpret_cast<char*>(&partialParam_idx1), sizeof(partialParam_idx1) );
00312         stream.read(reinterpret_cast<char*>(&partialParam_idx2), sizeof(partialParam_idx2) );
00313         partialParam = partial->getSynth()->indexPartialParam(partialParam_idx1, partialParam_idx2);
00314 
00315 
00316         stream.read(reinterpret_cast<char*>(&baseCutoff), sizeof(baseCutoff) );
00317         stream.read(reinterpret_cast<char*>(&keyTimeSubtraction), sizeof(keyTimeSubtraction) );
00318         stream.read(reinterpret_cast<char*>(&levelMult), sizeof(levelMult) );
00319         stream.read(reinterpret_cast<char*>(&target), sizeof(target) );
00320         stream.read(reinterpret_cast<char*>(&phase), sizeof(phase) );
00321 
00322 
00323 #ifdef WIN32_DEBUG
00324         // DEBUG
00325         partial->getSynth()->rawDumpState( "temp-load", this, sizeof(*this) );
00326         this->rawVerifyState( "temp-save", partial->getSynth() );
00327         partial->getSynth()->rawDumpNo++;
00328 #endif
00329 }
00330 
00331 }