DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/mt32/TVA.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 /*
00019  * This class emulates the calculations performed by the 8095 microcontroller in order to configure the LA-32's amplitude ramp for a single partial at each stage of its TVA envelope.
00020  * Unless we introduced bugs, it should be pretty much 100% accurate according to Mok's specifications.
00021 */
00022 #include <cmath>
00023 
00024 #include "mt32emu.h"
00025 #include "mmath.h"
00026 #include "PartialManager.h"
00027 
00028 namespace MT32Emu {
00029 
00030 // CONFIRMED: Matches a table in ROM - haven't got around to coming up with a formula for it yet.
00031 static Bit8u biasLevelToAmpSubtractionCoeff[13] = {255, 187, 137, 100, 74, 54, 40, 29, 21, 15, 10, 5, 0};
00032 
00033 TVA::TVA(const Partial *usePartial, LA32Ramp *useAmpRamp) :
00034         partial(usePartial), ampRamp(useAmpRamp), system(&usePartial->getSynth()->mt32ram.system), phase(TVA_PHASE_DEAD) {
00035 
00036 
00037         // init ptr warnings (load state crashes)
00038         part = NULL;
00039         partialParam = NULL;
00040         patchTemp = NULL;
00041         rhythmTemp = NULL;
00042 }
00043 
00044 void TVA::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) {
00045         if (newPhase != phase) {
00046                 partial->getSynth()->partialStateChanged(partial, phase, newPhase);
00047         }
00048         target = newTarget;
00049         phase = newPhase;
00050         ampRamp->startRamp(newTarget, newIncrement);
00051 #if MT32EMU_MONITOR_TVA >= 1
00052         partial->getSynth()->printDebug("[+%lu] [Partial %d] TVA,ramp,%d,%d,%d,%d", partial->debugGetSampleNum(), partial->debugGetPartialNum(), (newIncrement & 0x80) ? -1 : 1, (newIncrement & 0x7F), newPhase);
00053 #endif
00054 }
00055 
00056 void TVA::end(int newPhase) {
00057         if (newPhase != phase) {
00058                 partial->getSynth()->partialStateChanged(partial, phase, newPhase);
00059         }
00060         phase = newPhase;
00061         playing = false;
00062 #if MT32EMU_MONITOR_TVA >= 1
00063         partial->getSynth()->printDebug("[+%lu] [Partial %d] TVA,end,%d", partial->debugGetSampleNum(), partial->debugGetPartialNum(), newPhase);
00064 #endif
00065 }
00066 
00067 static int multBias(Bit8u biasLevel, int bias) {
00068         return (bias * biasLevelToAmpSubtractionCoeff[biasLevel]) >> 5;
00069 }
00070 
00071 static int calcBiasAmpSubtraction(Bit8u biasPoint, Bit8u biasLevel, int key) {
00072         if ((biasPoint & 0x40) == 0) {
00073                 int bias = biasPoint + 33 - key;
00074                 if (bias > 0) {
00075                         return multBias(biasLevel, bias);
00076                 }
00077         } else {
00078                 int bias = biasPoint - 31 - key;
00079                 if (bias < 0) {
00080                         bias = -bias;
00081                         return multBias(biasLevel, bias);
00082                 }
00083         }
00084         return 0;
00085 }
00086 
00087 static int calcBiasAmpSubtractions(const TimbreParam::PartialParam *partialParam, int key) {
00088         int biasAmpSubtraction1 = calcBiasAmpSubtraction(partialParam->tva.biasPoint1, partialParam->tva.biasLevel1, key);
00089         if (biasAmpSubtraction1 > 255) {
00090                 return 255;
00091         }
00092         int biasAmpSubtraction2 = calcBiasAmpSubtraction(partialParam->tva.biasPoint2, partialParam->tva.biasLevel2, key);
00093         if (biasAmpSubtraction2 > 255) {
00094                 return 255;
00095         }
00096         int biasAmpSubtraction = biasAmpSubtraction1 + biasAmpSubtraction2;
00097         if (biasAmpSubtraction > 255) {
00098                 return 255;
00099         }
00100         return biasAmpSubtraction;
00101 }
00102 
00103 static int calcVeloAmpSubtraction(Bit8u veloSensitivity, unsigned int velocity) {
00104         // FIXME:KG: Better variable names
00105         int velocityMult = veloSensitivity - 50;
00106         int absVelocityMult = velocityMult < 0 ? -velocityMult : velocityMult;
00107         velocityMult = (signed)((unsigned)(velocityMult * ((signed)velocity - 64)) << 2);
00108         return absVelocityMult - (velocityMult >> 8); // PORTABILITY NOTE: Assumes arithmetic shift
00109 }
00110 
00111 static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemParams::System *system, const TimbreParam::PartialParam *partialParam, const MemParams::PatchTemp *patchTemp, const MemParams::RhythmTemp *rhythmTemp, int biasAmpSubtraction, int veloAmpSubtraction, Bit8u expression) {
00112         int amp = 155;
00113 
00114         if (!partial->isRingModulatingSlave()) {
00115                 amp -= tables->masterVolToAmpSubtraction[system->masterVol];
00116                 if (amp < 0) {
00117                         return 0;
00118                 }
00119                 amp -= tables->levelToAmpSubtraction[patchTemp->outputLevel];
00120                 if (amp < 0) {
00121                         return 0;
00122                 }
00123                 amp -= tables->levelToAmpSubtraction[expression];
00124                 if (amp < 0) {
00125                         return 0;
00126                 }
00127                 if (rhythmTemp != NULL) {
00128                         amp -= tables->levelToAmpSubtraction[rhythmTemp->outputLevel];
00129                         if (amp < 0) {
00130                                 return 0;
00131                         }
00132                 }
00133         }
00134         amp -= biasAmpSubtraction;
00135         if (amp < 0) {
00136                 return 0;
00137         }
00138         amp -= tables->levelToAmpSubtraction[partialParam->tva.level];
00139         if (amp < 0) {
00140                 return 0;
00141         }
00142         amp -= veloAmpSubtraction;
00143         if (amp < 0) {
00144                 return 0;
00145         }
00146         if (amp > 155) {
00147                 amp = 155;
00148         }
00149         amp -= partialParam->tvf.resonance >> 1;
00150         if (amp < 0) {
00151                 return 0;
00152         }
00153         return amp;
00154 }
00155 
00156 int calcKeyTimeSubtraction(Bit8u envTimeKeyfollow, int key) {
00157         if (envTimeKeyfollow == 0) {
00158                 return 0;
00159         }
00160         return (key - 60) >> (5 - envTimeKeyfollow); // PORTABILITY NOTE: Assumes arithmetic shift
00161 }
00162 
00163 void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartialParam, const MemParams::RhythmTemp *newRhythmTemp) {
00164         part = newPart;
00165         partialParam = newPartialParam;
00166         patchTemp = newPart->getPatchTemp();
00167         rhythmTemp = newRhythmTemp;
00168 
00169         playing = true;
00170 
00171         const Tables *tables = &Tables::getInstance();
00172 
00173         int key = partial->getPoly()->getKey();
00174         int velocity = partial->getPoly()->getVelocity();
00175 
00176         keyTimeSubtraction = calcKeyTimeSubtraction(partialParam->tva.envTimeKeyfollow, key);
00177 
00178         biasAmpSubtraction = calcBiasAmpSubtractions(partialParam, key);
00179         veloAmpSubtraction = calcVeloAmpSubtraction(partialParam->tva.veloSensitivity, velocity);
00180 
00181         int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, newRhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
00182         int newPhase;
00183         if (partialParam->tva.envTime[0] == 0) {
00184                 // Initially go to the TVA_PHASE_ATTACK target amp, and spend the next phase going from there to the TVA_PHASE_2 target amp
00185                 // Note that this means that velocity never affects time for this partial.
00186                 newTarget += partialParam->tva.envLevel[0];
00187                 newPhase = TVA_PHASE_ATTACK; // The first target used in nextPhase() will be TVA_PHASE_2
00188         } else {
00189                 // Initially go to the base amp determined by TVA level, part volume, etc., and spend the next phase going from there to the full TVA_PHASE_ATTACK target amp.
00190                 newPhase = TVA_PHASE_BASIC; // The first target used in nextPhase() will be TVA_PHASE_ATTACK
00191         }
00192 
00193         ampRamp->reset();//currentAmp = 0;
00194 
00195         // "Go downward as quickly as possible".
00196         // Since the current value is 0, the LA32Ramp will notice that we're already at or below the target and trying to go downward,
00197         // and therefore jump to the target immediately and raise an interrupt.
00198         startRamp((Bit8u)newTarget, 0x80 | 127, newPhase);
00199 }
00200 
00201 void TVA::startAbort() {
00202         startRamp(64, 0x80 | 127, TVA_PHASE_RELEASE);
00203 }
00204 
00205 void TVA::startDecay() {
00206         if (phase >= TVA_PHASE_RELEASE) {
00207                 return;
00208         }
00209         Bit8u newIncrement;
00210         if (partialParam->tva.envTime[4] == 0) {
00211                 newIncrement = 1;
00212         } else {
00213                 newIncrement = -partialParam->tva.envTime[4];
00214         }
00215         // The next time nextPhase() is called, it will think TVA_PHASE_RELEASE has finished and the partial will be aborted
00216         startRamp(0, newIncrement, TVA_PHASE_RELEASE);
00217 }
00218 
00219 void TVA::handleInterrupt() {
00220         nextPhase();
00221 }
00222 
00223 void TVA::recalcSustain() {
00224         // We get pinged periodically by the pitch code to recalculate our values when in sustain.
00225         // This is done so that the TVA will respond to things like MIDI expression and volume changes while it's sustaining, which it otherwise wouldn't do.
00226 
00227         // The check for envLevel[3] == 0 strikes me as slightly dumb. FIXME: Explain why
00228         if (phase != TVA_PHASE_SUSTAIN || partialParam->tva.envLevel[3] == 0) {
00229                 return;
00230         }
00231         // We're sustaining. Recalculate all the values
00232         const Tables *tables = &Tables::getInstance();
00233         int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
00234         newTarget += partialParam->tva.envLevel[3];
00235         // Since we're in TVA_PHASE_SUSTAIN at this point, we know that target has been reached and an interrupt fired, so we can rely on it being the current amp.
00236         int targetDelta = newTarget - target;
00237 
00238         // Calculate an increment to get to the new amp value in a short, more or less consistent amount of time
00239         Bit8u newIncrement;
00240         if (targetDelta >= 0) {
00241                 newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - 2;
00242         } else {
00243                 newIncrement = (tables->envLogarithmicTime[(Bit8u)-targetDelta] - 2) | 0x80;
00244         }
00245         // Configure so that once the transition's complete and nextPhase() is called, we'll just re-enter sustain phase (or decay phase, depending on parameters at the time).
00246         startRamp(newTarget, newIncrement, TVA_PHASE_SUSTAIN - 1);
00247 }
00248 
00249 bool TVA::isPlaying() const {
00250         return playing;
00251 }
00252 
00253 int TVA::getPhase() const {
00254         return phase;
00255 }
00256 
00257 void TVA::nextPhase() {
00258         const Tables *tables = &Tables::getInstance();
00259 
00260         if (phase >= TVA_PHASE_DEAD || !playing) {
00261                 partial->getSynth()->printDebug("TVA::nextPhase(): Shouldn't have got here with phase %d, playing=%s", phase, playing ? "true" : "false");
00262                 return;
00263         }
00264         int newPhase = phase + 1;
00265 
00266         if (newPhase == TVA_PHASE_DEAD) {
00267                 end(newPhase);
00268                 return;
00269         }
00270 
00271         bool allLevelsZeroFromNowOn = false;
00272         if (partialParam->tva.envLevel[3] == 0) {
00273                 if (newPhase == TVA_PHASE_4) {
00274                         allLevelsZeroFromNowOn = true;
00275                 } else if (partialParam->tva.envLevel[2] == 0) {
00276                         if (newPhase == TVA_PHASE_3) {
00277                                 allLevelsZeroFromNowOn = true;
00278                         } else if (partialParam->tva.envLevel[1] == 0) {
00279                                 if (newPhase == TVA_PHASE_2) {
00280                                         allLevelsZeroFromNowOn = true;
00281                                 } else if (partialParam->tva.envLevel[0] == 0) {
00282                                         if (newPhase == TVA_PHASE_ATTACK)  { // this line added, missing in ROM - FIXME: Add description of repercussions
00283                                                 allLevelsZeroFromNowOn = true;
00284                                         }
00285                                 }
00286                         }
00287                 }
00288         }
00289 
00290         int newTarget;
00291         int newIncrement = 0; // Initialised to please compilers
00292         int envPointIndex = phase;
00293 
00294         if (!allLevelsZeroFromNowOn) {
00295                 newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
00296 
00297                 if (newPhase == TVA_PHASE_SUSTAIN || newPhase == TVA_PHASE_RELEASE) {
00298                         if (partialParam->tva.envLevel[3] == 0) {
00299                                 end(newPhase);
00300                                 return;
00301                         }
00302                         if (!partial->getPoly()->canSustain()) {
00303                                 newPhase = TVA_PHASE_RELEASE;
00304                                 newTarget = 0;
00305                                 newIncrement = -partialParam->tva.envTime[4];
00306                                 if (newIncrement == 0) {
00307                                         // We can't let the increment be 0, or there would be no emulated interrupt.
00308                                         // So we do an "upward" increment, which should set the amp to 0 extremely quickly
00309                                         // and cause an "interrupt" to bring us back to nextPhase().
00310                                         newIncrement = 1;
00311                                 }
00312                         } else {
00313                                 newTarget += partialParam->tva.envLevel[3];
00314                                 newIncrement = 0;
00315                         }
00316                 } else {
00317                         newTarget += partialParam->tva.envLevel[envPointIndex];
00318                 }
00319         } else {
00320                 newTarget = 0;
00321         }
00322 
00323         if ((newPhase != TVA_PHASE_SUSTAIN && newPhase != TVA_PHASE_RELEASE) || allLevelsZeroFromNowOn) {
00324                 int envTimeSetting = partialParam->tva.envTime[envPointIndex];
00325 
00326                 if (newPhase == TVA_PHASE_ATTACK) {
00327                         envTimeSetting -= ((signed)partial->getPoly()->getVelocity() - 64) >> (6 - partialParam->tva.envTimeVeloSensitivity); // PORTABILITY NOTE: Assumes arithmetic shift
00328 
00329                         if (envTimeSetting <= 0 && partialParam->tva.envTime[envPointIndex] != 0) {
00330                                 envTimeSetting = 1;
00331                         }
00332                 } else {
00333                         envTimeSetting -= keyTimeSubtraction;
00334                 }
00335                 if (envTimeSetting > 0) {
00336                         int targetDelta = newTarget - target;
00337                         if (targetDelta <= 0) {
00338                                 if (targetDelta == 0) {
00339                                         // target and newTarget are the same.
00340                                         // We can't have an increment of 0 or we wouldn't get an emulated interrupt.
00341                                         // So instead make the target one less than it really should be and set targetDelta accordingly.
00342                                         targetDelta = -1;
00343                                         newTarget--;
00344                                         if (newTarget < 0) {
00345                                                 // Oops, newTarget is less than zero now, so let's do it the other way:
00346                                                 // Make newTarget one more than it really should've been and set targetDelta accordingly.
00347                                                 // FIXME (apparent bug in real firmware):
00348                                                 // This means targetDelta will be positive just below here where it's inverted, and we'll end up using envLogarithmicTime[-1], and we'll be setting newIncrement to be descending later on, etc..
00349                                                 targetDelta = 1;
00350                                                 newTarget = -newTarget;
00351                                         }
00352                                 }
00353                                 targetDelta = -targetDelta;
00354                                 newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - envTimeSetting;
00355                                 if (newIncrement <= 0) {
00356                                         newIncrement = 1;
00357                                 }
00358                                 newIncrement = newIncrement | 0x80;
00359                         } else {
00360                                 // FIXME: The last 22 or so entries in this table are 128 - surely that fucks things up, since that ends up being -128 signed?
00361                                 newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - envTimeSetting;
00362                                 if (newIncrement <= 0) {
00363                                         newIncrement = 1;
00364                                 }
00365                         }
00366                 } else {
00367                         newIncrement = newTarget >= target ? (0x80 | 127) : 127;
00368                 }
00369 
00370                 // FIXME: What's the point of this? It's checked or set to non-zero everywhere above
00371                 if (newIncrement == 0) {
00372                         newIncrement = 1;
00373                 }
00374         }
00375 
00376         startRamp((Bit8u)newTarget, (Bit8u)newIncrement, newPhase);
00377 }
00378 
00379 
00380 #ifdef WIN32_DEBUG
00381 void TVA::rawVerifyState( char *name, Synth *useSynth )
00382 {
00383         TVA *ptr1, *ptr2;
00384         TVA tva_temp(partial,ampRamp);
00385 
00386 
00387 #ifndef WIN32_DUMP
00388         return;
00389 #endif
00390 
00391         ptr1 = this;
00392         ptr2 = &tva_temp;
00393         useSynth->rawLoadState( name, ptr2, sizeof(*this) );
00394 
00395 
00396         if( ptr1->partial != ptr2->partial ) __asm int 3
00397         if( ptr1->ampRamp != ptr2->ampRamp ) __asm int 3
00398         if( ptr1->system != ptr2->system ) __asm int 3
00399         if( ptr1->part != ptr2->part ) __asm int 3
00400         if( ptr1->partialParam != ptr2->partialParam ) __asm int 3
00401         if( ptr1->patchTemp != ptr2->patchTemp ) __asm int 3
00402         if( ptr1->rhythmTemp != ptr2->rhythmTemp ) __asm int 3
00403         if( ptr1->playing != ptr2->playing ) __asm int 3
00404         if( ptr1->biasAmpSubtraction != ptr2->biasAmpSubtraction ) __asm int 3
00405         if( ptr1->veloAmpSubtraction != ptr2->veloAmpSubtraction ) __asm int 3
00406         if( ptr1->keyTimeSubtraction != ptr2->keyTimeSubtraction ) __asm int 3
00407         if( ptr1->target != ptr2->target ) __asm int 3
00408         if( ptr1->phase != ptr2->phase ) __asm int 3
00409 
00410 
00411 
00412         // avoid destructor problems
00413         memset( ptr2, 0, sizeof(*ptr2) );
00414 }
00415 #endif
00416 
00417 
00418 void TVA::saveState( std::ostream &stream )
00419 {
00420         Bit16u partialParam_idx1, partialParam_idx2;
00421         Bit8u part_idx;
00422         Bit8u patchTemp_idx;
00423         Bit8u rhythmTemp_idx;
00424 
00425 
00426         // - static fastptr
00427         //const Partial * const partial;
00428         //LA32Ramp *ampRamp;
00429         //const MemParams::System * const system;
00430 
00431 
00432         // - reloc fastptr (!!)
00433         //const Part *part;
00434         partial->getSynth()->findPart( part, &part_idx );
00435         stream.write(reinterpret_cast<const char*>(&part_idx), sizeof(part_idx) );
00436 
00437 
00438         // - reloc fastptr (!!)
00439         //const TimbreParam::PartialParam *partialParam;
00440         partial->getSynth()->findPartialParam( partialParam, &partialParam_idx1, &partialParam_idx2 );
00441         stream.write(reinterpret_cast<const char*>(&partialParam_idx1), sizeof(partialParam_idx1) );
00442         stream.write(reinterpret_cast<const char*>(&partialParam_idx2), sizeof(partialParam_idx2) );
00443 
00444 
00445         // - reloc fastptr (!!)
00446         //const MemParams::PatchTemp *patchTemp;
00447         partial->getSynth()->findPatchTemp( patchTemp, &patchTemp_idx );
00448         stream.write(reinterpret_cast<const char*>(&patchTemp_idx), sizeof(patchTemp_idx) );
00449 
00450 
00451         // - reloc fastptr (!!)
00452         //const MemParams::RhythmTemp *rhythmTemp;
00453         partial->getSynth()->findRhythmTemp( rhythmTemp, &rhythmTemp_idx );
00454         stream.write(reinterpret_cast<const char*>(&rhythmTemp_idx), sizeof(rhythmTemp_idx) );
00455 
00456 
00457         stream.write(reinterpret_cast<const char*>(&playing), sizeof(playing) );
00458         stream.write(reinterpret_cast<const char*>(&biasAmpSubtraction), sizeof(biasAmpSubtraction) );
00459         stream.write(reinterpret_cast<const char*>(&veloAmpSubtraction), sizeof(veloAmpSubtraction) );
00460         stream.write(reinterpret_cast<const char*>(&keyTimeSubtraction), sizeof(keyTimeSubtraction) );
00461         stream.write(reinterpret_cast<const char*>(&target), sizeof(target) );
00462         stream.write(reinterpret_cast<const char*>(&phase), sizeof(phase) );
00463 
00464 
00465 #ifdef WIN32_DEBUG
00466         // DEBUG
00467         partial->getSynth()->rawDumpState( "temp-save", this, sizeof(*this) );
00468         partial->getSynth()->rawDumpNo++;
00469 #endif
00470 }
00471 
00472 
00473 void TVA::loadState( std::istream &stream )
00474 {
00475         Bit16u partialParam_idx1, partialParam_idx2;
00476         Bit8u part_idx;
00477         Bit8u patchTemp_idx;
00478         Bit8u rhythmTemp_idx;
00479 
00480 
00481         // - static fastptr
00482         //const Partial * const partial;
00483         //LA32Ramp *ampRamp;
00484         //const MemParams::System * const system;
00485 
00486 
00487         // - reloc fastptr (!!)
00488         //const Part *part;
00489         stream.read(reinterpret_cast<char*>(&part_idx), sizeof(part_idx) );
00490         part = partial->getSynth()->indexPart(part_idx);
00491 
00492 
00493         // - reloc fastptr (!!)
00494         //const TimbreParam::PartialParam *partialParam;
00495         stream.read(reinterpret_cast<char*>(&partialParam_idx1), sizeof(partialParam_idx1) );
00496         stream.read(reinterpret_cast<char*>(&partialParam_idx2), sizeof(partialParam_idx2) );
00497         partialParam = partial->getSynth()->indexPartialParam(partialParam_idx1, partialParam_idx2);
00498 
00499 
00500         // - reloc fastptr (!!)
00501         //const MemParams::PatchTemp *patchTemp;
00502         stream.read(reinterpret_cast<char*>(&patchTemp_idx), sizeof(patchTemp_idx) );
00503         patchTemp = partial->getSynth()->indexPatchTemp(patchTemp_idx);
00504 
00505 
00506         // - reloc fastptr (!!)
00507         //const MemParams::RhythmTemp *rhythmTemp;
00508         stream.read(reinterpret_cast<char*>(&rhythmTemp_idx), sizeof(rhythmTemp_idx) );
00509         rhythmTemp = partial->getSynth()->indexRhythmTemp(rhythmTemp_idx);
00510 
00511 
00512         stream.read(reinterpret_cast<char*>(&playing), sizeof(playing) );
00513         stream.read(reinterpret_cast<char*>(&biasAmpSubtraction), sizeof(biasAmpSubtraction) );
00514         stream.read(reinterpret_cast<char*>(&veloAmpSubtraction), sizeof(veloAmpSubtraction) );
00515         stream.read(reinterpret_cast<char*>(&keyTimeSubtraction), sizeof(keyTimeSubtraction) );
00516         stream.read(reinterpret_cast<char*>(&target), sizeof(target) );
00517         stream.read(reinterpret_cast<char*>(&phase), sizeof(phase) );
00518 
00519 
00520 #ifdef WIN32_DEBUG
00521         // DEBUG
00522         partial->getSynth()->rawDumpState( "temp-load", this, sizeof(*this) );
00523         this->rawVerifyState( "temp-save", partial->getSynth() );
00524         partial->getSynth()->rawDumpNo++;
00525 #endif
00526 }
00527 
00528 }