00001
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #define INITGUID
00029
00030 #include "stddsound.h"
00031
00032
00033 #include "sound_driver_dsound.h"
00034 #include "listener_dsound.h"
00035 #include <math.h>
00036
00037
00038 using namespace std;
00039 using namespace NLMISC;
00040
00041
00042 namespace NLSOUND {
00043
00044 CSoundDriverDSound* CSoundDriverDSound::_Instance = NULL;
00045 uint32 CSoundDriverDSound::_TimerPeriod = 100;
00046 HINSTANCE CSoundDriverDllHandle = 0;
00047 HWND CSoundDriverWnd = 0;
00048
00049
00050
00051
00052
00053 BOOL WINAPI DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
00054 {
00055 CSoundDriverDllHandle = (HINSTANCE) hModule;
00056 return TRUE;
00057 }
00058
00059
00060
00061
00062
00063 long FAR PASCAL CSoundDriverCreateWindowProc(HWND hWnd, unsigned message, WPARAM wParam, LPARAM lParam)
00064 {
00065 return DefWindowProc(hWnd, message, wParam, lParam);
00066 }
00067
00068
00069
00070 __declspec(dllexport) ISoundDriver *NLSOUND_createISoundDriverInstance()
00071 {
00072 static bool Registered = false;
00073
00074 if (!Registered)
00075 {
00076
00077
00078 WNDCLASS myClass;
00079 myClass.hCursor = LoadCursor( NULL, IDC_ARROW );
00080 myClass.hIcon = NULL;
00081 myClass.lpszMenuName = (LPSTR) NULL;
00082 myClass.lpszClassName = (LPSTR) "CSoundDriver";
00083 myClass.hbrBackground = (HBRUSH)(COLOR_WINDOW);
00084 myClass.hInstance = CSoundDriverDllHandle;
00085 myClass.style = CS_GLOBALCLASS;
00086 myClass.lpfnWndProc = CSoundDriverCreateWindowProc;
00087 myClass.cbClsExtra = 0;
00088 myClass.cbWndExtra = 0;
00089
00090 if (!RegisterClass(&myClass))
00091 {
00092 nlwarning("Failed to initialize the sound driver (RegisterClass)");
00093 return 0;
00094 }
00095
00096 Registered = true;
00097 }
00098
00099 CSoundDriverWnd = CreateWindow((LPSTR) "CSoundDriver", (LPSTR) "CSoundDriver", WS_OVERLAPPEDWINDOW,
00100 CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, (HWND) NULL, (HMENU) NULL,
00101 CSoundDriverDllHandle, (LPSTR) NULL);
00102
00103 if (CSoundDriverWnd == NULL)
00104 {
00105 nlwarning("Failed to initialize the sound driver (CreateWindow)");
00106 return 0;
00107 }
00108
00109 CSoundDriverDSound *driver = new CSoundDriverDSound();
00110 driver->init(CSoundDriverWnd);
00111
00112 return driver;
00113 }
00114
00115
00116
00117 __declspec(dllexport) uint32 NLSOUND_interfaceVersion()
00118 {
00119 return ISoundDriver::InterfaceVersion;
00120 }
00121
00122
00123
00124 __declspec(dllexport) void NLSOUND_outputProfile(ostream &out)
00125 {
00126 CSoundDriverDSound::instance()->writeProfile(out);
00127 }
00128
00129
00130
00131
00132
00133
00134
00135 CSoundDriverDSound::CSoundDriverDSound() : ISoundDriver()
00136 {
00137 if ( _Instance == NULL )
00138 {
00139 _Instance = this;
00140
00141 _DirectSound = NULL;
00142 _PrimaryBuffer = NULL;
00143 _SourceCount = 0;
00144 _TimerID = NULL;
00145
00146 #if NLSOUND_PROFILE
00147 _TimerIntervalCount = 0;
00148 _TotalTime = 0.0;
00149 _TotalUpdateTime = 0.0;
00150 _UpdateCount = 0;
00151 _UpdateSources = 0;
00152 _UpdateExec = 0;
00153 #endif
00154
00155 }
00156 else
00157 {
00158 nlerror("Sound driver singleton instanciated twice");
00159 }
00160 }
00161
00162
00163
00164 CSoundDriverDSound::~CSoundDriverDSound()
00165 {
00166 nldebug("Destroying DirectSound driver");
00167
00168 if (_TimerID != NULL)
00169 {
00170 timeKillEvent(_TimerID);
00171 timeEndPeriod(_TimerResolution);
00172 }
00173
00174
00175
00176
00177 set<CSourceDSound*>::iterator iter;
00178
00179 for (iter = _Sources.begin(); iter != _Sources.end(); iter++)
00180 {
00181 (*iter)->release();
00182 }
00183
00184
00185
00186
00187 if (CListenerDSound::instance() != 0)
00188 {
00189 CListenerDSound::instance()->release();
00190 }
00191
00192
00193 if (_PrimaryBuffer != NULL)
00194 {
00195 _PrimaryBuffer->Release();
00196 _PrimaryBuffer = NULL;
00197 }
00198
00199 if (_DirectSound != NULL)
00200 {
00201 _DirectSound->Release();
00202 _DirectSound = NULL;
00203 }
00204
00205 _Instance = 0;
00206 }
00207
00208
00209
00210 class CDeviceDescription
00211 {
00212 public:
00213
00214 static CDeviceDescription* _List;
00215
00216 CDeviceDescription(LPGUID guid, const char* descr)
00217 {
00218 _Guid = guid;
00219 _Description = strdup(descr);
00220 _Next = _List;
00221 _List = this;
00222 }
00223
00224 virtual ~CDeviceDescription()
00225 {
00226 if (_Description)
00227 {
00228 free(_Description);
00229 }
00230 if (_Next)
00231 {
00232 delete _Next;
00233 }
00234 }
00235
00236 char* _Description;
00237 CDeviceDescription* _Next;
00238 LPGUID _Guid;
00239 };
00240
00241 CDeviceDescription* CDeviceDescription::_List = 0;
00242
00243
00244 BOOL CALLBACK CSoundDriverDSoundEnumCallback(LPGUID guid, LPCSTR description, PCSTR module, LPVOID context)
00245 {
00246 new CDeviceDescription(guid, description);
00247 return TRUE;
00248 }
00249
00250
00251
00252 bool CSoundDriverDSound::init(HWND wnd)
00253 {
00254 if (FAILED(DirectSoundEnumerate(CSoundDriverDSoundEnumCallback, this)))
00255 {
00256 throw ESoundDriver("Failed to enumerate the DirectSound devices");
00257 }
00258
00259
00260
00261 if (DirectSoundCreate(NULL, &_DirectSound, NULL) != DS_OK)
00262 {
00263 throw ESoundDriver("Failed to create the DirectSound object");
00264 }
00265
00266
00267 if (_DirectSound->SetCooperativeLevel(wnd, DSSCL_PRIORITY) != DS_OK)
00268 {
00269 throw ESoundDriver("Failed to set the cooperative level");
00270 }
00271
00272
00273
00274
00275 _Caps.dwSize = sizeof(_Caps);
00276
00277 if (_DirectSound->GetCaps(&_Caps) != DS_OK)
00278 {
00279 throw ESoundDriver("Failed to query the sound device caps");
00280 }
00281
00282
00283
00284
00285 DSBUFFERDESC desc;
00286
00287 ZeroMemory(&desc, sizeof(DSBUFFERDESC));
00288 desc.dwSize = sizeof(DSBUFFERDESC);
00289
00290
00291
00292
00293
00294
00295 if (countHw3DBuffers() > 0)
00296 {
00297 nldebug("Primary buffer: Allocating 3D buffer in hardware");
00298 desc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_LOCHARDWARE | DSBCAPS_CTRL3D | DSBCAPS_CTRLVOLUME;
00299 }
00300 else
00301 {
00302 nldebug("Primary buffer: Allocating 3D buffer in software");
00303 desc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_LOCSOFTWARE | DSBCAPS_CTRL3D | DSBCAPS_CTRLVOLUME;
00304 desc.guid3DAlgorithm = DS3DALG_NO_VIRTUALIZATION;
00305 }
00306
00307
00308 if (_DirectSound->CreateSoundBuffer(&desc, &_PrimaryBuffer, NULL) != DS_OK)
00309 {
00310
00311 nlwarning("Primary buffer: Failed to create a buffer with 3D capabilities.");
00312
00313 ZeroMemory(&desc, sizeof(DSBUFFERDESC));
00314 desc.dwSize = sizeof(DSBUFFERDESC);
00315
00316 if (countHw2DBuffers() > 0)
00317 {
00318 nldebug("Primary buffer: Allocating 2D buffer in hardware");
00319 desc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_LOCHARDWARE | DSBCAPS_CTRLVOLUME;
00320 }
00321 else
00322 {
00323 nldebug("Primary buffer: Allocating 2D buffer in software");
00324 desc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_LOCSOFTWARE | DSBCAPS_CTRLVOLUME;
00325 }
00326
00327 if (_DirectSound->CreateSoundBuffer(&desc, &_PrimaryBuffer, NULL) != DS_OK)
00328 {
00329 throw ESoundDriver("Failed to create the primary buffer");
00330 }
00331 }
00332
00333
00334
00335
00336 WAVEFORMATEX format;
00337
00338 format.cbSize = sizeof(WAVEFORMATEX);
00339
00340
00341
00342
00343
00344 if ((_Caps.dwMinSecondarySampleRate > 22050) && (22050 > _Caps.dwMaxSecondarySampleRate)) {
00345 throw ESoundDriver("Unsupported sample rate range");
00346 }
00347
00348 if ((_Caps.dwFlags & DSCAPS_PRIMARY16BIT) == 0) {
00349 throw ESoundDriver("Unsupported sample size [16bits]");
00350 }
00351
00352 format.wBitsPerSample = 16;
00353 format.nChannels = 1;
00354 format.nSamplesPerSec = 22050;
00355 format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
00356 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
00357 format.wFormatTag = WAVE_FORMAT_PCM;
00358
00359
00360 if (_PrimaryBuffer->SetFormat(&format) != DS_OK)
00361 {
00362 throw ESoundDriver("Failed to create set the format of the primary buffer");
00363 }
00364
00365
00366 uint32 numBuffers = countHw3DBuffers();
00367 if (numBuffers == 0)
00368 {
00369 numBuffers = 31;
00370 }
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408 TIMECAPS tcaps;
00409
00410 timeGetDevCaps(&tcaps, sizeof(TIMECAPS));
00411 _TimerResolution = (tcaps.wPeriodMin > 10)? tcaps.wPeriodMin : 10;
00412 timeBeginPeriod(_TimerResolution);
00413
00414
00415
00416 #if NLSOUND_PROFILE
00417 for (uint i = 0; i < 1024; i++)
00418 {
00419 _TimerInterval[i] = 0;
00420 }
00421
00422 _TimerDate = CTime::getPerformanceTime();
00423 #endif
00424
00425
00426
00427
00428 _TimerID = timeSetEvent(_TimerPeriod, 0, &CSoundDriverDSound::TimerCallback, (DWORD)this, TIME_CALLBACK_FUNCTION | TIME_PERIODIC);
00429
00430 if (_TimerID == NULL)
00431 {
00432 throw ESoundDriver("Failed to create the timer");
00433 }
00434
00435
00436
00437
00438 return true;
00439 }
00440
00441
00442
00443 uint CSoundDriverDSound::countMaxSources()
00444 {
00445
00446 uint n = countHw3DBuffers();
00447 if (n > 0)
00448 {
00449 return n;
00450 }
00451
00452
00453 n = countHw2DBuffers();
00454 if (n > 0)
00455 {
00456 return n;
00457 }
00458
00459
00460 return 32;
00461 }
00462
00463
00464
00465 void CSoundDriverDSound::writeProfile(ostream& out)
00466 {
00467
00468 CDeviceDescription* list = CDeviceDescription::_List;
00469 while (list) {
00470 out << list->_Description << "\n";
00471 list = list->_Next;
00472 }
00473
00474 out << "\n";
00475
00476
00477 out << "buffer size: " << CSourceDSound::_SecondaryBufferSize << "\n";
00478 out << "copy size: " << CSourceDSound::_UpdateCopySize << "\n";
00479 out << "swap size: " << CSourceDSound::_SwapCopySize << "\n";
00480 out << "\n";
00481
00482
00483 DSCAPS caps;
00484 caps.dwSize = sizeof(caps);
00485 _DirectSound->GetCaps(&caps);
00486
00487 cout << "3d hw buffers: " << caps.dwMaxHw3DAllBuffers << "\n";
00488 cout << "2d hw buffers: " << caps.dwMaxHwMixingAllBuffers << "\n";
00489 out << "\n";
00490
00491
00492 #if NLSOUND_PROFILE
00493 out << "update time total --- " << getAverageUpdateTime()<< "\n";
00494 out << "update time source --- " << CSourceDSound::getAverageUpdateTime() << "\n";
00495 out << "update --- t: " << CSourceDSound::getAverageCumulTime();
00496 out << " - p: " << CSourceDSound::getAveragePosTime();
00497 out << " - l: " << CSourceDSound::getAverageLockTime();
00498 out << " - c: " << CSourceDSound::getAverageCopyTime();
00499 out << " - u: " << CSourceDSound::getAverageUnlockTime() << "\n";
00500 out << "update percentage: --- " << getUpdatePercentage() << "\n";
00501 out << "update num sources --- " << getAverageUpdateSources() << "\n";
00502 out << "update byte size --- " << CSourceDSound::getAverageUpdateSize() << "\n";
00503 out << "swap time --- " << CSourceDSound::getTestAverage() << "\n";
00504 out << "src --- " << countPlayingSources() << "\n";
00505 #endif
00506 }
00507
00508
00509 void CALLBACK CSoundDriverDSound::TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
00510 {
00511 CSoundDriverDSound* driver = (CSoundDriverDSound*) dwUser;
00512 driver->update();
00513 }
00514
00515
00516
00517 void CSoundDriverDSound::update()
00518 {
00519 #if NLSOUND_PROFILE
00520 TTicks now = CTime::getPerformanceTime();
00521 #endif
00522
00523 set<CSourceDSound*>::iterator iter;
00524
00525 iter = _Sources.begin();
00526
00527 if ((iter != _Sources.end()) && (*iter)->needsUpdate())
00528 {
00529 while (iter != _Sources.end())
00530 {
00531 if ((*iter)->update2()) {
00532 #if NLSOUND_PROFILE
00533 _UpdateSources++;
00534 #endif
00535 }
00536 iter++;
00537 }
00538 }
00539
00540
00541 #if NLSOUND_PROFILE
00542 _TotalUpdateTime += 1000.0 * CTime::ticksToSecond(CTime::getPerformanceTime() - now);
00543 _UpdateCount++;
00544 #endif
00545 }
00546
00547
00548
00549 uint CSoundDriverDSound::countHw3DBuffers()
00550 {
00551 DSCAPS caps;
00552 caps.dwSize = sizeof(caps);
00553
00554 if (_DirectSound->GetCaps(&caps) != DS_OK)
00555 {
00556 throw ESoundDriver("Failed to query the sound device caps");
00557 }
00558
00559 return caps.dwFreeHw3DStreamingBuffers;
00560 }
00561
00562
00563
00564 uint CSoundDriverDSound::countHw2DBuffers()
00565 {
00566 DSCAPS caps;
00567 caps.dwSize = sizeof(caps);
00568
00569 if (_DirectSound->GetCaps(&caps) != DS_OK)
00570 {
00571 throw ESoundDriver("Failed to query the sound device caps");
00572 }
00573
00574 return caps.dwFreeHwMixingStreamingBuffers;
00575 }
00576
00577
00578
00579 IListener *CSoundDriverDSound::createListener()
00580 {
00581 LPDIRECTSOUND3DLISTENER dsoundListener;
00582
00583 if (CListenerDSound::instance() != NULL)
00584 {
00585 return CListenerDSound::instance();
00586 }
00587
00588 if (_PrimaryBuffer == 0)
00589 {
00590 throw ESoundDriver("Corrupt driver");
00591 }
00592
00593 if (FAILED(_PrimaryBuffer->QueryInterface(IID_IDirectSound3DListener, (LPVOID *) &dsoundListener)))
00594 {
00595 nlwarning("The 3D listener interface is not available.");
00596 return new CListenerDSound(NULL);
00597 }
00598
00599 return new CListenerDSound(dsoundListener);
00600 }
00601
00602
00603
00604 IBuffer *CSoundDriverDSound::createBuffer()
00605 {
00606 if (_PrimaryBuffer == 0)
00607 {
00608 throw ESoundDriver("Corrupt driver");
00609 }
00610
00611
00612
00613 return new CBufferDSound();
00614 }
00615
00616
00617
00618 void CSoundDriverDSound::removeBuffer(IBuffer *buffer)
00619 {
00620 }
00621
00622
00623
00624 bool CSoundDriverDSound::loadWavFile(IBuffer *destbuffer, const char *filename)
00625 {
00626 return ((CBufferDSound*) destbuffer)->loadWavFile(filename);
00627 }
00628
00629
00630
00631 ISource *CSoundDriverDSound::createSource()
00632 {
00633 if (_PrimaryBuffer == 0)
00634 {
00635 throw ESoundDriver("Corrupt driver");
00636 }
00637
00638
00639 CSourceDSound* src = new CSourceDSound(0);
00640 src->init(_DirectSound);
00641 _Sources.insert(src);
00642
00643 return src;
00644 }
00645
00646
00647
00648
00649 void CSoundDriverDSound::removeSource(ISource *source)
00650 {
00651 _Sources.erase((CSourceDSound*) source);
00652 }
00653
00654
00655
00656 void CSoundDriverDSound::commit3DChanges()
00657 {
00658 CListenerDSound* listener = CListenerDSound::instance();
00659 listener->commit3DChanges();
00660
00661
00662 const CVector &origin = listener->getPos();
00663
00664 set<CSourceDSound*>::iterator iter;
00665
00666
00667
00668
00669 for (iter = _Sources.begin(); iter != _Sources.end(); iter++)
00670 {
00671 if ((*iter)->isPlaying())
00672 {
00673 (*iter)->updateVolume(origin);
00674 }
00675 }
00676 }
00677
00678
00679
00680
00681 uint CSoundDriverDSound::countPlayingSources()
00682 {
00683 uint n = 0;
00684 set<CSourceDSound*>::iterator iter;
00685
00686 for (iter = _Sources.begin(); iter != _Sources.end(); iter++)
00687 {
00688 if ((*iter)->isPlaying())
00689 {
00690 n++;
00691 }
00692 }
00693
00694 return n;
00695 }
00696
00697
00698
00699
00700 void CSoundDriverDSound::setGain( float gain )
00701 {
00702 if (_PrimaryBuffer != 0)
00703 {
00704 if (gain < 0.00001f)
00705 {
00706 gain = 0.00001f;
00707 }
00708
00709
00710 LONG volume = (LONG)(100.0 * 20.0 * log10(gain));
00711
00712 if (volume < DSBVOLUME_MIN)
00713 {
00714 volume = DSBVOLUME_MIN;
00715 }
00716 else if (volume > DSBVOLUME_MAX)
00717 {
00718 volume = DSBVOLUME_MAX;
00719 }
00720
00721 HRESULT hr = _PrimaryBuffer->SetVolume(volume);
00722
00723 if (hr != DS_OK)
00724 {
00725 nldebug("Failed to set the volume");
00726 }
00727 }
00728 }
00729
00730
00731
00732 float CSoundDriverDSound::getGain()
00733 {
00734 if (_PrimaryBuffer != 0)
00735 {
00736
00737 LONG volume;
00738 HRESULT hr = _PrimaryBuffer->GetVolume(&volume);
00739
00740 if (hr != DS_OK)
00741 {
00742 nldebug("Failed to get the volume");
00743 return 1.0;
00744 }
00745
00746 return (float) pow(10, (double) volume / 20.0 / 100.0);
00747 }
00748
00749 return 1.0;
00750 }
00751
00752
00753
00754 #if NLSOUND_PROFILE
00755
00756
00757
00758 uint CSoundDriverDSound::countTimerIntervals()
00759 {
00760 return 1024;
00761 }
00762
00763
00764
00765 uint CSoundDriverDSound::getTimerIntervals(uint index)
00766 {
00767 return _TimerInterval[index];
00768 }
00769
00770
00771
00772 void CSoundDriverDSound::addTimerInterval(uint32 dt)
00773 {
00774 if (_TimerIntervalCount >= 1024)
00775 {
00776 _TimerIntervalCount = 0;
00777 }
00778
00779 _TimerInterval[_TimerIntervalCount++] = dt;
00780 }
00781
00782
00783
00784 double CSoundDriverDSound::getCPULoad()
00785 {
00786 return (_TotalTime > 0.0)? 100.0 * _TotalUpdateTime / _TotalTime : 0.0;
00787 }
00788
00789
00790
00791 void CSoundDriverDSound::printDriverInfo(FILE* fp)
00792 {
00793 CDeviceDescription* list = CDeviceDescription::_List;
00794
00795 while (list) {
00796 fprintf(fp, "%s\n", list->_Description);
00797 list = list->_Next;
00798 }
00799
00800 fprintf(fp, "\n");
00801
00802 fprintf(fp, "buffer size: %d\n"
00803 "copy size: %d\n"
00804 "swap size: %d\n",
00805 CSourceDSound::_SecondaryBufferSize,
00806 CSourceDSound::_UpdateCopySize,
00807 CSourceDSound::_SwapCopySize);
00808
00809 fprintf(fp, "\n");
00810
00811 DSCAPS caps;
00812 caps.dwSize = sizeof(caps);
00813
00814 if (_DirectSound->GetCaps(&caps) != DS_OK)
00815 {
00816 throw ESoundDriver("Failed to query the sound device caps");
00817 }
00818
00819
00820 fprintf(fp, "3d hw buffers: %d\n" "2d hw buffers: %d\n\n", caps.dwMaxHw3DAllBuffers, caps.dwMaxHwMixingAllBuffers);
00821 }
00822
00823 #endif
00824
00825
00826 }