sound_driver_dsound.cpp

Go to the documentation of this file.
00001 
00005 /* Copyright, 2001 Nevrax Ltd.
00006  *
00007  * This file is part of NEVRAX NEL.
00008  * NEVRAX NEL is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2, or (at your option)
00011  * any later version.
00012 
00013  * NEVRAX NEL is distributed in the hope that it will be useful, but
00014  * WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * General Public License for more details.
00017 
00018  * You should have received a copy of the GNU General Public License
00019  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00020  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00021  * MA 02111-1307, USA.
00022  */
00023 
00024 #include "stddsound.h"
00025 
00026 // The one and only INITGUID
00027 #define INITGUID
00028 
00029 #ifdef DIRECTSOUND_VERSION
00030     #undef DIRECTSOUND_VERSION
00031 #endif
00032 #define DIRECTSOUND_VERSION 0x0800
00033 
00034 #include "../sound_driver.h"
00035 
00036 #include <cmath>
00037 
00038 #include "nel/misc/hierarchical_timer.h"
00039 #include "nel/misc/dynloadlib.h"
00040 #include "sound_driver_dsound.h"
00041 #include "listener_dsound.h"
00042 
00043 
00044 using namespace std;
00045 using namespace NLMISC;
00046 
00047 
00048 namespace NLSOUND {
00049 
00050 CSoundDriverDSound* CSoundDriverDSound::_Instance = NULL;
00051 uint32 CSoundDriverDSound::_TimerPeriod = 100;
00052 HWND CSoundDriverWnd = 0;
00053 
00055 LRESULT NelIOProc(LPSTR lpmmioinfo, UINT uMsg, LONG lParam1, LONG lParam2);
00056 
00057 #ifndef NL_STATIC
00058 
00059 HINSTANCE CSoundDriverDllHandle = 0;
00060 
00061 // ******************************************************************
00062 // The main entry of the DLL. It's used to get a hold of the hModule handle.
00063 BOOL WINAPI DllMain(HANDLE hModule, DWORD /* ul_reason_for_call */, LPVOID /* lpReserved */)
00064 {
00065   CSoundDriverDllHandle = (HINSTANCE) hModule;
00066   return TRUE;
00067 }
00068 
00069 class CSoundDriverDSoundNelLibrary : public NLMISC::INelLibrary {
00070     void onLibraryLoaded(bool /* firstTime */) { }
00071     void onLibraryUnloaded(bool /* lastTime */) { }
00072 };
00073 NLMISC_DECL_PURE_LIB(CSoundDriverDSoundNelLibrary)
00074 
00075 #endif /* #ifndef NL_STATIC */
00076 
00077 
00078 // ******************************************************************
00079 // The event handling procedure of the invisible window created below.
00080 
00081 LRESULT CALLBACK CSoundDriverCreateWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
00082 {
00083     return DefWindowProc(hWnd, message, wParam, lParam);
00084 }
00085 
00086 // ******************************************************************
00087 
00088 #ifdef NL_STATIC
00089 ISoundDriver* createISoundDriverInstanceDSound
00090 #else
00091 __declspec(dllexport) ISoundDriver *NLSOUND_createISoundDriverInstance
00092 #endif
00093     (ISoundDriver::IStringMapperProvider *stringMapper)
00094 {
00095 #ifdef NL_STATIC
00096     HINSTANCE CSoundDriverDllHandle = (HINSTANCE)GetModuleHandle(NULL);
00097 #endif
00098 
00099     static bool Registered = false;
00100 
00101     if (!Registered)
00102     {
00103         // Don't ask me why we have to create a window to do sound!
00104         // echo <your comment> | mail support@microsoft.com -s "F#%@cking window"
00105         WNDCLASS myClass;
00106         myClass.hCursor = LoadCursor( NULL, IDC_ARROW );
00107         myClass.hIcon = NULL;
00108         myClass.lpszMenuName = (LPSTR) NULL;
00109         myClass.lpszClassName = (LPSTR) "CSoundDriver";
00110         myClass.hbrBackground = (HBRUSH)(COLOR_WINDOW);
00111         myClass.hInstance = CSoundDriverDllHandle;
00112         myClass.style = CS_GLOBALCLASS;
00113         myClass.lpfnWndProc = CSoundDriverCreateWindowProc;
00114         myClass.cbClsExtra = 0;
00115         myClass.cbWndExtra = 0;
00116 
00117         if (!RegisterClass(&myClass))
00118         {
00119             nlwarning("Failed to initialize the sound driver (RegisterClass)");
00120             return 0;
00121         }
00122 
00123         Registered = true;
00124     }
00125 
00126     CSoundDriverWnd = CreateWindow((LPSTR) "CSoundDriver", (LPSTR) "CSoundDriver", WS_OVERLAPPEDWINDOW,
00127                                     CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, (HWND) NULL, (HMENU) NULL,
00128                                     CSoundDriverDllHandle, (LPSTR) NULL);
00129 
00130     if (CSoundDriverWnd == NULL)
00131     {
00132         nlwarning("Failed to initialize the sound driver (CreateWindow)");
00133         return 0;
00134     }
00135 
00136 /*  // install the NeL Io routine
00137     LPMMIOPROC ret = mmioInstallIOProc(mmioStringToFOURCC("NEL_", 0), (LPMMIOPROC)NelIOProc, MMIO_INSTALLPROC);
00138     nlassert(ret != 0);
00139 */
00140 
00141     return new CSoundDriverDSound(stringMapper);
00142 }
00143 
00144 // ******************************************************************
00145 
00146 #ifdef NL_STATIC
00147 uint32 interfaceVersionDSound()
00148 #else
00149 __declspec(dllexport) uint32 NLSOUND_interfaceVersion()
00150 #endif
00151 {
00152     return ISoundDriver::InterfaceVersion;
00153 }
00154 
00155 // ******************************************************************
00156 
00157 #ifdef NL_STATIC
00158 void outputProfileDSound
00159 #else
00160 __declspec(dllexport) void NLSOUND_outputProfile
00161 #endif
00162     (string &out)
00163 {
00164     CSoundDriverDSound::instance()->writeProfile(out);
00165 }
00166 
00167 // ******************************************************************
00168 
00169 #ifdef NL_STATIC
00170 ISoundDriver::TDriver getDriverTypeDSound()
00171 #else
00172 __declspec(dllexport) ISoundDriver::TDriver NLSOUND_getDriverType()
00173 #endif
00174 {
00175     return ISoundDriver::DriverDSound;
00176 }
00177 
00178 // ******************************************************************
00179 
00180 
00181 
00182 
00183 // ******************************************************************
00184 
00185 CSoundDriverDSound::CSoundDriverDSound(ISoundDriver::IStringMapperProvider *stringMapper)
00186 : _StringMapper(stringMapper)
00187 {
00188     if ( _Instance == NULL )
00189     {
00190         _Instance = this;
00191 
00192         _DirectSound = NULL;
00193         _PrimaryBuffer = NULL;
00194         _SourceCount = 0;
00195         _TimerID = NULL;
00196 
00197 #if NLSOUND_PROFILE
00198         _TimerIntervalCount = 0;
00199         _TotalTime = 0.0;
00200         _TotalUpdateTime = 0.0;
00201         _UpdateCount = 0;
00202         _UpdateSources = 0;
00203         _UpdateExec = 0;
00204 #endif
00205 
00206     }
00207     else
00208     {
00209         nlerror("Sound driver singleton instanciated twice");
00210     }
00211 }
00212 
00213 
00214 #if EAX_AVAILABLE == 1
00215 
00216 LPKSPROPERTYSET CSoundDriverDSound::createPropertySet(CSourceDSound *source)
00217 {
00218     if (_Sources.empty())
00219         return NULL;
00220 
00221     LPDIRECTSOUND3DBUFFER8 d3dBuffer;
00222     if (source == NULL)
00223         d3dBuffer = (*_Sources.begin())->_3DBuffer;
00224     else
00225     {
00226         d3dBuffer = source->_3DBuffer;
00227     }
00228     LPKSPROPERTYSET propertySet;
00229     d3dBuffer->QueryInterface(IID_IKsPropertySet, (void**) &propertySet);
00230 
00231     // some checking code
00232     {
00233         if (propertySet != 0)
00234         {
00235             char *listenerProperties[] =
00236             {
00237                 "DSPROPERTY_EAXLISTENER_NONE",
00238                 "DSPROPERTY_EAXLISTENER_ALLPARAMETERS",
00239                 "DSPROPERTY_EAXLISTENER_ROOM",
00240                 "DSPROPERTY_EAXLISTENER_ROOMHF",
00241                 "DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR",
00242                 "DSPROPERTY_EAXLISTENER_DECAYTIME",
00243                 "DSPROPERTY_EAXLISTENER_DECAYHFRATIO",
00244                 "DSPROPERTY_EAXLISTENER_REFLECTIONS",
00245                 "DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY",
00246                 "DSPROPERTY_EAXLISTENER_REVERB",
00247                 "DSPROPERTY_EAXLISTENER_REVERBDELAY",
00248                 "DSPROPERTY_EAXLISTENER_ENVIRONMENT",
00249                 "DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE",
00250                 "DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION",
00251                 "DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF",
00252                 "DSPROPERTY_EAXLISTENER_FLAGS"
00253             };
00254             uint i;
00255             for (i=DSPROPERTY_EAXLISTENER_NONE; i<= DSPROPERTY_EAXLISTENER_FLAGS; ++i)
00256             {
00257                 ULONG ulSupport = 0;
00258                 propertySet->QuerySupport(DSPROPSETID_EAX_ListenerProperties, i, &ulSupport);
00259                 if ( (ulSupport&(KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET)) != (KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET) )
00260                 {
00261 //                  nlwarning("CSoundDriverDSound::createPropertySet : listener property %s not supported", listenerProperties[i]);
00262                 }
00263             }
00264 
00265             char *bufferProperties[] =
00266             {
00267                 "DSPROPERTY_EAXBUFFER_NONE",
00268                 "DSPROPERTY_EAXBUFFER_ALLPARAMETERS",
00269                 "DSPROPERTY_EAXBUFFER_DIRECT",
00270                 "DSPROPERTY_EAXBUFFER_DIRECTHF",
00271                 "DSPROPERTY_EAXBUFFER_ROOM",
00272                 "DSPROPERTY_EAXBUFFER_ROOMHF",
00273                 "DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR",
00274                 "DSPROPERTY_EAXBUFFER_OBSTRUCTION",
00275                 "DSPROPERTY_EAXBUFFER_OBSTRUCTIONLFRATIO",
00276                 "DSPROPERTY_EAXBUFFER_OCCLUSION",
00277                 "DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO",
00278                 "DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO",
00279                 "DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF",
00280                 "DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR",
00281                 "DSPROPERTY_EAXBUFFER_FLAGS"
00282             };
00283 
00284             for (i=DSPROPERTY_EAXBUFFER_NONE; i<=DSPROPERTY_EAXBUFFER_FLAGS; ++i)
00285             {
00286                 ULONG ulSupport = 0;
00287                 propertySet->QuerySupport(DSPROPSETID_EAX_BufferProperties, i, &ulSupport);
00288                 if ( (ulSupport&(KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET)) != (KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET) )
00289                 {
00290 //                  nlwarning("CSoundDriverDSound::createPropertySet : buffer property %s not supported", bufferProperties[i]);
00291                 }
00292             }
00293         }
00294         else
00295         {
00296             nlwarning("CSoundDriverDSound::createPropertySet : propertie set not available !");
00297         }
00298     }
00299 
00300     return propertySet;
00301 }
00302 
00303 #endif // EAX_AVAILABLE
00304 
00305 
00306 // ******************************************************************
00307 
00308 class CDeviceDescription
00309 {
00310 public:
00311 
00312     static CDeviceDescription* _List;
00313 
00314     CDeviceDescription(LPGUID guid, const char* descr)
00315     {
00316         _Guid = guid;
00317         _Description = strdup(descr);
00318         _Next = _List;
00319         _List = this;
00320     }
00321 
00322     virtual ~CDeviceDescription()
00323     {
00324         if (_Description)
00325         {
00326             free(_Description);
00327         }
00328         if (_Next)
00329         {
00330             delete _Next;
00331         }
00332     }
00333 
00334     char* _Description;
00335     CDeviceDescription* _Next;
00336     LPGUID _Guid;
00337 };
00338 
00339 CDeviceDescription* CDeviceDescription::_List = 0;
00340 
00341 
00342 BOOL CALLBACK CSoundDriverDSoundEnumCallback(LPGUID guid, LPCSTR description, PCSTR /* module */, LPVOID /* context */)
00343 {
00344     new CDeviceDescription(guid, description);
00345     return TRUE;
00346 }
00347 
00348 // ******************************************************************
00349 
00350 CSoundDriverDSound::~CSoundDriverDSound()
00351 {
00352     nldebug("Destroying DirectSound driver");
00353 
00354     if (_TimerID != NULL)
00355     {
00356         timeKillEvent(_TimerID);
00357         timeEndPeriod(_TimerResolution);
00358     }
00359 
00360 
00361     // Assure that the remaining sources have released all their DSBuffers
00362     // before closing down DirectSound
00363     set<CSourceDSound*>::iterator iter;
00364 
00365     for (iter = _Sources.begin(); iter != _Sources.end(); iter++)
00366     {
00367         (*iter)->release();
00368     }
00369 
00370 
00371     // Assure that the listener has released all resources before closing
00372     // down DirectSound
00373     if (CListenerDSound::instance() != 0)
00374     {
00375         CListenerDSound::instance()->release();
00376     }
00377 
00378 
00379     if (_PrimaryBuffer != NULL)
00380     {
00381         _PrimaryBuffer->Release();
00382         _PrimaryBuffer = NULL;
00383     }
00384 
00385     if (_DirectSound != NULL)
00386     {
00387         _DirectSound->Release();
00388         _DirectSound = NULL;
00389     }
00390 
00391     _Instance = 0;
00392 
00393     // free the enumerated list
00394     if (CDeviceDescription::_List)
00395     {
00396         delete CDeviceDescription::_List;
00397         CDeviceDescription::_List = NULL;
00398     }
00399 }
00400 
00402 // ***todo*** void CSoundDriverDSound::getDevices(std::vector<std::string> &devices) { }
00403 
00405 void CSoundDriverDSound::init(std::string device, ISoundDriver::TSoundOptions options)
00406 {
00407     // list of supported options in this driver
00408     // disable effects if no eax, no buffer streaming
00409     const sint supportedOptions = 
00410         OptionAllowADPCM
00411 #if EAX_AVAILABLE
00412         | OptionEnvironmentEffects
00413 #endif
00414         | OptionSoftwareBuffer
00415         | OptionManualRolloff
00416         | OptionLocalBufferCopy;
00417 
00418     // list of forced options in this driver
00419     // always have local copy
00420     const sint forcedOptions = 
00421         OptionLocalBufferCopy;
00422 
00423     // set the options
00424     _Options = (TSoundOptions)(((sint)options & supportedOptions) | forcedOptions);
00425 
00426     if (FAILED(DirectSoundEnumerate(CSoundDriverDSoundEnumCallback, this)))
00427     {
00428         throw ESoundDriver("Failed to enumerate the DirectSound devices");
00429     }
00430 
00431     // Create a DirectSound object and set the cooperative level.
00432 #if EAX_AVAILABLE
00433     if (getOption(OptionEnvironmentEffects))
00434     {
00435         if (EAXDirectSoundCreate8(NULL, &_DirectSound, NULL) != DS_OK)
00436         {
00437             throw ESoundDriver("Failed to create the DirectSound object from EAX proxy funtion");
00438         }
00439     }
00440     else
00441 #endif
00442     {
00443         if (DirectSoundCreate(NULL, &_DirectSound, NULL) != DS_OK)
00444         {
00445             throw ESoundDriver("Failed to create the DirectSound object");
00446         }
00447     }
00448 
00449 
00450     if (_DirectSound->SetCooperativeLevel(CSoundDriverWnd, DSSCL_PRIORITY) != DS_OK)
00451     {
00452         throw ESoundDriver("Failed to set the cooperative level");
00453     }
00454 
00455 
00456     // Analyse the capabilities of the sound driver/device
00457 
00458     _Caps.dwSize = sizeof(_Caps);
00459 
00460     if (_DirectSound->GetCaps(&_Caps) != DS_OK)
00461     {
00462         throw ESoundDriver("Failed to query the sound device caps");
00463     }
00464 
00465 
00466     // Create primary buffer
00467 
00468     DSBUFFERDESC desc;
00469 
00470     ZeroMemory(&desc, sizeof(DSBUFFERDESC));
00471     desc.dwSize = sizeof(DSBUFFERDESC);
00472 
00473 
00474     // First, try to allocate a 3D hardware buffer.
00475     // If we can't get a 3D hardware buffer, use a 2D hardware buffer.
00476     // As last option, use a 2D software buffer.
00477 
00478     // check if wa can honor eax request
00479     if (countHw3DBuffers() > 10)
00480     {
00481         _UseEAX = getOption(OptionEnvironmentEffects);
00482     }
00483     else
00484     {
00485         // not enougth hardware buffer, can't use eax
00486         _UseEAX = false;
00487     }
00488 
00489     if (countHw3DBuffers() > 0)
00490     {
00491         nldebug("Primary buffer: Allocating 3D buffer in hardware");
00492         desc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_LOCHARDWARE | DSBCAPS_CTRL3D | DSBCAPS_CTRLVOLUME;
00493     }
00494     else
00495     {
00496         nldebug("Primary buffer: Allocating 3D buffer in software");
00497         desc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_LOCSOFTWARE | DSBCAPS_CTRL3D | DSBCAPS_CTRLVOLUME;
00498 //        desc.guid3DAlgorithm = DS3DALG_NO_VIRTUALIZATION;
00499     }
00500 
00501 
00502 
00503     HRESULT res = _DirectSound->CreateSoundBuffer(&desc, &_PrimaryBuffer, NULL);
00504 
00505     if (res != DS_OK && res != DS_NO_VIRTUALIZATION)
00506     {
00507 
00508         nlwarning("Primary buffer: Failed to create a buffer with 3D capabilities.");
00509 
00510         ZeroMemory(&desc, sizeof(DSBUFFERDESC));
00511         desc.dwSize = sizeof(DSBUFFERDESC);
00512 
00513         if (countHw2DBuffers() > 0)
00514         {
00515             nldebug("Primary buffer: Allocating 2D buffer in hardware");
00516             desc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_LOCHARDWARE | DSBCAPS_CTRLVOLUME;
00517         }
00518         else
00519         {
00520             nldebug("Primary buffer: Allocating 2D buffer in software");
00521             desc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_LOCSOFTWARE | DSBCAPS_CTRLVOLUME;
00522         }
00523 
00524         if (_DirectSound->CreateSoundBuffer(&desc, &_PrimaryBuffer, NULL) != DS_OK)
00525         {
00526             throw ESoundDriver("Failed to create the primary buffer");
00527         }
00528     }
00529 
00530 
00531     // Set the format of the primary buffer
00532 
00533     WAVEFORMATEX format;
00534 
00535     format.cbSize = sizeof(WAVEFORMATEX);
00536 
00537     // Make sure the sound card accepts the default settings.
00538     // For now, only the default settings are accepted. Fallback
00539     // strategy will be handled later.
00540 
00541     if ((_Caps.dwMinSecondarySampleRate > 22050) && (22050 > _Caps.dwMaxSecondarySampleRate)) {
00542         throw ESoundDriver("Unsupported sample rate range");
00543     }
00544 
00545     if ((_Caps.dwFlags & DSCAPS_PRIMARY16BIT) == 0) {
00546         throw ESoundDriver("Unsupported sample size [16bits]");
00547     }
00548 
00549     format.wBitsPerSample = 16;
00550     format.nChannels = 1;
00551     format.nSamplesPerSec = 22050;
00552     format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
00553     format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
00554     format.wFormatTag = WAVE_FORMAT_PCM;
00555 
00556 
00557     if (_PrimaryBuffer->SetFormat(&format) != DS_OK)
00558     {
00559         throw ESoundDriver("Failed to create set the format of the primary buffer");
00560     }
00561 
00562     // Fill the buffer with silence
00563 /*  LPVOID ptr;
00564     DWORD bytes;
00565 
00566     HRESULT hr = _PrimaryBuffer->Lock(0, 0, &ptr, &bytes, NULL, NULL, DSBLOCK_ENTIREBUFFER);
00567     if (FAILED(hr))
00568     {
00569         switch (hr)
00570         {
00571         case DSERR_BUFFERLOST:
00572             throw ESoundDriver("Failed to lock the DirectSound primary buffer : DSERR_BUFFERLOST");
00573         case DSERR_INVALIDCALL:
00574             throw ESoundDriver("Failed to lock the DirectSound primary buffer : DSERR_INVALIDCALL");
00575         case DSERR_INVALIDPARAM:
00576             throw ESoundDriver("Failed to lock the DirectSound primary buffer : DSERR_INVALIDPARAM");
00577         case DSERR_PRIOLEVELNEEDED:
00578             throw ESoundDriver("Failed to lock the DirectSound primary buffer : DSERR_PRIOLEVELNEEDED");
00579         default:
00580             throw ESoundDriver("Failed to lock the DirectSound primary buffer : unkown error");
00581 
00582         }
00583     }
00584 
00585     memset(ptr, 0, bytes);
00586 
00587     _PrimaryBuffer->Unlock(ptr, bytes, 0, 0);
00588 */
00589 
00590     uint32 numBuffers = countHw3DBuffers();
00591     if (numBuffers == 0)
00592     {
00593         numBuffers = 31;
00594     }
00595 
00596     /*
00597     _Sources = new CSourceDSound*[numBuffers];
00598 
00599 
00600     for (uint i = 0; i < numBuffers; i++)
00601     {
00602         _Sources[i] = 0;
00603     }
00604 
00605     try
00606     {
00607         for (i = 0; i < numBuffers; i++)
00608         {
00609             _Sources[i] = new CSourceDSound(i);
00610             _Sources[i]->init(_DirectSound);
00611             _SourceCount++;
00612         }
00613     }
00614     catch (ESoundDriver& e)
00615     {
00616         // Okay, here's the situation: I'm listening to WinAmp while debugging.
00617         // The caps told me there were 31 buffers available. In reality, there were
00618         // only 30 available because WinAmp was using one. Somehow DirectSound didn't
00619         // notice. So when creating buffer 31, an exception was thrown.
00620         // If something like this happens, don't bother and go on with the buffers we've
00621         // got. If no buffers are created at all, throw the exception again.
00622 
00623         if (_Sources == 0)
00624         {
00625             throw e;
00626         }
00627     }
00628 
00629 */
00630     
00631     TIMECAPS tcaps;
00632 
00633     timeGetDevCaps(&tcaps, sizeof(TIMECAPS));
00634     _TimerResolution = (tcaps.wPeriodMin > 10)? tcaps.wPeriodMin : 10;
00635     timeBeginPeriod(_TimerResolution);
00636     
00637 #if NLSOUND_PROFILE
00638     for (uint i = 0; i < 1024; i++)
00639     {
00640         _TimerInterval[i] = 0;
00641     }
00642 
00643     _TimerDate = CTime::getPerformanceTime();
00644 #endif
00645     
00646     _TimerID = timeSetEvent(_TimerPeriod, 0, &CSoundDriverDSound::TimerCallback, (DWORD_PTR)this, TIME_CALLBACK_FUNCTION | TIME_PERIODIC);
00647 
00648     if (_TimerID == NULL)
00649     {
00650         throw ESoundDriver("Failed to create the timer");
00651     }
00652 }
00653 
00655 ISoundDriver::TSoundOptions CSoundDriverDSound::getOptions()
00656 {
00657     return _Options;
00658 }
00659 
00661 bool CSoundDriverDSound::getOption(ISoundDriver::TSoundOptions option)
00662 {
00663     return ((uint)_Options & (uint)option) == (uint)option;
00664 }
00665 
00666 // ******************************************************************
00667 
00668 uint CSoundDriverDSound::countMaxSources()
00669 {
00670     // Try the hardware 3d buffers first
00671     uint n = countHw3DBuffers();
00672     if (n > 0)
00673     {
00674         return n;
00675     }
00676 
00677     // If not, try the hardware 2d buffers first
00678     n = countHw2DBuffers();
00679     if (n > 0)
00680     {
00681         return n;
00682     }
00683 
00684     // Okay, we'll use 32 software buffers
00685     return 32;
00686 }
00687 
00688 // ******************************************************************
00689 
00690 void CSoundDriverDSound::writeProfile(string& out)
00691 {
00692     // Write the available sound devices
00693     CDeviceDescription* list = CDeviceDescription::_List;
00694     while (list) {
00695         out += "\t" + string(list->_Description) + "\n";
00696         list = list->_Next;
00697     }
00698 
00699     // Write the buffers sizes
00700     out += "\tBuffer size: " + toString ((int)CSourceDSound::_SecondaryBufferSize) + "\n";
00701     out += "\tCopy size: " + toString ((int)CSourceDSound::_UpdateCopySize) + "\n";
00702     out += "\tSwap size: " + toString ((int)CSourceDSound::_SwapCopySize) + "\n";
00703 
00704     // Write the number of hardware buffers
00705     DSCAPS caps;
00706     caps.dwSize = sizeof(caps);
00707     _DirectSound->GetCaps(&caps);
00708 
00709     out += "\t3d hw buffers: " + toString ((uint32)caps.dwMaxHw3DAllBuffers) + "\n";
00710     out += "\t2d hw buffers: " + toString ((uint32)caps.dwMaxHwMixingAllBuffers) + "\n";
00711 
00712     // Write the number of hardware buffers
00713 #if NLSOUND_PROFILE
00714     out += "\tUpdate time total --- " + toString (getAverageUpdateTime()) + "\n";
00715     out += "\tUpdate time source --- " + toString (CSourceDSound::getAverageUpdateTime()) + "\n";
00716     out += "\tUpdate --- t: " + toString (CSourceDSound::getAverageCumulTime());
00717     out += "\t - p: " + toString (CSourceDSound::getAveragePosTime());
00718     out += "\t - l: " + toString (CSourceDSound::getAverageLockTime());
00719     out += "\t - c: " + toString (CSourceDSound::getAverageCopyTime());
00720     out += "\t - u: " + toString (CSourceDSound::getAverageUnlockTime()) + "\n";
00721     out += "\tUpdate percentage: --- " + toString (getUpdatePercentage()) + "\n";
00722     out += "\tUpdate num sources --- " + toString ((int)getAverageUpdateSources()) + "\n";
00723     out += "\tUpdate byte size --- " + toString (CSourceDSound::getAverageUpdateSize()) + "\n";
00724     out += "\tSwap time --- " + toString (CSourceDSound::getTestAverage()) + "\n";
00725     out += "\tSrc --- " + toString (countPlayingSources()) + "\n";
00726 #endif
00727 }
00728 
00729 
00730 void CALLBACK CSoundDriverDSound::TimerCallback(UINT /* uID */, UINT /* uMsg */, DWORD_PTR dwUser, DWORD_PTR /* dw1 */, DWORD_PTR /* dw2 */)
00731 {
00732     // a little speed check
00733     static NLMISC::TTime lastUpdate = NLMISC::CTime::getLocalTime();
00734     NLMISC::TTime now = NLMISC::CTime::getLocalTime();
00735 
00736     if (now - lastUpdate > _TimerPeriod * 2)
00737     {
00738 //      nlwarning("CSoundDriverDSound::TimerCallback : no update since %u millisec (nominal update = %u", uint32(now-lastUpdate), uint32(_TimerPeriod));
00739     }
00740     else
00741     {
00742 //      nldebug("Callback delay = %u ms", uint32(now-lastUpdate));
00743     }
00744 
00745     lastUpdate = now;
00746 
00747 
00748     CSoundDriverDSound* driver = (CSoundDriverDSound*) dwUser;
00749     driver->update();
00750 }
00751 
00752 // ******************************************************************
00753 
00754 void CSoundDriverDSound::update()
00755 {
00756     H_AUTO(NLSOUND_DSoundUpdate)
00757 #if NLSOUND_PROFILE
00758     TTicks tnow = CTime::getPerformanceTime();
00759 #endif
00760 
00761     NLMISC::TTime now = NLMISC::CTime::getLocalTime();
00762 
00763     set<CSourceDSound*>::iterator first(_Sources.begin()), last(_Sources.end());
00764     for (;first != last; ++first)
00765     {
00766         if ((*first)->needsUpdate())
00767         {
00768             if ((*first)->update())
00769             {
00770 #if NLSOUND_PROFILE
00771                 _UpdateSources++;
00772 #endif
00773             }
00774         }
00775     }
00776 
00777 /*
00778     set<CSourceDSound*>::iterator iter;
00779 
00780     iter = _Sources.begin();
00781 
00782 
00783     if ((iter != _Sources.end()) && (*iter)->needsUpdate())
00784     {
00785         while (iter != _Sources.end())
00786         {
00787 //          if ((*iter)->update2()) {
00788             if ((*iter)->update())
00789             {
00790 #if NLSOUND_PROFILE
00791                 _UpdateSources++;
00792 #endif
00793             }
00794             iter++;
00795         }
00796     }
00797 
00798 */
00799     {
00800         NLMISC::TTime   last = CTime::getLocalTime() - now;
00801         if (last > _TimerPeriod / 2)
00802         {
00803             nlwarning("CSoundDriverDSound::TimerCallback : update took %u millisec", (uint32)last);
00804         }
00805     }
00806 
00807 #if NLSOUND_PROFILE
00808     _TotalUpdateTime += 1000.0 * CTime::ticksToSecond(CTime::getPerformanceTime() - tnow);
00809     _UpdateCount++;
00810 #endif
00811 }
00812 
00813 // ******************************************************************
00814 
00815 uint CSoundDriverDSound::countHw3DBuffers()
00816 {
00817     DSCAPS caps;
00818     caps.dwSize = sizeof(caps);
00819 
00820     if (_DirectSound->GetCaps(&caps) != DS_OK)
00821     {
00822         throw ESoundDriver("Failed to query the sound device caps");
00823     }
00824 
00825     return caps.dwFreeHw3DStreamingBuffers;
00826 }
00827 
00828 // ******************************************************************
00829 
00830 uint CSoundDriverDSound::countHw2DBuffers()
00831 {
00832     DSCAPS caps;
00833     caps.dwSize = sizeof(caps);
00834 
00835     if (_DirectSound->GetCaps(&caps) != DS_OK)
00836     {
00837         throw ESoundDriver("Failed to query the sound device caps");
00838     }
00839 
00840     return caps.dwFreeHwMixingStreamingBuffers;
00841 }
00842 
00843 // ******************************************************************
00844 
00845 IListener *CSoundDriverDSound::createListener()
00846 {
00847     LPDIRECTSOUND3DLISTENER dsoundListener;
00848 
00849     if (CListenerDSound::instance() != NULL)
00850     {
00851         return CListenerDSound::instance();
00852     }
00853 
00854     if (_PrimaryBuffer == 0)
00855     {
00856         throw ESoundDriver("Corrupt driver");
00857     }
00858 
00859     if (FAILED(_PrimaryBuffer->QueryInterface(IID_IDirectSound3DListener, (LPVOID *) &dsoundListener)))
00860     {
00861         nlwarning("The 3D listener interface is not available.");
00862         return new CListenerDSound(NULL);
00863     }
00864 
00865     return new CListenerDSound(dsoundListener);
00866 }
00867 
00868 // ******************************************************************
00869 
00870 IBuffer *CSoundDriverDSound::createBuffer()
00871 {
00872     if (_PrimaryBuffer == 0)
00873     {
00874         throw ESoundDriver("Corrupt driver");
00875     }
00876 
00877 
00878     // FIXME: set buffer ID
00879     return new CBufferDSound();
00880 }
00881 
00882 // ******************************************************************
00883 
00884 void CSoundDriverDSound::removeBuffer(IBuffer * /* buffer */)
00885 {
00886 }
00887 
00889 //bool CSoundDriverDSound::readWavBuffer( IBuffer *destbuffer, const std::string &name, uint8 *wavData, uint dataSize)
00890 //{
00891 //  return ((CBufferDSound*) destbuffer)->readWavBuffer(name, wavData, dataSize);
00892 //}
00893 //
00894 //bool CSoundDriverDSound::readRawBuffer( IBuffer *destbuffer, const std::string &name, uint8 *rawData, uint dataSize, TSampleFormat format, uint32 frequency)
00895 //{
00896 //  return ((CBufferDSound*) destbuffer)->readRawBuffer(name, rawData, dataSize, format, frequency);
00897 //}
00898 
00899 
00900 // ******************************************************************
00901 
00902 ISource *CSoundDriverDSound::createSource()
00903 {
00904     if (_PrimaryBuffer == 0)
00905     {
00906         throw ESoundDriver("Corrupt driver");
00907     }
00908 
00909 
00910     CSourceDSound* src = new CSourceDSound(0);
00911     src->init(_DirectSound, _UseEAX);
00912     _Sources.insert(src);
00913 
00914     return src;
00915 }
00916 
00917 
00918 // ******************************************************************
00919 
00920 void CSoundDriverDSound::removeSource(ISource *source)
00921 {
00922     _Sources.erase((CSourceDSound*) source);
00923 }
00924 
00925 // ******************************************************************
00926 
00927 void CSoundDriverDSound::commit3DChanges()
00928 {
00929     CListenerDSound* listener = CListenerDSound::instance();
00930     listener->commit3DChanges();
00931 
00932 
00933     const CVector &origin = listener->getPos();
00934 
00935     set<CSourceDSound*>::iterator iter;
00936 
00937     // We handle the volume of the source according to the distance
00938     // ourselves. Call updateVolume() to, well..., update the volume
00939     // according to, euh ..., the new distance!
00940     for (iter = _Sources.begin(); iter != _Sources.end(); iter++)
00941     {
00942         if ((*iter)->isPlaying())
00943         {
00944             (*iter)->updateVolume(origin);
00945         }
00946     }
00947 }
00948 
00949 
00950 // ******************************************************************
00951 
00952 uint CSoundDriverDSound::countPlayingSources()
00953 {
00954     uint n = 0;
00955     set<CSourceDSound*>::iterator iter;
00956 
00957     for (iter = _Sources.begin(); iter != _Sources.end(); iter++)
00958     {
00959         if ((*iter)->isPlaying())
00960         {
00961             n++;
00962         }
00963     }
00964 
00965     return n;
00966 }
00967 
00968 
00969 // ******************************************************************
00970 
00971 void CSoundDriverDSound::setGain( float gain )
00972 {
00973     if (_PrimaryBuffer != 0)
00974     {
00975         if (gain < 0.00001f)
00976         {
00977             gain = 0.00001f;
00978         }
00979 
00980         /* convert from linear amplitude to hundredths of decibels */
00981         LONG volume = (LONG)(100.0 * 20.0 * log10(gain));
00982 
00983         if (volume < DSBVOLUME_MIN)
00984         {
00985             volume = DSBVOLUME_MIN;
00986         }
00987         else if (volume > DSBVOLUME_MAX)
00988         {
00989             volume = DSBVOLUME_MAX;
00990         }
00991 
00992         HRESULT hr = _PrimaryBuffer->SetVolume(volume);
00993 
00994         if (hr != DS_OK)
00995         {
00996             nldebug("Failed to set the volume");
00997         }
00998     }
00999 }
01000 
01001 // ******************************************************************
01002 
01003 float CSoundDriverDSound::getGain()
01004 {
01005     if (_PrimaryBuffer != 0)
01006     {
01007         /* convert from hundredths of decibels to linear amplitude */
01008         LONG volume;
01009         HRESULT hr = _PrimaryBuffer->GetVolume(&volume);
01010 
01011         if (hr != DS_OK)
01012         {
01013             nldebug("Failed to get the volume");
01014             return 1.0;
01015         }
01016 
01017         return (float) pow((double)10, (double) volume / 20.0 / 100.0);
01018     }
01019 
01020     return 1.0;
01021 }
01022 
01023 
01024 
01025 #if NLSOUND_PROFILE
01026 
01027 // ******************************************************************
01028 
01029 uint CSoundDriverDSound::countTimerIntervals()
01030 {
01031     return 1024;
01032 }
01033 
01034 // ******************************************************************
01035 
01036 uint CSoundDriverDSound::getTimerIntervals(uint index)
01037 {
01038     return _TimerInterval[index];
01039 }
01040 
01041 // ******************************************************************
01042 
01043 void CSoundDriverDSound::addTimerInterval(uint32 dt)
01044 {
01045     if (_TimerIntervalCount >= 1024)
01046     {
01047         _TimerIntervalCount = 0;
01048     }
01049 
01050     _TimerInterval[_TimerIntervalCount++] = dt;
01051 }
01052 
01053 // ******************************************************************
01054 
01055 double CSoundDriverDSound::getCPULoad()
01056 {
01057     return (_TotalTime > 0.0)? 100.0 * _TotalUpdateTime / _TotalTime : 0.0;
01058 }
01059 
01060 // ******************************************************************
01061 
01062 void CSoundDriverDSound::printDriverInfo(FILE* fp)
01063 {
01064     CDeviceDescription* list = CDeviceDescription::_List;
01065 
01066     while (list) {
01067         fprintf(fp, "%s\n", list->_Description);
01068         list = list->_Next;
01069     }
01070 
01071     fprintf(fp, "\n");
01072 
01073     fprintf(fp, "buffer size: %d\n"
01074                 "copy size: %d\n"
01075                 "swap size: %d\n",
01076             CSourceDSound::_SecondaryBufferSize,
01077             CSourceDSound::_UpdateCopySize,
01078             CSourceDSound::_SwapCopySize);
01079 
01080     fprintf(fp, "\n");
01081 
01082     DSCAPS caps;
01083     caps.dwSize = sizeof(caps);
01084 
01085     if (_DirectSound->GetCaps(&caps) != DS_OK)
01086     {
01087         throw ESoundDriver("Failed to query the sound device caps");
01088     }
01089 
01090 
01091     fprintf(fp, "3d hw buffers: %d\n" "2d hw buffers: %d\n\n", caps.dwMaxHw3DAllBuffers, caps.dwMaxHwMixingAllBuffers);
01092 }
01093 
01094 #endif
01095 
01096 
01097 void    CSoundDriverDSound::startBench()
01098 {
01099     NLMISC::CHTimer::startBench();
01100 }
01101 void    CSoundDriverDSound::endBench()
01102 {
01103     NLMISC::CHTimer::endBench();
01104 }
01105 void    CSoundDriverDSound::displayBench(CLog *log)
01106 {
01107         NLMISC::CHTimer::displayHierarchicalByExecutionPathSorted(log, CHTimer::TotalTime, true, 48, 2);
01108         NLMISC::CHTimer::displayHierarchical(log, true, 48, 2);
01109         NLMISC::CHTimer::displayByExecutionPath(log, CHTimer::TotalTime);
01110         NLMISC::CHTimer::display(log, CHTimer::TotalTime);
01111 }
01112 
01113 } // NLSOUND

Generated on Thu Jan 7 08:26:37 2010 for NeL by  doxygen 1.6.1