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 #ifndef MT32EMU_SYNTH_H 00019 #define MT32EMU_SYNTH_H 00020 00021 #include <cstdarg> 00022 00023 namespace MT32Emu { 00024 00025 class File; 00026 class TableInitialiser; 00027 class Partial; 00028 class PartialManager; 00029 class Part; 00030 class ROMImage; 00031 00037 enum DACInputMode { 00038 // Produces samples at double the volume, without tricks. 00039 // * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range) 00040 // * Higher quality than the real devices 00041 DACInputMode_NICE, 00042 00043 // Produces samples that exactly match the bits output from the emulated LA32. 00044 // * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range) 00045 // * Much less likely to overdrive than any other mode. 00046 // * Half the volume of any of the other modes, meaning its volume relative to the reverb 00047 // output when mixed together directly will sound wrong. 00048 // * Perfect for developers while debugging :) 00049 DACInputMode_PURE, 00050 00051 // Re-orders the LA32 output bits as in early generation MT-32s (according to Wikipedia). 00052 // Bit order at DAC (where each number represents the original LA32 output bit number, and XX means the bit is always low): 00053 // 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 XX 00054 DACInputMode_GENERATION1, 00055 00056 // Re-orders the LA32 output bits as in later generations (personally confirmed on my CM-32L - KG). 00057 // Bit order at DAC (where each number represents the original LA32 output bit number): 00058 // 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 14 00059 DACInputMode_GENERATION2 00060 }; 00061 00062 typedef void (*FloatToBit16sFunc)(Bit16s *target, const float *source, Bit32u len, float outputGain); 00063 00064 const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41; 00065 00066 const Bit8u SYSEX_MDL_MT32 = 0x16; 00067 const Bit8u SYSEX_MDL_D50 = 0x14; 00068 00069 const Bit8u SYSEX_CMD_RQ1 = 0x11; // Request data #1 00070 const Bit8u SYSEX_CMD_DT1 = 0x12; // Data set 1 00071 const Bit8u SYSEX_CMD_WSD = 0x40; // Want to send data 00072 const Bit8u SYSEX_CMD_RQD = 0x41; // Request data 00073 const Bit8u SYSEX_CMD_DAT = 0x42; // Data set 00074 const Bit8u SYSEX_CMD_ACK = 0x43; // Acknowledge 00075 const Bit8u SYSEX_CMD_EOD = 0x45; // End of data 00076 const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error 00077 const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection 00078 00079 const int MAX_SYSEX_SIZE = 512; 00080 00081 const unsigned int CONTROL_ROM_SIZE = 64 * 1024; 00082 00083 struct ControlROMPCMStruct { 00084 Bit8u pos; 00085 Bit8u len; 00086 Bit8u pitchLSB; 00087 Bit8u pitchMSB; 00088 }; 00089 00090 struct ControlROMMap { 00091 Bit16u idPos; 00092 Bit16u idLen; 00093 const char *idBytes; 00094 Bit16u pcmTable; // 4 * pcmCount bytes 00095 Bit16u pcmCount; 00096 Bit16u timbreAMap; // 128 bytes 00097 Bit16u timbreAOffset; 00098 bool timbreACompressed; 00099 Bit16u timbreBMap; // 128 bytes 00100 Bit16u timbreBOffset; 00101 bool timbreBCompressed; 00102 Bit16u timbreRMap; // 2 * timbreRCount bytes 00103 Bit16u timbreRCount; 00104 Bit16u rhythmSettings; // 4 * rhythmSettingsCount bytes 00105 Bit16u rhythmSettingsCount; 00106 Bit16u reserveSettings; // 9 bytes 00107 Bit16u panSettings; // 8 bytes 00108 Bit16u programSettings; // 8 bytes 00109 Bit16u rhythmMaxTable; // 4 bytes 00110 Bit16u patchMaxTable; // 16 bytes 00111 Bit16u systemMaxTable; // 23 bytes 00112 Bit16u timbreMaxTable; // 72 bytes 00113 }; 00114 00115 enum MemoryRegionType { 00116 MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset 00117 }; 00118 00119 enum ReverbMode { 00120 REVERB_MODE_ROOM, 00121 REVERB_MODE_HALL, 00122 REVERB_MODE_PLATE, 00123 REVERB_MODE_TAP_DELAY 00124 }; 00125 00126 class MemoryRegion { 00127 private: 00128 Synth *synth; 00129 Bit8u *realMemory; 00130 Bit8u *maxTable; 00131 public: 00132 MemoryRegionType type; 00133 Bit32u startAddr, entrySize, entries; 00134 00135 MemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable, MemoryRegionType useType, Bit32u useStartAddr, Bit32u useEntrySize, Bit32u useEntries) { 00136 synth = useSynth; 00137 realMemory = useRealMemory; 00138 maxTable = useMaxTable; 00139 type = useType; 00140 startAddr = useStartAddr; 00141 entrySize = useEntrySize; 00142 entries = useEntries; 00143 } 00144 int lastTouched(Bit32u addr, Bit32u len) const { 00145 return (int)(((unsigned long)offset(addr) + len - 1u) / entrySize); 00146 } 00147 int firstTouchedOffset(Bit32u addr) const { 00148 return (int)((unsigned int)offset(addr) % entrySize); 00149 } 00150 int firstTouched(Bit32u addr) const { 00151 return (int)((unsigned int)offset(addr) / entrySize); 00152 } 00153 Bit32u regionEnd() const { 00154 return startAddr + entrySize * entries; 00155 } 00156 bool contains(Bit32u addr) const { 00157 return addr >= startAddr && addr < regionEnd(); 00158 } 00159 int offset(Bit32u addr) const { 00160 return (int)(addr - startAddr); 00161 } 00162 Bit32u getClampedLen(Bit32u addr, Bit32u len) const { 00163 if (addr + len > regionEnd()) 00164 return regionEnd() - addr; 00165 return len; 00166 } 00167 Bit32u next(Bit32u addr, Bit32u len) const { 00168 if (addr + len > regionEnd()) { 00169 return regionEnd() - addr; 00170 } 00171 return 0; 00172 } 00173 Bit8u getMaxValue(int off) const { 00174 if (maxTable == NULL) 00175 return 0xFF; 00176 return maxTable[(unsigned int)off % entrySize]; 00177 } 00178 Bit8u *getRealMemory() const { 00179 return realMemory; 00180 } 00181 bool isReadable() const { 00182 return getRealMemory() != NULL; 00183 } 00184 void read(unsigned int entry, unsigned int off, Bit8u *dst, unsigned int len) const; 00185 void write(unsigned int entry, unsigned int off, const Bit8u *src, unsigned int len, bool init = false) const; 00186 }; 00187 00188 class PatchTempMemoryRegion : public MemoryRegion { 00189 public: 00190 PatchTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_PatchTemp, MT32EMU_MEMADDR(0x030000), sizeof(MemParams::PatchTemp), 9) {} 00191 }; 00192 class RhythmTempMemoryRegion : public MemoryRegion { 00193 public: 00194 RhythmTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_RhythmTemp, MT32EMU_MEMADDR(0x030110), sizeof(MemParams::RhythmTemp), 85) {} 00195 }; 00196 class TimbreTempMemoryRegion : public MemoryRegion { 00197 public: 00198 TimbreTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_TimbreTemp, MT32EMU_MEMADDR(0x040000), sizeof(TimbreParam), 8) {} 00199 }; 00200 class PatchesMemoryRegion : public MemoryRegion { 00201 public: 00202 PatchesMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Patches, MT32EMU_MEMADDR(0x050000), sizeof(PatchParam), 128) {} 00203 }; 00204 class TimbresMemoryRegion : public MemoryRegion { 00205 public: 00206 TimbresMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Timbres, MT32EMU_MEMADDR(0x080000), sizeof(MemParams::PaddedTimbre), 64 + 64 + 64 + 64) {} 00207 }; 00208 class SystemMemoryRegion : public MemoryRegion { 00209 public: 00210 SystemMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_System, MT32EMU_MEMADDR(0x100000), sizeof(MemParams::System), 1) {} 00211 }; 00212 class DisplayMemoryRegion : public MemoryRegion { 00213 public: 00214 DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1) {} 00215 }; 00216 class ResetMemoryRegion : public MemoryRegion { 00217 public: 00218 ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {} 00219 }; 00220 00221 class ReverbModel { 00222 public: 00223 virtual ~ReverbModel() {} 00224 // After construction or a close(), open() will be called at least once before any other call (with the exception of close()). 00225 virtual void open() = 0; 00226 // May be called multiple times without an open() in between. 00227 virtual void close() = 0; 00228 virtual void setParameters(Bit8u time, Bit8u level) = 0; 00229 virtual void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) = 0; 00230 virtual bool isActive() const = 0; 00231 00232 virtual void saveState( std::ostream &stream ) { (void)stream; } 00233 virtual void loadState( std::istream &stream ) { (void)stream; } 00234 }; 00235 00236 class ReportHandler { 00237 friend class Synth; 00238 00239 public: 00240 virtual ~ReportHandler() {} 00241 00242 protected: 00243 00244 // Callback for debug messages, in vprintf() format 00245 virtual void printDebug(const char *fmt, va_list list); 00246 00247 // Callbacks for reporting various errors and information 00248 virtual void onErrorControlROM() {} 00249 virtual void onErrorPCMROM() {} 00250 virtual void showLCDMessage(const char *data); 00251 virtual void onDeviceReset() {} 00252 virtual void onDeviceReconfig() {} 00253 virtual void onNewReverbMode(Bit8u /* mode */) {} 00254 virtual void onNewReverbTime(Bit8u /* time */) {} 00255 virtual void onNewReverbLevel(Bit8u /* level */) {} 00256 virtual void onPartStateChanged(int /* partNum */, bool /* isActive */) {} 00257 virtual void onPolyStateChanged(int /* partNum */) {} 00258 virtual void onPartialStateChanged(int /* partialNum */, int /* oldPartialPhase */, int /* newPartialPhase */) {} 00259 virtual void onProgramChanged(int /* partNum */, char * /* patchName */) {} 00260 }; 00261 00262 class Synth { 00263 friend class Part; 00264 friend class RhythmPart; 00265 friend class Poly; 00266 friend class Partial; 00267 friend class Tables; 00268 friend class MemoryRegion; 00269 friend class TVA; 00270 friend class TVF; 00271 friend class TVP; 00272 private: 00273 PatchTempMemoryRegion *patchTempMemoryRegion; 00274 RhythmTempMemoryRegion *rhythmTempMemoryRegion; 00275 TimbreTempMemoryRegion *timbreTempMemoryRegion; 00276 PatchesMemoryRegion *patchesMemoryRegion; 00277 TimbresMemoryRegion *timbresMemoryRegion; 00278 SystemMemoryRegion *systemMemoryRegion; 00279 DisplayMemoryRegion *displayMemoryRegion; 00280 ResetMemoryRegion *resetMemoryRegion; 00281 00282 Bit8u *paddedTimbreMaxTable; 00283 00284 bool isEnabled; 00285 00286 PCMWaveEntry *pcmWaves; // Array 00287 00288 const ControlROMMap *controlROMMap; 00289 Bit8u controlROMData[CONTROL_ROM_SIZE]; 00290 Bit16s *pcmROMData; 00291 size_t pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM 00292 00293 Bit8s chantable[32]; 00294 00295 Bit32u renderedSampleCount; 00296 00297 00298 MemParams mt32ram, mt32default; 00299 00300 ReverbModel *reverbModels[4]; 00301 ReverbModel *reverbModel; 00302 bool reverbEnabled; 00303 bool reverbOverridden; 00304 00305 FloatToBit16sFunc la32FloatToBit16sFunc; 00306 FloatToBit16sFunc reverbFloatToBit16sFunc; 00307 float outputGain; 00308 float reverbOutputGain; 00309 00310 bool isOpen; 00311 00312 bool isDefaultReportHandler; 00313 ReportHandler *reportHandler; 00314 00315 PartialManager *partialManager; 00316 Part *parts[9]; 00317 00318 // FIXME: We can reorganise things so that we don't need all these separate tmpBuf, tmp and prerender buffers. 00319 // This should be rationalised when things have stabilised a bit (if prerender buffers don't die in the mean time). 00320 00321 float tmpBufPartialLeft[MAX_SAMPLES_PER_RUN]; 00322 float tmpBufPartialRight[MAX_SAMPLES_PER_RUN]; 00323 float tmpBufMixLeft[MAX_SAMPLES_PER_RUN]; 00324 float tmpBufMixRight[MAX_SAMPLES_PER_RUN]; 00325 float tmpBufReverbOutLeft[MAX_SAMPLES_PER_RUN]; 00326 float tmpBufReverbOutRight[MAX_SAMPLES_PER_RUN]; 00327 00328 Bit16s tmpNonReverbLeft[MAX_SAMPLES_PER_RUN]; 00329 Bit16s tmpNonReverbRight[MAX_SAMPLES_PER_RUN]; 00330 Bit16s tmpReverbDryLeft[MAX_SAMPLES_PER_RUN]; 00331 Bit16s tmpReverbDryRight[MAX_SAMPLES_PER_RUN]; 00332 Bit16s tmpReverbWetLeft[MAX_SAMPLES_PER_RUN]; 00333 Bit16s tmpReverbWetRight[MAX_SAMPLES_PER_RUN]; 00334 00335 // These ring buffers are only used to simulate delays present on the real device. 00336 // In particular, when a partial needs to be aborted to free it up for use by a new Poly, 00337 // the controller will busy-loop waiting for the sound to finish. 00338 Bit16s prerenderNonReverbLeft[MAX_PRERENDER_SAMPLES]; 00339 Bit16s prerenderNonReverbRight[MAX_PRERENDER_SAMPLES]; 00340 Bit16s prerenderReverbDryLeft[MAX_PRERENDER_SAMPLES]; 00341 Bit16s prerenderReverbDryRight[MAX_PRERENDER_SAMPLES]; 00342 Bit16s prerenderReverbWetLeft[MAX_PRERENDER_SAMPLES]; 00343 Bit16s prerenderReverbWetRight[MAX_PRERENDER_SAMPLES]; 00344 int prerenderReadIx; 00345 int prerenderWriteIx; 00346 00347 unsigned int partialLimit; 00348 00349 bool prerender(); 00350 void copyPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u pos, Bit32u len); 00351 void checkPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u &pos, Bit32u &len); 00352 void doRenderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len); 00353 00354 void playAddressedSysex(unsigned char channel, const Bit8u *sysex, Bit32u len); 00355 void readSysex(unsigned char channel, const Bit8u *sysex, Bit32u len) const; 00356 void initMemoryRegions(); 00357 void deleteMemoryRegions(); 00358 MemoryRegion *findMemoryRegion(Bit32u addr); 00359 void writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data); 00360 void readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data); 00361 00362 bool loadControlROM(const ROMImage &controlROMImage); 00363 bool loadPCMROM(const ROMImage &pcmROMImage); 00364 00365 bool initPCMList(Bit16u mapAddress, Bit16u count); 00366 bool initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTimbre, bool compressed); 00367 bool initCompressedTimbre(int timbreNum, const Bit8u *src, unsigned int srcLen); 00368 00369 void refreshSystemMasterTune(); 00370 void refreshSystemReverbParameters(); 00371 void refreshSystemReserveSettings(); 00372 void refreshSystemChanAssign(unsigned int firstPart, unsigned int lastPart); 00373 void refreshSystemMasterVol(); 00374 void refreshSystem(); 00375 void reset(); 00376 00377 void printPartialUsage(unsigned long sampleOffset = 0); 00378 00379 void partStateChanged(int partNum, bool isPartActive); 00380 void polyStateChanged(int partNum); 00381 void partialStateChanged(const Partial * const partial, int oldPartialPhase, int newPartialPhase); 00382 void newTimbreSet(int partNum, char patchName[]); 00383 void printDebug(const char *fmt, ...); 00384 00385 public: 00386 static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum); 00387 00388 // Optionally sets callbacks for reporting various errors, information and debug messages 00389 Synth(ReportHandler *useReportHandler = NULL); 00390 ~Synth(); 00391 00392 // Used to initialise the MT-32. Must be called before any other function. 00393 // Returns true if initialization was sucessful, otherwise returns false. 00394 // controlROMImage and pcmROMImage represent Control and PCM ROM images for use by synth. 00395 bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage); 00396 00397 // Closes the MT-32 and deallocates any memory used by the synthesizer 00398 void close(void); 00399 00400 // Sends a 4-byte MIDI message to the MT-32 for immediate playback 00401 void playMsg(Bit32u msg); 00402 void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity); 00403 00404 // Sends a string of Sysex commands to the MT-32 for immediate interpretation 00405 // The length is in bytes 00406 void playSysex(const Bit8u *sysex, Bit32u len); 00407 void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len); 00408 void playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len); 00409 void writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len); 00410 00411 void setReverbEnabled(bool newReverbEnabled); 00412 bool isReverbEnabled() const; 00413 void setReverbOverridden(bool newReverbOverridden); 00414 bool isReverbOverridden() const; 00415 void setDACInputMode(DACInputMode mode); 00416 00417 // Sets output gain factor. Applied to all output samples and unrelated with the synth's Master volume. 00418 void setOutputGain(float); 00419 00420 // Sets output gain factor for the reverb wet output. setOutputGain() doesn't change reverb output gain. 00421 void setReverbOutputGain(float); 00422 00423 // Renders samples to the specified output stream. 00424 // The length is in frames, not bytes (in 16-bit stereo, 00425 // one frame is 4 bytes). 00426 void render(Bit16s *stream, Bit32u len); 00427 00428 // Renders samples to the specified output streams (any or all of which may be NULL). 00429 void renderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len); 00430 00431 // Returns true when there is at least one active partial, otherwise false. 00432 bool hasActivePartials() const; 00433 00434 // Returns true if hasActivePartials() returns true, or reverb is (somewhat unreliably) detected as being active. 00435 bool isActive() const; 00436 00437 const Partial *getPartial(unsigned int partialNum) const; 00438 00439 void setPartialLimit( unsigned int _partialLimit ); 00440 unsigned int getPartialLimit() const; 00441 00442 void readMemory(Bit32u addr, Bit32u len, Bit8u *data); 00443 00444 // partNum should be 0..7 for Part 1..8, or 8 for Rhythm 00445 const Part *getPart(unsigned int partNum) const; 00446 00447 // svn-daum 00448 void *dumpRam(); 00449 void loadRam( void *buf ); 00450 00451 void findPart( const Part *src, Bit8u *index_out ); 00452 void findPartial( const Partial *src, Bit8u *index_out ); 00453 void findPartialParam( const TimbreParam::PartialParam *src, Bit16u *index_out1, Bit16u *index_out2 ); 00454 void findPatchCache( const PatchCache *src, Bit16u *index_out1, Bit16u *index_out2 ); 00455 void findPatchTemp( const MemParams::PatchTemp *src, Bit8u *index_out ); 00456 void findPCMWaveEntry( const PCMWaveEntry *src, Bit16u *index_out ); 00457 void findPoly( const Poly *src, Bit16u *index_out1, Bit16u *index_out2 ); 00458 void findRhythmTemp( const MemParams::RhythmTemp *src, Bit8u *index_out ); 00459 void findTimbreParam( const TimbreParam *src, Bit8u *index_out ); 00460 00461 Part *indexPart( Bit8u index ); 00462 Partial *indexPartial( Bit8u index ); 00463 TimbreParam::PartialParam *indexPartialParam( Bit16u index1, Bit16u index2 ); 00464 PatchCache *indexPatchCache( Bit16u index1, Bit16u index2 ); 00465 MemParams::PatchTemp *indexPatchTemp( Bit8u index ); 00466 PCMWaveEntry *indexPCMWaveEntry( Bit16u index ); 00467 Poly *indexPoly( Bit16u index1, Bit16u index2 ); 00468 MemParams::RhythmTemp *indexRhythmTemp( Bit8u index ); 00469 TimbreParam *indexTimbreParam( Bit8u index ); 00470 }; 00471 00472 00473 // debugger only 00474 //#define WIN32_DEBUG 00475 //#define WIN32_DUMP 00476 } 00477 00478 #endif