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 <cstdlib> 00020 00021 #include "mt32emu.h" 00022 00023 namespace MT32Emu { 00024 00025 // FIXME: Add Explanation 00026 static Bit16u lowerDurationToDivisor[] = {34078, 37162, 40526, 44194, 48194, 52556, 57312, 62499}; 00027 00028 // These values represent unique options with no consistent pattern, so we have to use something like a table in any case. 00029 // The table matches exactly what the manual claims (when divided by 8192): 00030 // -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 00031 // ...except for the last two entries, which are supposed to be "1 cent above 1" and "2 cents above 1", respectively. They can only be roughly approximated with this integer math. 00032 static Bit16s pitchKeyfollowMult[] = {-8192, -4096, -2048, 0, 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192, 10240, 12288, 16384, 8198, 8226}; 00033 00034 // Note: Keys < 60 use keyToPitchTable[60 - key], keys >= 60 use keyToPitchTable[key - 60]. 00035 // FIXME: This table could really be shorter, since we never use e.g. key 127. 00036 static Bit16u keyToPitchTable[] = { 00037 0, 341, 683, 1024, 1365, 1707, 2048, 2389, 00038 2731, 3072, 3413, 3755, 4096, 4437, 4779, 5120, 00039 5461, 5803, 6144, 6485, 6827, 7168, 7509, 7851, 00040 8192, 8533, 8875, 9216, 9557, 9899, 10240, 10581, 00041 10923, 11264, 11605, 11947, 12288, 12629, 12971, 13312, 00042 13653, 13995, 14336, 14677, 15019, 15360, 15701, 16043, 00043 16384, 16725, 17067, 17408, 17749, 18091, 18432, 18773, 00044 19115, 19456, 19797, 20139, 20480, 20821, 21163, 21504, 00045 21845, 22187, 22528, 22869 00046 }; 00047 00048 TVP::TVP(const Partial *usePartial) : 00049 partial(usePartial), system(&usePartial->getSynth()->mt32ram.system) { 00050 // We want to do processing 4000 times per second. FIXME: This is pretty arbitrary. 00051 maxCounter = SAMPLE_RATE / 4000; 00052 // The timer runs at 500kHz. We only need to bother updating it every maxCounter samples, before we do processing. 00053 // This is how much to increment it by every maxCounter samples. 00054 processTimerIncrement = 500000 * maxCounter / SAMPLE_RATE; 00055 00056 00057 // init ptr warnings (load state crashes) 00058 part = NULL; 00059 partialParam = NULL; 00060 patchTemp = NULL; 00061 } 00062 00063 static Bit16s keyToPitch(unsigned int key) { 00064 // We're using a table to do: return round_to_nearest_or_even((key - 60) * (4096.0 / 12.0)) 00065 // Banker's rounding is just slightly annoying to do in C++ 00066 int k = (int)key; 00067 Bit16s pitch = keyToPitchTable[abs(k - 60)]; 00068 return key < 60 ? -pitch : pitch; 00069 } 00070 00071 static inline Bit32s coarseToPitch(Bit8u coarse) { 00072 return (coarse - 36) * 4096 / 12; // One semitone per coarse offset 00073 } 00074 00075 static inline Bit32s fineToPitch(Bit8u fine) { 00076 return (fine - 50) * 4096 / 1200; // One cent per fine offset 00077 } 00078 00079 static Bit32u calcBasePitch(const Partial *partial, const TimbreParam::PartialParam *partialParam, const MemParams::PatchTemp *patchTemp, unsigned int key) { 00080 Bit32s basePitch = keyToPitch(key); 00081 basePitch = (basePitch * pitchKeyfollowMult[partialParam->wg.pitchKeyfollow]) >> 13; // PORTABILITY NOTE: Assumes arithmetic shift 00082 basePitch += coarseToPitch(partialParam->wg.pitchCoarse); 00083 basePitch += fineToPitch(partialParam->wg.pitchFine); 00084 // NOTE:Mok: This is done on MT-32, but not LAPC-I: 00085 //pitch += coarseToPitch(patchTemp->patch.keyShift + 12); 00086 basePitch += fineToPitch(patchTemp->patch.fineTune); 00087 00088 const ControlROMPCMStruct *controlROMPCMStruct = partial->getControlROMPCMStruct(); 00089 if (controlROMPCMStruct != NULL) { 00090 basePitch += (Bit32s)((((Bit32s)controlROMPCMStruct->pitchMSB) << 8) | (Bit32s)controlROMPCMStruct->pitchLSB); 00091 } else { 00092 if ((partialParam->wg.waveform & 1) == 0) { 00093 basePitch += 37133; // This puts Middle C at around 261.64Hz (assuming no other modifications, masterTune of 64, etc.) 00094 } else { 00095 // Sawtooth waves are effectively double the frequency of square waves. 00096 // Thus we add 4096 less than for square waves here, which results in halving the frequency. 00097 basePitch += 33037; 00098 } 00099 } 00100 if (basePitch < 0) { 00101 basePitch = 0; 00102 } 00103 if (basePitch > 59392) { 00104 basePitch = 59392; 00105 } 00106 return (Bit32u)basePitch; 00107 } 00108 00109 static Bit32u calcVeloMult(Bit8u veloSensitivity, unsigned int velocity) { 00110 if (veloSensitivity == 0 || veloSensitivity > 3) { 00111 // Note that on CM-32L/LAPC-I veloSensitivity is never > 3, since it's clipped to 3 by the max tables. 00112 return 21845; // aka floor(4096 / 12 * 64), aka ~64 semitones 00113 } 00114 // When velocity is 127, the multiplier is 21845, aka ~64 semitones (regardless of veloSensitivity). 00115 // The lower the velocity, the lower the multiplier. The veloSensitivity determines the amount decreased per velocity value. 00116 // The minimum multiplier (with velocity 0, veloSensitivity 3) is 170 (~half a semitone). 00117 Bit32u veloMult = 32768; 00118 veloMult -= (127 - velocity) << (5 + veloSensitivity); 00119 veloMult *= 21845; 00120 veloMult >>= 15; 00121 return veloMult; 00122 } 00123 00124 static Bit32s calcTargetPitchOffsetWithoutLFO(const TimbreParam::PartialParam *partialParam, int levelIndex, unsigned int velocity) { 00125 int veloMult = calcVeloMult(partialParam->pitchEnv.veloSensitivity, velocity); 00126 int targetPitchOffsetWithoutLFO = partialParam->pitchEnv.level[levelIndex] - 50; 00127 targetPitchOffsetWithoutLFO = (Bit32s)(targetPitchOffsetWithoutLFO * veloMult) >> (16 - partialParam->pitchEnv.depth); // PORTABILITY NOTE: Assumes arithmetic shift 00128 return targetPitchOffsetWithoutLFO; 00129 } 00130 00131 void TVP::reset(const Part *usePart, const TimbreParam::PartialParam *usePartialParam) { 00132 part = usePart; 00133 partialParam = usePartialParam; 00134 patchTemp = part->getPatchTemp(); 00135 00136 unsigned int key = partial->getPoly()->getKey(); 00137 unsigned int velocity = partial->getPoly()->getVelocity(); 00138 00139 // FIXME: We're using a per-TVP timer instead of a system-wide one for convenience. 00140 timeElapsed = 0; 00141 00142 basePitch = calcBasePitch(partial, partialParam, patchTemp, key); 00143 currentPitchOffset = calcTargetPitchOffsetWithoutLFO(partialParam, 0, velocity); 00144 targetPitchOffsetWithoutLFO = currentPitchOffset; 00145 phase = 0; 00146 00147 if (partialParam->pitchEnv.timeKeyfollow) { 00148 timeKeyfollowSubtraction = (key - 60) >> (5 - partialParam->pitchEnv.timeKeyfollow); // PORTABILITY NOTE: Assumes arithmetic shift 00149 } else { 00150 timeKeyfollowSubtraction = 0; 00151 } 00152 lfoPitchOffset = 0; 00153 counter = 0; 00154 pitch = basePitch; 00155 00156 // These don't really need to be initialised, but it aids debugging. 00157 pitchOffsetChangePerBigTick = 0; 00158 targetPitchOffsetReachedBigTick = 0; 00159 shifts = 0; 00160 } 00161 00162 Bit32u TVP::getBasePitch() const { 00163 return basePitch; 00164 } 00165 00166 void TVP::updatePitch() { 00167 Bit32s newPitch = basePitch + currentPitchOffset; 00168 if (!partial->isPCM() || (partial->getControlROMPCMStruct()->len & 0x01) == 0) { // FIXME: Use !partial->pcmWaveEntry->unaffectedByMasterTune instead 00169 // FIXME: masterTune recalculation doesn't really happen here, and there are various bugs not yet emulated 00170 // 171 is ~half a semitone. 00171 newPitch += ((system->masterTune - 64) * 171) >> 6; // PORTABILITY NOTE: Assumes arithmetic shift. 00172 } 00173 if ((partialParam->wg.pitchBenderEnabled & 1) != 0) { 00174 newPitch += part->getPitchBend(); 00175 } 00176 if (newPitch < 0) { 00177 newPitch = 0; 00178 } 00179 00180 // Note: Temporary #ifdef until we have proper "quirk" configuration 00181 // This is about right emulation of MT-32 GEN0 quirk exploited in Colonel's Bequest timbre "Lightning" 00182 #ifndef MT32EMU_QUIRK_PITCH_ENVELOPE_OVERFLOW_MT32 00183 if (newPitch > 59392) { 00184 newPitch = 59392; 00185 } 00186 #endif 00187 pitch = (Bit16u)newPitch; 00188 00189 // FIXME: We're doing this here because that's what the CM-32L does - we should probably move this somewhere more appropriate in future. 00190 partial->tva->recalcSustain(); 00191 } 00192 00193 void TVP::targetPitchOffsetReached() { 00194 currentPitchOffset = targetPitchOffsetWithoutLFO + lfoPitchOffset; 00195 00196 switch (phase) { 00197 case 3: 00198 case 4: 00199 { 00200 int newLFOPitchOffset = (part->getModulation() * partialParam->pitchLFO.modSensitivity) >> 7; 00201 newLFOPitchOffset = (newLFOPitchOffset + partialParam->pitchLFO.depth) << 1; 00202 if (pitchOffsetChangePerBigTick > 0) { 00203 // Go in the opposite direction to last time 00204 newLFOPitchOffset = -newLFOPitchOffset; 00205 } 00206 lfoPitchOffset = newLFOPitchOffset; 00207 int targetPitchOffset = targetPitchOffsetWithoutLFO + lfoPitchOffset; 00208 setupPitchChange(targetPitchOffset, 101 - partialParam->pitchLFO.rate); 00209 updatePitch(); 00210 break; 00211 } 00212 case 6: 00213 updatePitch(); 00214 break; 00215 default: 00216 nextPhase(); 00217 } 00218 } 00219 00220 void TVP::nextPhase() { 00221 phase++; 00222 int envIndex = phase == 6 ? 4 : phase; 00223 00224 targetPitchOffsetWithoutLFO = calcTargetPitchOffsetWithoutLFO(partialParam, envIndex, partial->getPoly()->getVelocity()); // pitch we'll reach at the end 00225 00226 int changeDuration = partialParam->pitchEnv.time[envIndex - 1]; 00227 changeDuration -= timeKeyfollowSubtraction; 00228 if (changeDuration > 0) { 00229 setupPitchChange(targetPitchOffsetWithoutLFO, changeDuration); // changeDuration between 0 and 112 now 00230 updatePitch(); 00231 } else { 00232 targetPitchOffsetReached(); 00233 } 00234 } 00235 00236 // Shifts val to the left until bit 31 is 1 and returns the number of shifts 00237 static Bit8u normalise(Bit32u &val) { 00238 Bit8u leftShifts; 00239 for (leftShifts = 0; leftShifts < 31; leftShifts++) { 00240 if ((val & 0x80000000) != 0) { 00241 break; 00242 } 00243 val = val << 1; 00244 } 00245 return leftShifts; 00246 } 00247 00248 void TVP::setupPitchChange(int targetPitchOffset, Bit8u changeDuration) { 00249 bool negativeDelta = targetPitchOffset < currentPitchOffset; 00250 Bit32s pitchOffsetDelta = targetPitchOffset - currentPitchOffset; 00251 if (pitchOffsetDelta > 32767 || pitchOffsetDelta < -32768) { 00252 pitchOffsetDelta = 32767; 00253 } 00254 if (negativeDelta) { 00255 pitchOffsetDelta = -pitchOffsetDelta; 00256 } 00257 // We want to maximise the number of bits of the Bit16s "pitchOffsetChangePerBigTick" we use in order to get the best possible precision later 00258 Bit32u absPitchOffsetDelta = pitchOffsetDelta << 16; 00259 Bit8u normalisationShifts = normalise(absPitchOffsetDelta); // FIXME: Double-check: normalisationShifts is usually between 0 and 15 here, unless the delta is 0, in which case it's 31 00260 absPitchOffsetDelta = absPitchOffsetDelta >> 1; // Make room for the sign bit 00261 00262 changeDuration--; // changeDuration's now between 0 and 111 00263 unsigned int upperDuration = changeDuration >> 3; // upperDuration's now between 0 and 13 00264 shifts = normalisationShifts + upperDuration + 2; 00265 Bit16u divisor = lowerDurationToDivisor[changeDuration & 7]; 00266 Bit16s newPitchOffsetChangePerBigTick = ((absPitchOffsetDelta & 0xFFFF0000) / divisor) >> 1; // Result now fits within 15 bits. FIXME: Check nothing's getting sign-extended incorrectly 00267 if (negativeDelta) { 00268 newPitchOffsetChangePerBigTick = -newPitchOffsetChangePerBigTick; 00269 } 00270 pitchOffsetChangePerBigTick = newPitchOffsetChangePerBigTick; 00271 00272 int currentBigTick = timeElapsed >> 8; 00273 int durationInBigTicks = divisor >> (12 - upperDuration); 00274 if (durationInBigTicks > 32767) { 00275 durationInBigTicks = 32767; 00276 } 00277 // The result of the addition may exceed 16 bits, but wrapping is fine and intended here. 00278 targetPitchOffsetReachedBigTick = currentBigTick + durationInBigTicks; 00279 } 00280 00281 void TVP::startDecay() { 00282 phase = 5; 00283 lfoPitchOffset = 0; 00284 targetPitchOffsetReachedBigTick = timeElapsed >> 8; // FIXME: Afaict there's no good reason for this - check 00285 } 00286 00287 Bit16u TVP::nextPitch() { 00288 // FIXME: Write explanation of counter and time increment 00289 if (counter == 0) { 00290 timeElapsed += processTimerIncrement; 00291 timeElapsed = timeElapsed & 0x00FFFFFF; 00292 process(); 00293 } 00294 counter = (counter + 1) % maxCounter; 00295 return pitch; 00296 } 00297 00298 void TVP::process() { 00299 if (phase == 0) { 00300 targetPitchOffsetReached(); 00301 return; 00302 } 00303 if (phase == 5) { 00304 nextPhase(); 00305 return; 00306 } 00307 if (phase > 7) { 00308 updatePitch(); 00309 return; 00310 } 00311 00312 Bit16s negativeBigTicksRemaining = (timeElapsed >> 8) - targetPitchOffsetReachedBigTick; 00313 if (negativeBigTicksRemaining >= 0) { 00314 // We've reached the time for a phase change 00315 targetPitchOffsetReached(); 00316 return; 00317 } 00318 // FIXME: Write explanation for this stuff 00319 int rightShifts = shifts; 00320 if (rightShifts > 13) { 00321 rightShifts -= 13; 00322 negativeBigTicksRemaining = negativeBigTicksRemaining >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift 00323 rightShifts = 13; 00324 } 00325 int newResult = ((Bit32s)(negativeBigTicksRemaining * pitchOffsetChangePerBigTick)) >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift 00326 newResult += targetPitchOffsetWithoutLFO + lfoPitchOffset; 00327 currentPitchOffset = newResult; 00328 updatePitch(); 00329 } 00330 00331 00332 #ifdef WIN32_DEBUG 00333 void TVP::rawVerifyState( char *name, Synth *useSynth ) 00334 { 00335 TVP *ptr1, *ptr2; 00336 TVP tvp_temp(partial); 00337 00338 00339 #ifndef WIN32_DUMP 00340 return; 00341 #endif 00342 00343 ptr1 = this; 00344 ptr2 = &tvp_temp; 00345 useSynth->rawLoadState( name, ptr2, sizeof(*this) ); 00346 00347 00348 if( ptr1->partial != ptr2->partial ) __asm int 3 00349 if( ptr1->system != ptr2->system ) __asm int 3 00350 if( ptr1->part != ptr2->part ) __asm int 3 00351 if( ptr1->partialParam != ptr2->partialParam ) __asm int 3 00352 if( ptr1->patchTemp != ptr2->patchTemp ) __asm int 3 00353 if( ptr1->maxCounter != ptr2->maxCounter ) __asm int 3 00354 if( ptr1->processTimerIncrement != ptr2->processTimerIncrement ) __asm int 3 00355 if( ptr1->counter != ptr2->counter ) __asm int 3 00356 if( ptr1->timeElapsed != ptr2->timeElapsed ) __asm int 3 00357 if( ptr1->phase != ptr2->phase ) __asm int 3 00358 if( ptr1->basePitch != ptr2->basePitch ) __asm int 3 00359 if( ptr1->targetPitchOffsetWithoutLFO != ptr2->targetPitchOffsetWithoutLFO ) __asm int 3 00360 if( ptr1->currentPitchOffset != ptr2->currentPitchOffset ) __asm int 3 00361 if( ptr1->lfoPitchOffset != ptr2->lfoPitchOffset ) __asm int 3 00362 if( ptr1->timeKeyfollowSubtraction != ptr2->timeKeyfollowSubtraction ) __asm int 3 00363 if( ptr1->pitchOffsetChangePerBigTick != ptr2->pitchOffsetChangePerBigTick ) __asm int 3 00364 if( ptr1->targetPitchOffsetReachedBigTick != ptr2->targetPitchOffsetReachedBigTick ) __asm int 3 00365 if( ptr1->shifts != ptr2->shifts ) __asm int 3 00366 if( ptr1->pitch != ptr2->pitch ) __asm int 3 00367 00368 00369 00370 // avoid destructor problems 00371 memset( ptr2, 0, sizeof(*ptr2) ); 00372 } 00373 #endif 00374 00375 00376 void TVP::saveState( std::ostream &stream ) 00377 { 00378 Bit16u partialParam_idx1, partialParam_idx2; 00379 Bit8u part_idx; 00380 Bit8u patchTemp_idx; 00381 00382 00383 // - static fastptr 00384 //const Partial * const partial; 00385 //const MemParams::System * const system; // FIXME: Only necessary because masterTune calculation is done in the wrong place atm. 00386 00387 00388 // - reloc fastptr (!!) 00389 //const Part *part; 00390 partial->getSynth()->findPart( part, &part_idx ); 00391 stream.write(reinterpret_cast<const char*>(&part_idx), sizeof(part_idx) ); 00392 00393 00394 // - reloc fastptr (!!) 00395 //const TimbreParam::PartialParam *partialParam; 00396 partial->getSynth()->findPartialParam( partialParam, &partialParam_idx1, &partialParam_idx2 ); 00397 stream.write(reinterpret_cast<const char*>(&partialParam_idx1), sizeof(partialParam_idx1) ); 00398 stream.write(reinterpret_cast<const char*>(&partialParam_idx2), sizeof(partialParam_idx2) ); 00399 00400 00401 // - reloc fastptr (!!) 00402 //const MemParams::PatchTemp *patchTemp; 00403 partial->getSynth()->findPatchTemp( patchTemp, &patchTemp_idx ); 00404 stream.write(reinterpret_cast<const char*>(&patchTemp_idx), sizeof(patchTemp_idx) ); 00405 00406 00407 stream.write(reinterpret_cast<const char*>(&maxCounter), sizeof(maxCounter) ); 00408 stream.write(reinterpret_cast<const char*>(&processTimerIncrement), sizeof(processTimerIncrement) ); 00409 stream.write(reinterpret_cast<const char*>(&counter), sizeof(counter) ); 00410 stream.write(reinterpret_cast<const char*>(&timeElapsed), sizeof(timeElapsed) ); 00411 stream.write(reinterpret_cast<const char*>(&phase), sizeof(phase) ); 00412 stream.write(reinterpret_cast<const char*>(&basePitch), sizeof(basePitch) ); 00413 stream.write(reinterpret_cast<const char*>(&targetPitchOffsetWithoutLFO), sizeof(targetPitchOffsetWithoutLFO) ); 00414 stream.write(reinterpret_cast<const char*>(¤tPitchOffset), sizeof(currentPitchOffset) ); 00415 stream.write(reinterpret_cast<const char*>(&lfoPitchOffset), sizeof(lfoPitchOffset) ); 00416 stream.write(reinterpret_cast<const char*>(&timeKeyfollowSubtraction), sizeof(timeKeyfollowSubtraction) ); 00417 stream.write(reinterpret_cast<const char*>(&pitchOffsetChangePerBigTick), sizeof(pitchOffsetChangePerBigTick) ); 00418 stream.write(reinterpret_cast<const char*>(&targetPitchOffsetReachedBigTick), sizeof(targetPitchOffsetReachedBigTick) ); 00419 stream.write(reinterpret_cast<const char*>(&shifts), sizeof(shifts) ); 00420 stream.write(reinterpret_cast<const char*>(&pitch), sizeof(pitch) ); 00421 00422 00423 #ifdef WIN32_DEBUG 00424 // DEBUG 00425 partial->getSynth()->rawDumpState( "temp-save", this, sizeof(*this) ); 00426 partial->getSynth()->rawDumpNo++; 00427 #endif 00428 } 00429 00430 00431 void TVP::loadState( std::istream &stream ) 00432 { 00433 Bit16u partialParam_idx1, partialParam_idx2; 00434 Bit8u part_idx; 00435 Bit8u patchTemp_idx; 00436 00437 00438 // - static fastptr 00439 //const Partial * const partial; 00440 //const MemParams::System * const system; // FIXME: Only necessary because masterTune calculation is done in the wrong place atm. 00441 00442 00443 // - reloc fastptr (!!) 00444 //const Part *part; 00445 stream.read(reinterpret_cast<char*>(&part_idx), sizeof(part_idx) ); 00446 part = partial->getSynth()->indexPart(part_idx); 00447 00448 00449 // - reloc fastptr (!!) 00450 //const TimbreParam::PartialParam *partialParam; 00451 stream.read(reinterpret_cast<char*>(&partialParam_idx1), sizeof(partialParam_idx1) ); 00452 stream.read(reinterpret_cast<char*>(&partialParam_idx2), sizeof(partialParam_idx2) ); 00453 partialParam = partial->getSynth()->indexPartialParam(partialParam_idx1, partialParam_idx2); 00454 00455 00456 // - reloc fastptr (!!) 00457 //const MemParams::PatchTemp *patchTemp; 00458 stream.read(reinterpret_cast<char*>(&patchTemp_idx), sizeof(patchTemp_idx) ); 00459 patchTemp = partial->getSynth()->indexPatchTemp(patchTemp_idx); 00460 00461 00462 stream.read(reinterpret_cast<char*>(&maxCounter), sizeof(maxCounter) ); 00463 stream.read(reinterpret_cast<char*>(&processTimerIncrement), sizeof(processTimerIncrement) ); 00464 stream.read(reinterpret_cast<char*>(&counter), sizeof(counter) ); 00465 stream.read(reinterpret_cast<char*>(&timeElapsed), sizeof(timeElapsed) ); 00466 stream.read(reinterpret_cast<char*>(&phase), sizeof(phase) ); 00467 stream.read(reinterpret_cast<char*>(&basePitch), sizeof(basePitch) ); 00468 stream.read(reinterpret_cast<char*>(&targetPitchOffsetWithoutLFO), sizeof(targetPitchOffsetWithoutLFO) ); 00469 stream.read(reinterpret_cast<char*>(¤tPitchOffset), sizeof(currentPitchOffset) ); 00470 stream.read(reinterpret_cast<char*>(&lfoPitchOffset), sizeof(lfoPitchOffset) ); 00471 stream.read(reinterpret_cast<char*>(&timeKeyfollowSubtraction), sizeof(timeKeyfollowSubtraction) ); 00472 stream.read(reinterpret_cast<char*>(&pitchOffsetChangePerBigTick), sizeof(pitchOffsetChangePerBigTick) ); 00473 stream.read(reinterpret_cast<char*>(&targetPitchOffsetReachedBigTick), sizeof(targetPitchOffsetReachedBigTick) ); 00474 stream.read(reinterpret_cast<char*>(&shifts), sizeof(shifts) ); 00475 stream.read(reinterpret_cast<char*>(&pitch), sizeof(pitch) ); 00476 00477 00478 #ifdef WIN32_DEBUG 00479 // DEBUG 00480 partial->getSynth()->rawDumpState( "temp-load", this, sizeof(*this) ); 00481 this->rawVerifyState( "temp-save", partial->getSynth() ); 00482 partial->getSynth()->rawDumpNo++; 00483 #endif 00484 } 00485 00486 }