Don't restart OAL device when switching EAX

This commit is contained in:
Sergeanur 2021-06-24 14:15:18 +03:00
parent 5156626582
commit b8cf8c53e7
3 changed files with 174 additions and 169 deletions

View File

@ -24,12 +24,6 @@
#include "aldlist.h" #include "aldlist.h"
#ifndef _WIN32
#define _stricmp strcasecmp
#define _strnicmp strncasecmp
#define _strdup strdup
#endif
#ifdef AUDIO_OAL #ifdef AUDIO_OAL
/* /*
* Init call * Init call
@ -47,8 +41,8 @@ ALDeviceList::ALDeviceList()
defaultDeviceIndex = 0; defaultDeviceIndex = 0;
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT")) { if (alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT")) {
devices = (char *)alcGetString(NULL, ALC_DEVICE_SPECIFIER); devices = (char *)alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
defaultDeviceName = (char *)alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); defaultDeviceName = (char *)alcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
index = 0; index = 0;
// go through device list (each device terminated with a single NULL, list terminated with double NULL) // go through device list (each device terminated with a single NULL, list terminated with double NULL)
@ -62,17 +56,11 @@ ALDeviceList::ALDeviceList()
if (context) { if (context) {
alcMakeContextCurrent(context); alcMakeContextCurrent(context);
// if new actual device name isn't already in the list, then add it... // if new actual device name isn't already in the list, then add it...
actualDeviceName = alcGetString(device, ALC_DEVICE_SPECIFIER); actualDeviceName = alcGetString(device, ALC_ALL_DEVICES_SPECIFIER);
bool bNewName = true; if ((actualDeviceName != NULL) && (strlen(actualDeviceName) > 0)) {
for (unsigned int i = 0; i < GetNumDevices(); i++) { ALDEVICEINFO &ALDeviceInfo = aDeviceInfo[nNumOfDevices++];
if (strcmp(GetDeviceName(i), actualDeviceName) == 0) {
bNewName = false;
}
}
if ((bNewName) && (actualDeviceName != NULL) && (strlen(actualDeviceName) > 0)) {
ALDEVICEINFO ALDeviceInfo;
ALDeviceInfo.bSelected = true; ALDeviceInfo.bSelected = true;
ALDeviceInfo.strDeviceName = _strdup(actualDeviceName); ALDeviceInfo.SetName(actualDeviceName);
alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(int), &ALDeviceInfo.iMajorVersion); alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(int), &ALDeviceInfo.iMajorVersion);
alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(int), &ALDeviceInfo.iMinorVersion); alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(int), &ALDeviceInfo.iMinorVersion);
@ -105,8 +93,6 @@ ALDeviceList::ALDeviceList()
// Get Source Count // Get Source Count
ALDeviceInfo.uiSourceCount = GetMaxNumSources(); ALDeviceInfo.uiSourceCount = GetMaxNumSources();
aDeviceInfo[nNumOfDevices++] = ALDeviceInfo;
} }
alcMakeContextCurrent(NULL); alcMakeContextCurrent(NULL);
alcDestroyContext(context); alcDestroyContext(context);

View File

@ -21,7 +21,7 @@ enum
}; };
struct ALDEVICEINFO { struct ALDEVICEINFO {
const char *strDeviceName; char *strDeviceName;
int iMajorVersion; int iMajorVersion;
int iMinorVersion; int iMinorVersion;
unsigned int uiSourceCount; unsigned int uiSourceCount;
@ -33,6 +33,19 @@ struct ALDEVICEINFO {
strDeviceName = NULL; strDeviceName = NULL;
Extensions = 0; Extensions = 0;
} }
~ALDEVICEINFO()
{
delete[] strDeviceName;
strDeviceName = NULL;
}
void SetName(const char *name)
{
if(strDeviceName) delete[] strDeviceName;
strDeviceName = new char[strlen(name) + 1];
strcpy(strDeviceName, name);
}
}; };
typedef ALDEVICEINFO *LPALDEVICEINFO; typedef ALDEVICEINFO *LPALDEVICEINFO;

View File

@ -45,7 +45,6 @@
#endif #endif
//TODO: fix eax3 reverb //TODO: fix eax3 reverb
//TODO: max channels
cSampleManager SampleManager; cSampleManager SampleManager;
bool8 _bSampmanInitialised = FALSE; bool8 _bSampmanInitialised = FALSE;
@ -60,16 +59,18 @@ int usingEAX3=0;
ALCdevice *ALDevice = NULL; ALCdevice *ALDevice = NULL;
ALCcontext *ALContext = NULL; ALCcontext *ALContext = NULL;
unsigned int _maxSamples; unsigned int _maxSamples;
float _fPrevEaxRatioDestination; float _fPrevEaxRatioDestination;
bool _effectsSupported = false;
bool _usingEFX; bool _usingEFX;
float _fEffectsLevel; float _fEffectsLevel;
ALuint ALEffect = AL_EFFECT_NULL; ALuint ALEffect = AL_EFFECT_NULL;
ALuint ALEffectSlot = AL_EFFECTSLOT_NULL; ALuint ALEffectSlot = AL_EFFECTSLOT_NULL;
struct struct
{ {
char id[256]; const char *id;
char name[256]; char name[256];
int sources; int sources;
bool bSupportsFx;
}providers[MAXPROVIDERS]; }providers[MAXPROVIDERS];
int defaultProvider; int defaultProvider;
@ -135,7 +136,7 @@ EAXLISTENERPROPERTIES EAX3Params;
bool IsFXSupported() bool IsFXSupported()
{ {
return usingEAX || usingEAX3 || _usingEFX; return _effectsSupported; // usingEAX || usingEAX3 || _usingEFX;
} }
void EAX_SetAll(const EAXLISTENERPROPERTIES *allparameters) void EAX_SetAll(const EAXLISTENERPROPERTIES *allparameters)
@ -150,21 +151,22 @@ static void
add_providers() add_providers()
{ {
SampleManager.SetNum3DProvidersAvailable(0); SampleManager.SetNum3DProvidersAvailable(0);
ALDeviceList *pDeviceList = NULL; static ALDeviceList DeviceList;
pDeviceList = new ALDeviceList(); ALDeviceList *pDeviceList = &DeviceList;
if ((pDeviceList) && (pDeviceList->GetNumDevices())) if ((pDeviceList) && (pDeviceList->GetNumDevices()))
{ {
const int devNumber = Min(pDeviceList->GetNumDevices(), MAXPROVIDERS); const int devNumber = Min(pDeviceList->GetNumDevices(), MAXPROVIDERS);
int n = 0; int n = 0;
for (int i = 0; i < devNumber; i++) //for (int i = 0; i < devNumber; i++)
int i = pDeviceList->GetDefaultDevice();
{ {
if ( n < MAXPROVIDERS ) if ( n < MAXPROVIDERS )
{ {
strcpy(providers[n].id, pDeviceList->GetDeviceName(i)); providers[n].id = pDeviceList->GetDeviceName(i);
strncpy(providers[n].name, pDeviceList->GetDeviceName(i), sizeof(providers[n].name)); strcpy(providers[n].name, "OPENAL SOFT");
providers[n].sources = pDeviceList->GetMaxNumSources(i); providers[n].sources = pDeviceList->GetMaxNumSources(i);
SampleManager.Set3DProviderName(n, providers[n].name); SampleManager.Set3DProviderName(n, providers[n].name);
n++; n++;
@ -175,23 +177,24 @@ add_providers()
|| pDeviceList->IsExtensionSupported(i, ADEXT_EAX3) || pDeviceList->IsExtensionSupported(i, ADEXT_EAX3)
|| pDeviceList->IsExtensionSupported(i, ADEXT_EAX4) || pDeviceList->IsExtensionSupported(i, ADEXT_EAX4)
|| pDeviceList->IsExtensionSupported(i, ADEXT_EAX5) ) || pDeviceList->IsExtensionSupported(i, ADEXT_EAX5) )
{ {
providers[n - 1].bSupportsFx = true;
if ( n < MAXPROVIDERS ) if ( n < MAXPROVIDERS )
{ {
strcpy(providers[n].id, pDeviceList->GetDeviceName(i)); providers[n].id = pDeviceList->GetDeviceName(i);
strncpy(providers[n].name, pDeviceList->GetDeviceName(i), sizeof(providers[n].name)); strcpy(providers[n].name, "OPENAL SOFT EAX");
strcat(providers[n].name, " EAX");
providers[n].sources = pDeviceList->GetMaxNumSources(i); providers[n].sources = pDeviceList->GetMaxNumSources(i);
providers[n].bSupportsFx = true;
SampleManager.Set3DProviderName(n, providers[n].name); SampleManager.Set3DProviderName(n, providers[n].name);
n++; n++;
} }
if ( n < MAXPROVIDERS ) if ( n < MAXPROVIDERS )
{ {
strcpy(providers[n].id, pDeviceList->GetDeviceName(i)); providers[n].id = pDeviceList->GetDeviceName(i);
strncpy(providers[n].name, pDeviceList->GetDeviceName(i), sizeof(providers[n].name)); strcpy(providers[n].name, "OPENAL SOFT EAX3");
strcat(providers[n].name, " EAX3");
providers[n].sources = pDeviceList->GetMaxNumSources(i); providers[n].sources = pDeviceList->GetMaxNumSources(i);
providers[n].bSupportsFx = true;
SampleManager.Set3DProviderName(n, providers[n].name); SampleManager.Set3DProviderName(n, providers[n].name);
n++; n++;
} }
@ -201,67 +204,30 @@ add_providers()
for(int j=n;j<MAXPROVIDERS;j++) for(int j=n;j<MAXPROVIDERS;j++)
SampleManager.Set3DProviderName(j, NULL); SampleManager.Set3DProviderName(j, NULL);
defaultProvider = pDeviceList->GetDefaultDevice(); // devices are gone now
if ( defaultProvider > MAXPROVIDERS ) //defaultProvider = pDeviceList->GetDefaultDevice();
defaultProvider = 0; //if ( defaultProvider > MAXPROVIDERS )
defaultProvider = 0;
} }
delete pDeviceList;
} }
static void static void
release_existing() release_existing()
{ {
for ( int32 i = 0; i < NUM_CHANNELS; i++ )
aChannel[i].Term();
if ( IsFXSupported() ) if ( IsFXSupported() )
{ {
if ( alIsEffect(ALEffect) ) if ( alIsEffect(ALEffect) )
{ {
alEffecti(ALEffect, AL_EFFECT_TYPE, AL_EFFECT_NULL); alEffecti(ALEffect, AL_EFFECT_TYPE, AL_EFFECT_NULL);
alDeleteEffects(1, &ALEffect);
ALEffect = AL_EFFECT_NULL;
} }
if (alIsAuxiliaryEffectSlot(ALEffectSlot)) if (alIsAuxiliaryEffectSlot(ALEffectSlot))
{ {
alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL); alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
alDeleteAuxiliaryEffectSlots(1, &ALEffectSlot);
ALEffectSlot = AL_EFFECTSLOT_NULL;
} }
} }
for ( int32 i = 0; i < MAX_STREAMS; i++ )
{
CStream *stream = aStream[i];
if (stream)
stream->ProviderTerm();
alDeleteBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]);
}
alDeleteSources(MAX_STREAMS*2, ALStreamSources[0]);
CChannel::DestroyChannels();
if ( ALContext )
{
alcMakeContextCurrent(NULL);
alcSuspendContext(ALContext);
alcDestroyContext(ALContext);
}
if ( ALDevice )
alcCloseDevice(ALDevice);
ALDevice = NULL;
ALContext = NULL;
_fPrevEaxRatioDestination = 0.0f;
_usingEFX = false;
_fEffectsLevel = 0.0f;
DEV("release_existing()\n"); DEV("release_existing()\n");
} }
@ -279,62 +245,6 @@ set_new_provider(int index)
{ {
DEV("set_new_provider()\n"); DEV("set_new_provider()\n");
//TODO:
_maxSamples = MAXCHANNELS;
ALCint attr[] = {ALC_FREQUENCY,MAX_FREQ,
ALC_MONO_SOURCES, MAX_DIGITAL_MIXER_CHANNELS - MAX2DCHANNELS,
ALC_STEREO_SOURCES, MAX2DCHANNELS,
0,
};
ALDevice = alcOpenDevice(providers[index].id);
ASSERT(ALDevice != NULL);
ALContext = alcCreateContext(ALDevice, attr);
ASSERT(ALContext != NULL);
alcMakeContextCurrent(ALContext);
const char* ext=(const char*)alGetString(AL_EXTENSIONS);
ASSERT(strstr(ext,"AL_SOFT_loop_points")!=NULL);
if ( strstr(ext,"AL_SOFT_loop_points")==NULL )
{
curprovider=-1;
release_existing();
return FALSE;
}
alListenerf (AL_GAIN, 1.0f);
alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f);
alListener3f(AL_VELOCITY, 0.0f, 0.0f, 0.0f);
ALfloat orientation[6] = { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f };
alListenerfv(AL_ORIENTATION, orientation);
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
if ( alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) )
{
alGenAuxiliaryEffectSlots(1, &ALEffectSlot);
alGenEffects(1, &ALEffect);
}
alGenSources(MAX_STREAMS*2, ALStreamSources[0]);
for ( int32 i = 0; i < MAX_STREAMS; i++ )
{
alGenBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]);
alSourcei(ALStreamSources[i][0], AL_SOURCE_RELATIVE, AL_TRUE);
alSource3f(ALStreamSources[i][0], AL_POSITION, 0.0f, 0.0f, 0.0f);
alSourcef(ALStreamSources[i][0], AL_GAIN, 1.0f);
alSourcei(ALStreamSources[i][1], AL_SOURCE_RELATIVE, AL_TRUE);
alSource3f(ALStreamSources[i][1], AL_POSITION, 0.0f, 0.0f, 0.0f);
alSourcef(ALStreamSources[i][1], AL_GAIN, 1.0f);
CStream *stream = aStream[i];
if (stream)
stream->ProviderInit();
}
usingEAX = 0; usingEAX = 0;
usingEAX3 = 0; usingEAX3 = 0;
_usingEFX = false; _usingEFX = false;
@ -342,16 +252,16 @@ set_new_provider(int index)
if ( !strcmp(&providers[index].name[strlen(providers[index].name) - strlen(" EAX3")], " EAX3") if ( !strcmp(&providers[index].name[strlen(providers[index].name) - strlen(" EAX3")], " EAX3")
&& alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) ) && alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) )
{ {
EAX_SetAll(&FinishEAX3);
usingEAX = 1; usingEAX = 1;
usingEAX3 = 1; usingEAX3 = 1;
alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect);
EAX_SetAll(&FinishEAX3);
DEV("EAX3\n"); DEV("EAX3\n");
} }
else if ( alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) ) else if ( alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) )
{ {
EAX_SetAll(&EAX30_ORIGINAL_PRESETS[EAX_ENVIRONMENT_CAVE]);
if ( !strcmp(&providers[index].name[strlen(providers[index].name) - strlen(" EAX")], " EAX")) if ( !strcmp(&providers[index].name[strlen(providers[index].name) - strlen(" EAX")], " EAX"))
{ {
@ -363,23 +273,14 @@ set_new_provider(int index)
_usingEFX = true; _usingEFX = true;
DEV("EFX\n"); DEV("EFX\n");
} }
alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect);
EAX_SetAll(&EAX30_ORIGINAL_PRESETS[EAX_ENVIRONMENT_CAVE]);
} }
//SampleManager.SetSpeakerConfig(speaker_type); //SampleManager.SetSpeakerConfig(speaker_type);
CChannel::InitChannels();
for ( int32 i = 0; i < MAXCHANNELS; i++ )
aChannel[i].Init(i);
for ( int32 i = 0; i < MAX2DCHANNELS; i++ )
aChannel[MAXCHANNELS+i].Init(MAXCHANNELS+i, true);
if ( IsFXSupported() ) if ( IsFXSupported() )
{ {
/**/
alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect);
/**/
for ( int32 i = 0; i < MAXCHANNELS; i++ ) for ( int32 i = 0; i < MAXCHANNELS; i++ )
aChannel[i].SetReverbMix(ALEffectSlot, 0.0f); aChannel[i].SetReverbMix(ALEffectSlot, 0.0f);
} }
@ -895,21 +796,12 @@ cSampleManager::IsMP3RadioChannelAvailable(void)
void cSampleManager::ReleaseDigitalHandle(void) void cSampleManager::ReleaseDigitalHandle(void)
{ {
if ( ALDevice ) // TODO? alcSuspendContext
{
prevprovider = curprovider;
release_existing();
curprovider = -1;
}
} }
void cSampleManager::ReacquireDigitalHandle(void) void cSampleManager::ReacquireDigitalHandle(void)
{ {
if ( ALDevice ) // TODO? alcProcessContext
{
if ( prevprovider != -1 )
set_new_provider(prevprovider);
}
} }
bool8 bool8
@ -926,7 +818,7 @@ cSampleManager::Initialise(void)
{ {
m_aSamples[i].nOffset = 0; m_aSamples[i].nOffset = 0;
m_aSamples[i].nSize = 0; m_aSamples[i].nSize = 0;
m_aSamples[i].nFrequency = MAX_FREQ; m_aSamples[i].nFrequency = 22050;
m_aSamples[i].nLoopStart = 0; m_aSamples[i].nLoopStart = 0;
m_aSamples[i].nLoopEnd = -1; m_aSamples[i].nLoopEnd = -1;
} }
@ -982,13 +874,84 @@ cSampleManager::Initialise(void)
for ( int32 i = 0; i < NUM_CHANNELS; i++ ) for ( int32 i = 0; i < NUM_CHANNELS; i++ )
nChannelVolume[i] = 0; nChannelVolume[i] = 0;
} }
add_providers();
{
int index = 0;
_maxSamples = Min(MAXCHANNELS, providers[index].sources);
ALCint attr[] = {ALC_FREQUENCY,MAX_FREQ,
ALC_MONO_SOURCES, MAX_DIGITAL_MIXER_CHANNELS - MAX2DCHANNELS,
ALC_STEREO_SOURCES, MAX2DCHANNELS,
0,
};
ALDevice = alcOpenDevice(providers[index].id);
ASSERT(ALDevice != NULL);
ALContext = alcCreateContext(ALDevice, attr);
ASSERT(ALContext != NULL);
alcMakeContextCurrent(ALContext);
const char* ext=(const char*)alGetString(AL_EXTENSIONS);
ASSERT(strstr(ext,"AL_SOFT_loop_points")!=NULL);
if ( strstr(ext,"AL_SOFT_loop_points")==NULL )
{
Terminate();
return FALSE;
}
alListenerf (AL_GAIN, 1.0f);
alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f);
alListener3f(AL_VELOCITY, 0.0f, 0.0f, 0.0f);
ALfloat orientation[6] = { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f };
alListenerfv(AL_ORIENTATION, orientation);
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
if ( alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) )
{
_effectsSupported = providers[index].bSupportsFx;
alGenAuxiliaryEffectSlots(1, &ALEffectSlot);
alGenEffects(1, &ALEffect);
}
alGenSources(MAX_STREAMS*2, ALStreamSources[0]);
for ( int32 i = 0; i < MAX_STREAMS; i++ )
{
alGenBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]);
alSourcei(ALStreamSources[i][0], AL_SOURCE_RELATIVE, AL_TRUE);
alSource3f(ALStreamSources[i][0], AL_POSITION, 0.0f, 0.0f, 0.0f);
alSourcef(ALStreamSources[i][0], AL_GAIN, 1.0f);
alSourcei(ALStreamSources[i][1], AL_SOURCE_RELATIVE, AL_TRUE);
alSource3f(ALStreamSources[i][1], AL_POSITION, 0.0f, 0.0f, 0.0f);
alSourcef(ALStreamSources[i][1], AL_GAIN, 1.0f);
}
CChannel::InitChannels();
for ( int32 i = 0; i < MAXCHANNELS; i++ )
aChannel[i].Init(i);
for ( int32 i = 0; i < MAX2DCHANNELS; i++ )
aChannel[MAXCHANNELS+i].Init(MAXCHANNELS+i, true);
if ( IsFXSupported() )
{
/**/
alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect);
/**/
for ( int32 i = 0; i < MAXCHANNELS; i++ )
aChannel[i].SetReverbMix(ALEffectSlot, 0.0f);
}
}
{ {
for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ ) for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ )
nStreamLength[i] = 0; nStreamLength[i] = 0;
} }
add_providers();
#ifdef AUDIO_CACHE #ifdef AUDIO_CACHE
FILE *cacheFile = fcaseopen("audio\\sound.cache", "rb"); FILE *cacheFile = fcaseopen("audio\\sound.cache", "rb");
@ -1149,8 +1112,51 @@ cSampleManager::Terminate(void)
} }
} }
release_existing(); for ( int32 i = 0; i < NUM_CHANNELS; i++ )
aChannel[i].Term();
if ( IsFXSupported() )
{
if ( alIsEffect(ALEffect) )
{
alEffecti(ALEffect, AL_EFFECT_TYPE, AL_EFFECT_NULL);
alDeleteEffects(1, &ALEffect);
ALEffect = AL_EFFECT_NULL;
}
if (alIsAuxiliaryEffectSlot(ALEffectSlot))
{
alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
alDeleteAuxiliaryEffectSlots(1, &ALEffectSlot);
ALEffectSlot = AL_EFFECTSLOT_NULL;
}
}
for ( int32 i = 0; i < MAX_STREAMS; i++ )
{
alDeleteBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]);
}
alDeleteSources(MAX_STREAMS*2, ALStreamSources[0]);
CChannel::DestroyChannels();
if ( ALContext )
{
alcMakeContextCurrent(NULL);
alcSuspendContext(ALContext);
alcDestroyContext(ALContext);
}
if ( ALDevice )
alcCloseDevice(ALDevice);
ALDevice = NULL;
ALContext = NULL;
_fPrevEaxRatioDestination = 0.0f;
_usingEFX = false;
_fEffectsLevel = 0.0f;
_DeleteMP3Entries(); _DeleteMP3Entries();
CStream::Terminate(); CStream::Terminate();