diff --git a/docs/rh-log.txt b/docs/rh-log.txt index b931c8459..5e9116f79 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,37 @@ +March 25, 2008 +- The garbage collector is now run one last time just before exiting the game. +- Removed movie volume from the sound menu and renamed some of the other + options to give the MIDI device name more room to display itself. +- Moved the midi device selection into the main sound menu. +- Added FMOD as MIDI device -1, to replace the MIDI mapper. This is still the + default device. By default, it uses exactly the same DLS instruments as the + Microsoft GS Wavetable Synth. If you have another set DLS level 1 patch set + you want to use, set the snd_midipatchfile cvar to specify where it should + load the instruments from. +- Changed the ProduceMIDI function to store its output into a TArray. + An overloaded version wraps around it to continue to supply file-writing + support for external Timidity++ usage. +- Added an FMOD credits banner to comply with their non-commercial license. +- Reimplemented the snd_buffersize cvar for the FMOD Ex sound system. Rather + than a time in ms, this is now the length in samples of the DSP buffer. + Also added the snd_buffercount cvar to offer complete control over the + call to FMOD::System::setDSPBufferSize(). Note that with any snd_samplerate + below about 44kHz, you will need to set snd_buffersize to avoid long + latencies. +- Reimplemented the snd_output cvar for the FMOD Ex sound system. +- Changed snd_samplerate default to 0. This now means to use the default + sample rate. +- Made snd_output, snd_output_format, snd_speakermode, snd_resampler, and + snd_hrtf available through the menu. +- Split the HRTF effect selection into its own cvar: snd_hrtf. +- Removed 96000 Hz option from the menu. It's still available through the + cvar, if desired. +- Fixed: If Windows sound init failed, retry with DirectSound. (Apparently, + WASAPI doesn't work with more than two speakers and PCM-Float output at the + same time.) +- Fixed: Area sounds only played from the front speakers once you got within + the 2D panning area. + March 25, 2008 (Changes by Graf Zahl) - Increased the limit for 'imp/active' to 6. This sound definitely benefits from a higher limit. diff --git a/src/b_game.cpp b/src/b_game.cpp index cdb2e52e9..602814d14 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -88,7 +88,6 @@ enum static bool waitingforspawn[MAXPLAYERS]; - DCajunMaster::~DCajunMaster() { ForgetBots(); diff --git a/src/dobject.cpp b/src/dobject.cpp index 304d1c87d..790147e94 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -418,16 +418,19 @@ void DObject::Destroy () size_t DObject::PropagateMark() { const PClass *info = GetClass(); - const size_t *offsets = info->FlatPointers; - if (offsets == NULL) + if (!PClass::bShutdown) { - const_cast(info)->BuildFlatPointers(); - offsets = info->FlatPointers; - } - while (*offsets != ~(size_t)0) - { - GC::Mark((DObject **)((BYTE *)this + *offsets)); - offsets++; + const size_t *offsets = info->FlatPointers; + if (offsets == NULL) + { + const_cast(info)->BuildFlatPointers(); + offsets = info->FlatPointers; + } + while (*offsets != ~(size_t)0) + { + GC::Mark((DObject **)((BYTE *)this + *offsets)); + offsets++; + } } return info->Size; } diff --git a/src/dobjgc.cpp b/src/dobjgc.cpp index 1bf3d483c..840c4e94b 100644 --- a/src/dobjgc.cpp +++ b/src/dobjgc.cpp @@ -298,10 +298,14 @@ static void MarkRoot() if (playeringame[i]) players[i].PropagateMark(); } - if (SectorMarker == NULL) + if (SectorMarker == NULL && sectors != NULL) { SectorMarker = new DSectorMarker; } + else if (sectors == NULL) + { + SectorMarker = NULL; + } else { SectorMarker->SecNum = 0; diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 53c8fa755..dcd2f697b 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -41,6 +41,7 @@ TArray PClass::m_RuntimeActors; TArray PClass::m_Types; PClass *PClass::TypeHash[PClass::HASH_SIZE]; +bool PClass::bShutdown; // A harmless non_NULL FlatPointer for classes without pointers. static const size_t TheEnd = ~0u; @@ -110,6 +111,7 @@ void PClass::StaticShutdown () { delete[] uniqueFPs[i]; } + bShutdown = true; } void PClass::StaticFreeData (PClass *type) diff --git a/src/dobjtype.h b/src/dobjtype.h index 88f832f9a..aae53970b 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -138,6 +138,8 @@ struct PClass enum { HASH_SIZE = 256 }; static PClass *TypeHash[HASH_SIZE]; + + static bool bShutdown; }; #endif diff --git a/src/m_menu.h b/src/m_menu.h index 4419828c9..679ee604c 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -71,8 +71,7 @@ void M_OptInit (void); // [RH] Initialize the video modes menu void M_InitVideoModesMenu (void); -struct menu_s; -void M_SwitchMenu (struct menu_s *menu); +void M_SwitchMenu (struct menu_t *menu); void M_PopMenuStack (void); @@ -100,6 +99,7 @@ typedef enum { discrete, discretes, cdiscrete, + ediscrete, discrete_guid, control, screenres, @@ -145,8 +145,9 @@ typedef struct menuitem_s { char *res3; } d; union { - struct value_s *values; + struct value_t *values; struct valuestring_t *valuestrings; + struct valueenum_t *enumvalues; GUIDName *guidvalues; char *command; void (*cfunc)(FBaseCVar *cvar, float newval); @@ -157,7 +158,7 @@ typedef struct menuitem_s { } e; } menuitem_t; -typedef struct menu_s { +struct menu_t { const char *texttitle; int lastOn; int numitems; @@ -169,18 +170,23 @@ typedef struct menu_s { void (*PreDraw)(void); bool DontDim; void (*EscapeHandler)(void); -} menu_t; +}; -typedef struct value_s { +struct value_t { float value; const char *name; -} value_t; +}; struct valuestring_t { float value; FString name; }; +struct valueenum_t { + const char *value; // Value of cvar + const char *name; // Name on menu +}; + typedef struct { // -1 = no cursor here, 1 = ok, 2 = arrows ok diff --git a/src/m_options.cpp b/src/m_options.cpp index 0941cfa61..f3759db8b 100644 --- a/src/m_options.cpp +++ b/src/m_options.cpp @@ -100,9 +100,6 @@ extern int skullAnimCounter; EXTERN_CVAR (Bool, cl_run) EXTERN_CVAR (Int, crosshair) EXTERN_CVAR (Bool, freelook) -EXTERN_CVAR (Int, snd_buffersize) -EXTERN_CVAR (Int, snd_samplerate) -EXTERN_CVAR (Bool, snd_waterreverb) EXTERN_CVAR (Int, sv_smartaim) static void CalcIndent (menu_t *menu); @@ -1128,62 +1125,131 @@ EXTERN_CVAR (Float, snd_movievolume) #endif EXTERN_CVAR (Bool, snd_flipstereo) EXTERN_CVAR (Bool, snd_pitched) +EXTERN_CVAR (String, snd_output_format) +EXTERN_CVAR (String, snd_speakermode) +EXTERN_CVAR (String, snd_resampler) +EXTERN_CVAR (String, snd_output) +EXTERN_CVAR (Int, snd_buffersize) +EXTERN_CVAR (Int, snd_buffercount) +EXTERN_CVAR (Int, snd_samplerate) +EXTERN_CVAR (Bool, snd_hrtf) +EXTERN_CVAR (Bool, snd_waterreverb) +EXTERN_CVAR (Int, snd_mididevice) static void MakeSoundChanges (); static void AdvSoundOptions (); -static void ChooseMIDI (); static value_t SampleRates[] = { - { 4000.f, "4000 Hz" }, - { 8000.f, "8000 Hz" }, - { 11025.f, "11025 Hz" }, - { 22050.f, "22050 Hz" }, - { 32000.f, "32000 Hz" }, - { 44100.f, "44100 Hz" }, - { 48000.f, "48000 Hz" }, - { 96000.f, "96000 Hz" } + { 0.f, "Default" }, + { 4000.f, "4000 Hz" }, + { 8000.f, "8000 Hz" }, + { 11025.f, "11025 Hz" }, + { 22050.f, "22050 Hz" }, + { 32000.f, "32000 Hz" }, + { 44100.f, "44100 Hz" }, + { 48000.f, "48000 Hz" } }; static value_t BufferSizes[] = { - { 0.f, "Default" }, - { 20.f, "20 ms" }, - { 40.f, "40 ms" }, - { 60.f, "60 ms" }, - { 80.f, "80 ms" }, - { 100.f, "100 ms" }, - { 120.f, "120 ms" }, - { 140.f, "140 ms" }, - { 160.f, "160 ms" }, - { 180.f, "180 ms" }, - { 200.f, "200 ms" }, + { 0.f, "Default" }, + { 64.f, "64 samples" }, + { 128.f, "128 samples" }, + { 256.f, "256 samples" }, + { 512.f, "512 samples" }, + { 1024.f, "1024 samples" }, + { 2048.f, "2048 samples" }, + { 4096.f, "4096 samples" } +}; + +static value_t BufferCounts[] = +{ + { 0.f, "Default" }, + { 2.f, "2" }, + { 3.f, "3" }, + { 4.f, "4" }, + { 5.f, "5" }, + { 6.f, "6" }, + { 7.f, "7" }, + { 8.f, "8" }, + { 9.f, "9" }, + { 10.f, "10" }, + { 11.f, "11" }, + { 12.f, "12" } +}; + +static valueenum_t Outputs[] = +{ + { "Default", "Default" }, +#if defined(_WIN32) + { "DirectSound", "DirectSound" }, + { "WASAPI", "Vista WASAPI" }, + { "ASIO", "ASIO" }, + { "WaveOut", "WaveOut" }, + { "OpenAL", "OpenAL (very beta)" }, +#elif defined(unix) + { "OSS", "OSS" }, + { "ALSA", "ALSA" }, + { "ESD", "ESD" }, +#elif defined(__APPLE__) + { "Sound Manager", "Sound Manager" }, + { "Core Audio", "Core Audio" }, +#endif + { "No sound", "No sound" } +}; + +static valueenum_t OutputFormats[] = +{ + { "PCM-8", "8-bit" }, + { "PCM-16", "16-bit" }, + { "PCM-24", "24-bit" }, + { "PCM-32", "32-bit" }, + { "PCM-Float", "32-bit float" } +}; + +static valueenum_t SpeakerModes[] = +{ + { "Auto", "Auto" }, + { "Mono", "Mono" }, + { "Stereo", "Stereo" }, + { "Prologic", "Dolby Prologic Decoder" }, + { "Quad", "Quad" }, + { "Surround", "5 speakers" }, + { "5.1", "5.1 speakers" }, + { "7.1", "7.1 speakers" } +}; + +static valueenum_t Resamplers[] = +{ + { "NoInterp", "No interpolation" }, + { "Linear", "Linear" }, + { "Cubic", "Cubic" }, + { "Spline", "Spline" } }; static menuitem_t SoundItems[] = { - { slider, "Sound effects volume", {&snd_sfxvolume}, {0.0}, {1.0}, {0.05}, {NULL} }, -#ifdef _WIN32 + { slider, "Sounds volume", {&snd_sfxvolume}, {0.0}, {1.0}, {0.05}, {NULL} }, { slider, "Music volume", {&snd_musicvolume}, {0.0}, {1.0}, {0.05}, {NULL} }, - { slider, "Movie volume", {&snd_movievolume}, {0.0}, {1.0}, {0.05}, {NULL} }, -#else - { slider, "Music volume", {&snd_musicvolume}, {0.0}, {1.0}, {0.05}, {NULL} }, -#endif + { discrete, "MIDI device", {&snd_mididevice}, {0.0}, {0.0}, {0.0}, {NULL} }, { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Underwater Reverb", {&snd_waterreverb}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Flip Stereo Channels", {&snd_flipstereo}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Random Pitch Variations", {&snd_pitched}, {2.0}, {0.0}, {0.0}, {OnOff} }, + { discrete, "Underwater reverb", {&snd_waterreverb}, {2.0}, {0.0}, {0.0}, {OnOff} }, + { discrete, "Randomize pitches", {&snd_pitched}, {2.0}, {0.0}, {0.0}, {OnOff} }, { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { more, "Activate below settings", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)MakeSoundChanges} }, + { more, "Restart sound", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)MakeSoundChanges} }, { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Sample Rate", {&snd_samplerate}, {8.0}, {0.0}, {0.0}, {SampleRates} }, - { discrete, "Buffer Size", {&snd_buffersize}, {11.0}, {0.0}, {0.0}, {BufferSizes} }, + { ediscrete,"Output system", {&snd_output}, {countof(Outputs)}, {0.0}, {0.0}, {(value_t *)Outputs} }, + { ediscrete,"Output format", {&snd_output_format}, {5.0}, {0.0}, {0.0}, {(value_t *)OutputFormats} }, + { ediscrete,"Speaker mode", {&snd_speakermode}, {8.0}, {0.0}, {0.0}, {(value_t *)SpeakerModes} }, + { ediscrete,"Resampler", {&snd_resampler}, {4.0}, {0.0}, {0.0}, {(value_t *)Resamplers} }, + { discrete, "HRTF filter", {&snd_hrtf}, {2.0}, {0.0}, {0.0}, {(value_t *)OnOff} }, + { discrete, "Sample rate", {&snd_samplerate}, {8.0}, {0.0}, {0.0}, {SampleRates} }, + { discrete, "Buffer size", {&snd_buffersize}, {8.0}, {0.0}, {0.0}, {BufferSizes} }, + { discrete, "Buffer count", {&snd_buffercount}, {12.0}, {0.0}, {0.0}, {BufferCounts} }, { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { more, "Advanced Options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)AdvSoundOptions} }, -#ifdef _WIN32 - { more, "Select MIDI Device", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)ChooseMIDI} }, -#endif + { more, "Advanced options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)AdvSoundOptions} }, }; static menu_t SoundMenu = @@ -1195,29 +1261,7 @@ static menu_t SoundMenu = SoundItems, }; -#ifdef _WIN32 -/*======================================= - * - * MIDI Device Menu - * - *=======================================*/ - -EXTERN_CVAR (Int, snd_mididevice) - -static menuitem_t MidiDeviceItems[] = -{ - { discrete, "Device", {&snd_mididevice}, {0.0}, {0.0}, {0.0}, {NULL} }, -}; - -static menu_t MidiDeviceMenu = -{ - "SELECT MIDI DEVICE", - 0, - 1, - 0, - MidiDeviceItems, -}; -#endif +#define MIDI_DEVICE_ITEM 2 /*======================================= * @@ -1498,6 +1542,42 @@ int M_FindCurGUID (const GUID &guid, GUIDName *values, int numvals) return v; } +const char *M_FindCurVal(const char *cur, valueenum_t *values, int numvals) +{ + for (int v = 0; v < numvals; ++v) + { + if (stricmp(values[v].value, cur) == 0) + { + return values[v].name; + } + } + return cur; +} + +const char *M_FindPrevVal(const char *cur, valueenum_t *values, int numvals) +{ + for (int v = 0; v < numvals; ++v) + { + if (stricmp(values[v].value, cur) == 0) + { + return values[v == 0 ? numvals - 1 : v - 1].value; + } + } + return values[0].value; +} + +const char *M_FindNextVal(const char *cur, valueenum_t *values, int numvals) +{ + for (int v = 0; v < numvals; ++v) + { + if (stricmp(values[v].value, cur) == 0) + { + return values[v == numvals - 1 ? 0 : v + 1].value; + } + } + return values[0].value; +} + void M_OptDrawer () { EColorRange color; @@ -1684,6 +1764,16 @@ void M_OptDrawer () } break; + case ediscrete: + { + const char *v; + + value = item->a.cvar->GetGenericRep (CVAR_String); + v = M_FindCurVal(value.String, item->e.enumvalues, (int)item->b.numvalues); + screen->DrawText(ValueColor, CurrentMenu->indent + 14, y, v, DTA_Clean, true, TAG_DONE); + } + break; + case discrete_guid: { int v, vals; @@ -2163,6 +2253,13 @@ void M_OptResponder (event_t *ev) S_Sound (CHAN_VOICE, "menu/change", 1, ATTN_NONE); break; + case ediscrete: + value = item->a.cvar->GetGenericRep(CVAR_String); + value.String = const_cast(M_FindPrevVal(value.String, item->e.enumvalues, (int)item->b.numvalues)); + item->a.cvar->SetGenericRep(value, CVAR_String); + S_Sound (CHAN_VOICE, "menu/change", 1, ATTN_NONE); + break; + case bitmask: { int cur; @@ -2305,6 +2402,13 @@ void M_OptResponder (event_t *ev) S_Sound (CHAN_VOICE, "menu/change", 1, ATTN_NONE); break; + case ediscrete: + value = item->a.cvar->GetGenericRep(CVAR_String); + value.String = const_cast(M_FindNextVal(value.String, item->e.enumvalues, (int)item->b.numvalues)); + item->a.cvar->SetGenericRep(value, CVAR_String); + S_Sound (CHAN_VOICE, "menu/change", 1, ATTN_NONE); + break; + case bitmask: { int cur; @@ -2870,9 +2974,19 @@ CCMD (menu_joystick) JoystickOptions (); } +static void FreeMIDIMenuList() +{ + if (SoundItems[MIDI_DEVICE_ITEM].e.values != NULL) + { + delete[] SoundItems[MIDI_DEVICE_ITEM].e.values; + } +} + static void SoundOptions () { - M_SwitchMenu (&SoundMenu); + I_BuildMIDIMenuList(&SoundItems[MIDI_DEVICE_ITEM].e.values, &SoundItems[MIDI_DEVICE_ITEM].b.min); + atterm(FreeMIDIMenuList); + M_SwitchMenu(&SoundMenu); } CCMD (menu_sound) @@ -2894,22 +3008,6 @@ CCMD (menu_advsound) AdvSoundOptions (); } -#ifdef _WIN32 -static void ChooseMIDI () -{ - I_BuildMIDIMenuList (&MidiDeviceItems[0].e.values, - &MidiDeviceItems[0].b.min); - M_SwitchMenu (&MidiDeviceMenu); -} - -CCMD (menu_mididevice) -{ - M_StartControlPanel (true); - OptionsActive = true; - ChooseMIDI (); -} -#endif - static void MakeSoundChanges (void) { static char snd_reset[] = "snd_reset"; diff --git a/src/mus2midi.cpp b/src/mus2midi.cpp index db2c5e73e..804adc11e 100644 --- a/src/mus2midi.cpp +++ b/src/mus2midi.cpp @@ -93,7 +93,7 @@ static size_t ReadVarLen (const BYTE *buf, int *time_out) return ofs; } -static size_t WriteVarLen (FILE *file, int time) +static size_t WriteVarLen (TArray &file, int time) { long buffer; size_t ofs; @@ -105,7 +105,7 @@ static size_t WriteVarLen (FILE *file, int time) } for (ofs = 0;;) { - fputc (buffer & 0xff, file); + file.Push(BYTE(buffer & 0xff)); if (buffer & 0x80) buffer >>= 8; else @@ -114,7 +114,7 @@ static size_t WriteVarLen (FILE *file, int time) return ofs; } -bool ProduceMIDI (const BYTE *musBuf, FILE *outFile) +bool ProduceMIDI (const BYTE *musBuf, TArray &outFile) { BYTE midStatus, midArgs, mid1, mid2; size_t mus_p, maxmus_p; @@ -125,7 +125,6 @@ bool ProduceMIDI (const BYTE *musBuf, FILE *outFile) BYTE lastVel[16]; SBYTE chanMap[16]; int chanCount; - int dupCount = 0; long trackLen; // Do some validation of the MUS file @@ -136,7 +135,9 @@ bool ProduceMIDI (const BYTE *musBuf, FILE *outFile) return false; // Prep for conversion - fwrite (StaticMIDIhead, 1, sizeof(StaticMIDIhead), outFile); + outFile.Clear(); + outFile.Reserve(sizeof(StaticMIDIhead)); + memcpy(&outFile[0], StaticMIDIhead, sizeof(StaticMIDIhead)); musBuf += LittleShort(musHead->SongStart); maxmus_p = LittleShort(musHead->SongLen); @@ -167,10 +168,10 @@ bool ProduceMIDI (const BYTE *musBuf, FILE *outFile) { // This is the first time this channel has been used, // so sets its volume to 127. - fputc (0, outFile); - fputc (0xB0 | chanCount, outFile); - fputc (7, outFile); - fputc (127, outFile); + outFile.Push(0); + outFile.Push(0xB0 | chanCount); + outFile.Push(7); + outFile.Push(127); chanMap[channel] = chanCount++; if (chanCount == 9) ++chanCount; @@ -237,20 +238,15 @@ bool ProduceMIDI (const BYTE *musBuf, FILE *outFile) WriteVarLen (outFile, deltaTime); - if (midStatus == status) - { - ++dupCount; - fputc (mid1, outFile); - if (!midArgs) - fputc (mid2, outFile); - } - else + if (midStatus != status) { status = midStatus; - fputc (status, outFile); - fputc (mid1, outFile); - if (!midArgs) - fputc (mid2, outFile); + outFile.Push(status); + } + outFile.Push(mid1); + if (midArgs == 0) + { + outFile.Push(mid2); } if (event & 128) { @@ -263,12 +259,20 @@ bool ProduceMIDI (const BYTE *musBuf, FILE *outFile) } // fill in track length - trackLen = ftell (outFile) - 22; - fseek (outFile, 18, SEEK_SET); - fputc ((trackLen >> 24) & 255, outFile); - fputc ((trackLen >> 16) & 255, outFile); - fputc ((trackLen >> 8) & 255, outFile); - fputc (trackLen & 255, outFile); - + trackLen = outFile.Size() - 22; + outFile[18] = BYTE((trackLen >> 24) & 255); + outFile[19] = BYTE((trackLen >> 16) & 255); + outFile[20] = BYTE((trackLen >> 8) & 255); + outFile[21] = BYTE(trackLen & 255); return true; } + +bool ProduceMIDI(const BYTE *musBuf, FILE *outFile) +{ + TArray work; + if (ProduceMIDI(musBuf, work)) + { + return fwrite(&work[0], 1, work.Size(), outFile) == work.Size(); + } + return false; +} diff --git a/src/mus2midi.h b/src/mus2midi.h index 61e32070c..e36da6782 100644 --- a/src/mus2midi.h +++ b/src/mus2midi.h @@ -73,6 +73,7 @@ typedef struct WORD Pad; } MUSHeader; +bool ProduceMIDI (const BYTE *musBuf, TArray &outFile); bool ProduceMIDI (const BYTE *musBuf, FILE *outFile); #endif //__MUS2MIDI_H__ diff --git a/src/sdl/i_main.cpp b/src/sdl/i_main.cpp index e208bae0b..336b303ce 100644 --- a/src/sdl/i_main.cpp +++ b/src/sdl/i_main.cpp @@ -121,6 +121,20 @@ void STACK_ARGS call_terms () } } +//========================================================================== +// +// FinalGC +// +// Collect garbage one last time before exiting. +// +//========================================================================== + +static void FinalGC() +{ + Args = NULL; + GC::FullGC(); +} + static void STACK_ARGS NewFailure () { I_FatalError ("Failed to allocate memory from system heap"); @@ -209,6 +223,7 @@ int main (int argc, char **argv) try { Args = new DArgs(argc, argv); + atterm(FinalGC); /* killough 1/98: diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index f6c243f9c..c89aae99a 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -92,6 +92,11 @@ FMOD_RESULT SPC_CreateCodec(FMOD::System *sys); static int Enum_NumForName(const FEnumList *list, const char *name); static const char *Enum_NameForNum(const FEnumList *list, int num); +static FMOD_RESULT F_CALLBACK Memory_Open(const char *name, int unicode, unsigned int *filesize, void **handle, void **userdata); +static FMOD_RESULT F_CALLBACK Memory_Close(void *handle, void *userdata); +static FMOD_RESULT F_CALLBACK Memory_Read(void *handle, void *buffer, unsigned int sizebytes, unsigned int *bytesread, void *userdata); +static FMOD_RESULT F_CALLBACK Memory_Seek(void *handle, unsigned int pos, void *userdata); + // EXTERNAL DATA DECLARATIONS ---------------------------------------------- EXTERN_CVAR (String, snd_output) @@ -106,15 +111,19 @@ EXTERN_CVAR (Int, snd_channels) ReverbContainer *ForcedEnvironment; CVAR (Int, snd_driver, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Int, snd_buffercount, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Bool, snd_hrtf, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, snd_waterreverb, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (String, snd_resampler, "Linear", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (String, snd_speakermode, "Auto", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (String, snd_output_format, "PCM-16", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (String, snd_midipatchset, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, snd_dspnet, false, 0) // PRIVATE DATA DEFINITIONS ------------------------------------------------ static const ReverbContainer *PrevEnvironment; +static bool ShowedBanner; // The rolloff callback is called during FMOD::Sound::play, so we need this // global variable to contain the sound info during that time for the @@ -164,8 +173,6 @@ static const FEnumList SpeakerModeNames[] = { "1", FMOD_SPEAKERMODE_MONO }, { "2", FMOD_SPEAKERMODE_STEREO }, { "4", FMOD_SPEAKERMODE_QUAD }, - { "Headphones", 9001 }, - { "HRTF", 9001 }, { NULL, 0 } }; @@ -195,6 +202,16 @@ static const FEnumList SoundFormatNames[] = { NULL, 0 } }; +static const char *OpenStateNames[] = +{ + "Ready", + "Loading", + "Error", + "Connecting", + "Buffering", + "Seeking" +}; + // CODE -------------------------------------------------------------------- //========================================================================== @@ -374,6 +391,26 @@ public: return FMOD_OK == Channel->setPosition(pos, FMOD_TIMEUNIT_MS); } + FString GetStats() + { + FString stats; + FMOD_OPENSTATE openstate; + unsigned int percentbuffered; + unsigned int position; + bool starving; + + if (FMOD_OK == Stream->getOpenState(&openstate, &percentbuffered, &starving)) + { + stats = (openstate <= FMOD_OPENSTATE_SEEKING ? OpenStateNames[openstate] : "Unknown state"); + stats.AppendFormat(",%3d%% buffered, %s", percentbuffered, starving ? "Starving" : "Well-fed"); + } + if (Channel != NULL && FMOD_OK == Channel->getPosition(&position, FMOD_TIMEUNIT_MS)) + { + stats.AppendFormat(", %d ms", position); + } + return stats; + } + static FMOD_RESULT F_CALLBACK PCMReadCallback(FMOD_SOUND *sound, void *data, unsigned int datalen) { FMOD_RESULT result; @@ -445,6 +482,8 @@ bool FMODSoundRenderer::Init() FMOD_SOUND_FORMAT format; FMOD_DSP_RESAMPLER resampler; FMOD_INITFLAGS initflags; + int samplerate; + int driver; int eval; @@ -454,7 +493,12 @@ bool FMODSoundRenderer::Init() PausableSfx = NULL; PrevEnvironment = DefaultEnvironments[0]; - Printf ("I_InitSound: Initializing FMOD\n"); + Printf("I_InitSound: Initializing FMOD\n"); + if (!ShowedBanner) + { + Printf("FMOD Sound System, copyright © Firelight Technologies Pty, Ltd., 1994-2007.\n"); + ShowedBanner = true; + } // Create a System object and initialize. result = FMOD::System_Create(&Sys); @@ -472,30 +516,6 @@ bool FMODSoundRenderer::Init() return false; } - result = Sys->getDriverCaps(0, &Driver_Caps, &Driver_MinFrequency, &Driver_MaxFrequency, &speakermode); - ERRCHECK(result); - - // Set the user selected speaker mode. - eval = Enum_NumForName(SpeakerModeNames, snd_speakermode); - if (eval >= 0) - { - speakermode = FMOD_SPEAKERMODE(eval); - } - result = Sys->setSpeakerMode(speakermode < 9000 ? speakermode : FMOD_SPEAKERMODE_STEREO); - ERRCHECK(result); - - // Set software format - eval = Enum_NumForName(SoundFormatNames, snd_output_format); - format = eval >= 0 ? FMOD_SOUND_FORMAT(eval) : FMOD_SOUND_FORMAT_PCM16; - eval = Enum_NumForName(ResamplerNames, snd_resampler); - resampler = eval >= 0 ? FMOD_DSP_RESAMPLER(eval) : FMOD_DSP_RESAMPLER_LINEAR; - result = Sys->setSoftwareFormat(snd_samplerate, format, 0, 0, resampler); - ERRCHECK(result); - - // Set software channels according to snd_channels - result = Sys->setSoftwareChannels(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS); - ERRCHECK(result); - #ifdef _WIN32 if (OSPlatform == os_WinNT4) { @@ -541,18 +561,74 @@ bool FMODSoundRenderer::Init() ERRCHECK(result); } + result = Sys->getNumDrivers(&driver); + if (result == FMOD_OK) + { + if (snd_driver >= driver) + { + Printf (TEXTCOLOR_BLUE"Driver %d does not exist. Using 0.\n", *snd_driver); + driver = 0; + } + else + { + driver = snd_driver; + } + result = Sys->setDriver(driver); + } + result = Sys->getDriver(&driver); + result = Sys->getDriverCaps(driver, &Driver_Caps, &Driver_MinFrequency, &Driver_MaxFrequency, &speakermode); + ERRCHECK(result); + + // Set the user selected speaker mode. + eval = Enum_NumForName(SpeakerModeNames, snd_speakermode); + if (eval >= 0) + { + speakermode = FMOD_SPEAKERMODE(eval); + } + result = Sys->setSpeakerMode(speakermode < 9000 ? speakermode : FMOD_SPEAKERMODE_STEREO); + ERRCHECK(result); + + // Set software format + eval = Enum_NumForName(SoundFormatNames, snd_output_format); + format = eval >= 0 ? FMOD_SOUND_FORMAT(eval) : FMOD_SOUND_FORMAT_PCM16; + eval = Enum_NumForName(ResamplerNames, snd_resampler); + resampler = eval >= 0 ? FMOD_DSP_RESAMPLER(eval) : FMOD_DSP_RESAMPLER_LINEAR; + samplerate = clamp(snd_samplerate, Driver_MinFrequency, Driver_MaxFrequency); + if (samplerate == 0 || snd_samplerate == 0) + { // Creative's ASIO drivers report the only supported frequency as 0! + if (FMOD_OK != Sys->getSoftwareFormat(&samplerate, NULL, NULL, NULL, NULL, NULL)) + { + samplerate = 48000; + } + } + if (samplerate != snd_samplerate && snd_samplerate != 0) + { + Printf(TEXTCOLOR_BLUE"Sample rate %d is unsupported. Trying %d\n", *snd_samplerate, samplerate); + } + result = Sys->setSoftwareFormat(samplerate, format, 0, 0, resampler); + + // Set software channels according to snd_channels + result = Sys->setSoftwareChannels(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS); + ERRCHECK(result); + if (Driver_Caps & FMOD_CAPS_HARDWARE_EMULATED) { // The user has the 'Acceleration' slider set to off! // This is really bad for latency! - Printf ("Warning: The sound acceleration slider has been set to off.\n"); - Printf ("Please turn it back on if you want decent sound.\n"); + Printf (TEXTCOLOR_BLUE"Warning: The sound acceleration slider has been set to off.\n"); + Printf (TEXTCOLOR_BLUE"Please turn it back on if you want decent sound.\n"); result = Sys->setDSPBufferSize(1024, 10); // At 48khz, the latency between issuing an fmod command and hearing it will now be about 213ms. ERRCHECK(result); } + else if (snd_buffersize != 0 || snd_buffercount != 0) + { + int buffersize = snd_buffersize ? snd_buffersize : 1024; + int buffercount = snd_buffercount ? snd_buffercount : 4; + result = Sys->setDSPBufferSize(buffersize, buffercount); + } // Try to init initflags = FMOD_INIT_NORMAL; - if (speakermode > 9000) + if (snd_hrtf) { initflags |= FMOD_INIT_SOFTWARE_HRTF; } @@ -560,17 +636,38 @@ bool FMODSoundRenderer::Init() { initflags |= FMOD_INIT_ENABLE_DSPNET; } - result = Sys->init(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS, initflags, 0); - if (result == FMOD_ERR_OUTPUT_CREATEBUFFER) - { // The speaker mode selected isn't supported by this soundcard. Switch it back to stereo. - result = Sys->setSpeakerMode(FMOD_SPEAKERMODE_STEREO); - ERRCHECK(result); - + for (;;) + { result = Sys->init(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS, initflags, 0); - ERRCHECK(result); + if (result == FMOD_ERR_OUTPUT_CREATEBUFFER) + { // The speaker mode selected isn't supported by this soundcard. Switch it back to stereo. + result = Sys->getSpeakerMode(&speakermode); + if (result == FMOD_OK && FMOD_OK == Sys->setSpeakerMode(FMOD_SPEAKERMODE_STEREO)) + { + continue; + } + } +#ifdef _WIN32 + else if (result == FMOD_ERR_OUTPUT_INIT) + { + FMOD_OUTPUTTYPE output; + result = Sys->getOutput(&output); + if (result == FMOD_OK && output != FMOD_OUTPUTTYPE_DSOUND) + { + Printf(TEXTCOLOR_BLUE" Init failed for output type %s. Retrying with DirectSound.\n", + Enum_NameForNum(OutputNames, output)); + if (FMOD_OK == Sys->setOutput(FMOD_OUTPUTTYPE_DSOUND)) + { + continue; + } + } + } +#endif + break; } if (result != FMOD_OK) { // Initializing FMOD failed. Cry cry. + Printf (" System::init returned error code %d\n", result); return false; } @@ -656,6 +753,8 @@ void FMODSoundRenderer::PrintStatus() int samplerate; int numoutputchannels; int num2d, num3d, total; + unsigned int bufferlength; + int numbuffers; if (FMOD_OK == Sys->getOutput(&output)) { @@ -688,6 +787,10 @@ void FMODSoundRenderer::PrintStatus() Printf (TEXTCOLOR_LIGHTBLUE "Software mixer channels: "TEXTCOLOR_GREEN"%d\n", numoutputchannels); Printf (TEXTCOLOR_LIGHTBLUE "Software mixer resampler: "TEXTCOLOR_GREEN"%s\n", Enum_NameForNum(ResamplerNames, resampler)); } + if (FMOD_OK == Sys->getDSPBufferSize(&bufferlength, &numbuffers)) + { + Printf (TEXTCOLOR_LIGHTBLUE "DSP buffers: "TEXTCOLOR_GREEN"%u samples x %d\n", bufferlength, numbuffers); + } } //========================================================================== @@ -892,6 +995,7 @@ SoundStream *FMODSoundRenderer::OpenStream(const char *filename_or_data, int fla FMOD_MODE mode; FMOD_CREATESOUNDEXINFO exinfo = { sizeof(exinfo), }; FMOD::Sound *stream; + FMOD_RESULT result; mode = FMOD_SOFTWARE | FMOD_2D | FMOD_CREATESTREAM; if (flags & SoundStream::Loop) @@ -905,8 +1009,25 @@ SoundStream *FMODSoundRenderer::OpenStream(const char *filename_or_data, int fla } exinfo.length = length; exinfo.fileoffset = offset; + if ((*snd_midipatchset)[0] != '\0') + { + exinfo.dlsname = snd_midipatchset; + } - if (FMOD_OK == Sys->createSound(filename_or_data, mode, &exinfo, &stream)) + result = Sys->createSound(filename_or_data, mode, &exinfo, &stream); + if (result == FMOD_ERR_FORMAT && exinfo.dlsname != NULL) + { + // FMOD_ERR_FORMAT could refer to either the main sound file or + // to the DLS instrument set. Try again without special DLS + // instruments to see if that lets it succeed. + exinfo.dlsname = NULL; + result = Sys->createSound(filename_or_data, mode, &exinfo, &stream); + if (result == FMOD_OK) + { + Printf("%s is an unsupported format.\n", *snd_midipatchset); + } + } + if (result == FMOD_OK) { return new FMODStreamCapsule(stream, this); } @@ -1080,6 +1201,10 @@ FMOD_MODE FMODSoundRenderer::SetChanHeadSettings(FMOD::Channel *chan, sfxinfo_t if (chan->get3DPanLevel(&old_level) == FMOD_OK && old_level != level) { // Only set it if it's different. chan->set3DPanLevel(level); + if (level < 1) + { // Let the noise come from all speakers, not just the front ones. + chan->setSpeakerMix(1,1,1,1,1,1,1,1); + } } return oldmode; } diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index fb1908fa8..8c4b706d3 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -69,6 +69,7 @@ extern void ChildSigHandler (int signum); #include "i_cd.h" #include "tempfiles.h" #include "templates.h" +#include "stats.h" #include @@ -130,6 +131,11 @@ void MusInfo::TimidityVolumeChanged() { } +FString MusInfo::GetStats() +{ + return "No stats available for this song"; +} + void I_InitMusic (void) { static bool setatterm = false; @@ -226,7 +232,7 @@ void I_UnRegisterSong (void *handle) } } -void *I_RegisterSong (const char *filename, char * musiccache, int offset, int len, int device) +void *I_RegisterSong (const char *filename, char *musiccache, int offset, int len, int device) { FILE *file; MusInfo *info = NULL; @@ -237,7 +243,7 @@ void *I_RegisterSong (const char *filename, char * musiccache, int offset, int l return 0; } - if (offset!=-1) + if (offset != -1) { file = fopen (filename, "rb"); if (file == NULL) @@ -288,14 +294,50 @@ void *I_RegisterSong (const char *filename, char * musiccache, int offset, int l info = NULL; } } - if (info == NULL && (snd_mididevice != -2 || device == 0)) + if (info == NULL && (snd_mididevice >= 0 || device == 0)) { info = new MUSSong2 (file, musiccache, len); } else if (info == NULL && GSnd != NULL) #endif // _WIN32 { - info = new TimiditySong (file, musiccache, len); + if (snd_mididevice == -1) + { + TArray midi; + bool midi_made = false; + + if (file == NULL) + { + midi_made = ProduceMIDI((BYTE *)musiccache, midi); + } + else + { + BYTE *mus = new BYTE[len]; + size_t did_read = fread(mus, 1, len, file); + if (did_read == len) + { + midi_made = ProduceMIDI(mus, midi); + } + fseek(file, -(long)did_read, SEEK_CUR); + delete[] mus; + } + if (midi_made) + { + FILE *f = fopen("latest.mid", "wb"); + fwrite(&midi[0], 1, midi.Size(), f); + fclose(f); + info = new StreamSong((char *)&midi[0], -1, midi.Size()); + if (!info->IsValid()) + { + delete info; + info = NULL; + } + } + } + if (info == NULL) + { + info = new TimiditySong (file, musiccache, len); + } } } } @@ -313,14 +355,17 @@ void *I_RegisterSong (const char *filename, char * musiccache, int offset, int l info = NULL; } } - if (info == NULL && (snd_mididevice != -2 || device == 0)) + else if (info == NULL && (snd_mididevice >= 0 || device == 0)) { info = new MIDISong2 (file, musiccache, len); } else if (info == NULL && GSnd != NULL) #endif // _WIN32 { - info = new TimiditySong (file, musiccache, len); + if (snd_mididevice != -1) + { + info = new TimiditySong (file, musiccache, len); + } } } // Check for RDosPlay raw OPL format @@ -480,3 +525,18 @@ CCMD(testmusicvol) else Printf("Current relative volume is %1.2f\n", relative_volume); } + +//========================================================================== +// +// STAT music +// +//========================================================================== + +ADD_STAT(music) +{ + if (currSong != NULL) + { + return currSong->GetStats(); + } + return "No song playing"; +} diff --git a/src/sound/i_music.h b/src/sound/i_music.h index cf808252f..039e1cf2d 100644 --- a/src/sound/i_music.h +++ b/src/sound/i_music.h @@ -43,7 +43,7 @@ // void I_InitMusic (); void I_ShutdownMusic (); -void I_BuildMIDIMenuList (struct value_s **values, float *numValues); +void I_BuildMIDIMenuList (struct value_t **values, float *numValues); void I_UpdateMusic (); // Volume. diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 6f227ff99..42f896ab4 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -44,6 +44,7 @@ public: virtual bool IsValid () const = 0; virtual bool SetPosition (int order); virtual void Update(); + virtual FString GetStats(); enum EState { @@ -242,6 +243,7 @@ public: bool IsMIDI () const { return false; } bool IsValid () const { return m_Stream != NULL; } bool SetPosition (int order); + FString GetStats(); protected: StreamSong () : m_Stream(NULL), m_LastPos(0) {} diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index 03e2ee3c6..b98337ac4 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -68,12 +68,13 @@ extern HINSTANCE g_hInst; #include "w_wad.h" #include "i_video.h" #include "s_sound.h" +#include "v_text.h" #include "gi.h" #include "doomdef.h" EXTERN_CVAR (Float, snd_sfxvolume) -CVAR (Int, snd_samplerate, 48000, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Int, snd_samplerate, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Int, snd_buffersize, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (String, snd_output, "default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) @@ -124,7 +125,7 @@ void I_InitSound () { delete GSnd; GSnd = NULL; - Printf ("Sound init failed. Using nosound.\n"); + Printf (TEXTCOLOR_RED"Sound init failed. Using nosound.\n"); } I_InitMusic (); snd_sfxvolume.Callback (); @@ -215,3 +216,8 @@ bool SoundStream::SetPosition(int pos) { return false; } + +FString SoundStream::GetStats() +{ + return "No stream stats available."; +} diff --git a/src/sound/i_sound.h b/src/sound/i_sound.h index 35acba411..ad36ce5ba 100644 --- a/src/sound/i_sound.h +++ b/src/sound/i_sound.h @@ -57,6 +57,7 @@ public: virtual bool SetPaused (bool paused) = 0; virtual unsigned int GetPosition () = 0; virtual bool SetPosition (int pos); + virtual FString GetStats(); }; typedef bool (*SoundStreamCallback)(SoundStream *stream, void *buff, int len, void *userdata); diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index ea9bc871f..8d52d9762 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -2,6 +2,7 @@ #include "i_musicinterns.h" #include "c_dispatch.h" #include "i_music.h" +#include "i_system.h" #include "templates.h" #include "v_text.h" @@ -56,19 +57,22 @@ void I_InitMusicWin32 () void I_ShutdownMusicWin32 () { - // I don't know if this is an NT 4.0 bug or an FMOD bug, but if waveout - // is used for sound, and a MIDI is also played, then when I quit, the OS + // Ancient bug a saw on NT 4.0 and an old version of FMOD 3: If waveout + // is used for sound and a MIDI is also played, then when I quit, the OS // tells me a free block was modified after being freed. This is // apparently a synchronization issue between two threads, because if I // put this Sleep here after stopping the music but before shutting down - // the entire sound system, the error does not happen. I don't think it's - // a driver problem, because it happens with both a Vortex 2 and an Audigy. - // Though if their drivers are both based off some common Microsoft sample - // code, I suppose it could be a driver issue. - Sleep (50); + // the entire sound system, the error does not happen. Observed with a + // Vortex 2 (may Aureal rest in peace) and an Audigy (damn you, Creative!). + // I no longer have a system with NT4 drivers, so I don't know if this + // workaround is still needed or not. + if (OSPlatform == os_WinNT4) + { + Sleep(50); + } } -void I_BuildMIDIMenuList (struct value_s **outValues, float *numValues) +void I_BuildMIDIMenuList (struct value_t **outValues, float *numValues) { if (*outValues == NULL) { @@ -79,13 +83,13 @@ void I_BuildMIDIMenuList (struct value_s **outValues, float *numValues) values[0].name = "TiMidity++"; values[0].value = -2.0; + values[1].name = "FMOD"; + values[1].value = -1.0; if (nummididevices > 0) { UINT id; int p; - values[1].name = "MIDI Mapper"; - values[1].value = -1.0; for (id = 0, p = 2; id < nummididevices; ++id) { MIDIOUTCAPS caps; @@ -107,7 +111,7 @@ void I_BuildMIDIMenuList (struct value_s **outValues, float *numValues) } else { - *numValues = 1.f; + *numValues = 2.f; } } } @@ -157,9 +161,9 @@ CCMD (snd_listmididevices) MMRESULT res; PrintMidiDevice (-2, "TiMidity++", 0, 0); + PrintMidiDevice (-1, "FMOD", 0, 0); if (nummididevices != 0) { - PrintMidiDevice (-1, "MIDI Mapper", MOD_MAPPER, 0); for (id = 0; id < nummididevices; ++id) { res = midiOutGetDevCaps (id, &caps, sizeof(caps)); @@ -174,4 +178,39 @@ CCMD (snd_listmididevices) } } } + +#else + +// Everything but Windows uses this code. + +CUSTOM_CVAR(Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (self < -2) + self = -2; + else if (self > -1) + self = -1; +} + +void I_BuildMIDIMenuList (struct value_t **outValues, float *numValues) +{ + if (*outValues == NULL) + { + int count = 1 + nummididevices + (nummididevices > 0); + value_t *values; + + *outValues = values = new value_t[count]; + + values[0].name = "TiMidity++"; + values[0].value = -2.0; + values[1].name = "FMOD"; + values[1].value = -1.0; + *numValues = 2.f; + } +} + +CCMD (snd_listmididevices) +{ + Printf("%s-2. TiMidity++\n", -2 == snd_mididevice ? TEXTCOLOR_BOLD : ""); + Printf("%s-1. FMOD\n", -1 == snd_mididevice ? TEXTCOLOR_BOLD : ""); +} #endif diff --git a/src/sound/music_stream.cpp b/src/sound/music_stream.cpp index 12fc8e60a..e6fdea535 100644 --- a/src/sound/music_stream.cpp +++ b/src/sound/music_stream.cpp @@ -92,3 +92,12 @@ bool StreamSong::SetPosition(int order) return false; } } + +FString StreamSong::GetStats() +{ + if (m_Stream != NULL) + { + return m_Stream->GetStats(); + } + return "No song loaded\n"; +} diff --git a/src/win32/i_main.cpp b/src/win32/i_main.cpp index f29ab2c15..4119debf5 100644 --- a/src/win32/i_main.cpp +++ b/src/win32/i_main.cpp @@ -238,6 +238,20 @@ static void UnWTS (void) } } +//========================================================================== +// +// FinalGC +// +// If this doesn't free everything, the debug CRT will let us know. +// +//========================================================================== + +static void FinalGC() +{ + Args = NULL; + GC::FullGC(); +} + //========================================================================== // // LayoutErrorPane @@ -777,6 +791,7 @@ void DoMain (HINSTANCE hInstance) #endif Args = new DArgs(__argc, __argv); + atterm(FinalGC); // Under XP, get our session ID so we can know when the user changes/locks sessions. // Since we need to remain binary compatible with older versions of Windows, we diff --git a/wadsrc/wadsrc.vcproj b/wadsrc/wadsrc.vcproj index fc24d78ed..d7307cb40 100644 --- a/wadsrc/wadsrc.vcproj +++ b/wadsrc/wadsrc.vcproj @@ -177,15 +177,19 @@ Name="Map Translators" > + +