]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/sys/win32/win_snd.cpp
hello world
[icculus/iodoom3.git] / neo / sys / win32 / win_snd.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28 #include "../../idlib/precompiled.h"
29 #pragma hdrstop
30
31 // DirectX SDK
32 #include <DxErr.h>
33
34 #include <ks.h>
35 #include <ksmedia.h>
36 #include "../../sound/snd_local.h"
37 #include "win_local.h"
38
39 #include "../../openal/idal.cpp"
40
41 #define SAFE_DELETE(p)       { if(p) { delete (p);     (p)=NULL; } }
42 #define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p);   (p)=NULL; } }
43 #define SAFE_RELEASE(p)      { if(p) { (p)->Release(); (p)=NULL; } }
44
45 class idAudioBufferWIN32 : public idAudioBuffer {
46 public:
47     idAudioBufferWIN32( LPDIRECTSOUNDBUFFER apDSBuffer, dword dwDSBufferSize, idWaveFile* pWaveFile=NULL );
48     ~idAudioBufferWIN32();
49
50     int FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB, bool bRepeatWavIfBufferLarger );
51
52         bool Lock( void **pDSLockedBuffer, ulong *dwDSLockedBufferSize );
53         bool Unlock(void *pDSLockedBuffer, dword dwDSLockedBufferSize );
54         bool GetCurrentPosition( ulong *pdwCurrentWriteCursor );
55
56     int Play( dword dwPriority=0, dword dwFlags=0 );
57     int Stop( void );
58     int Reset( void );
59     bool    IsSoundPlaying( void );
60         void SetVolume( float x);
61
62     idWaveFile*                 m_pWaveFile;
63 private:
64     LPDIRECTSOUNDBUFFER         m_apDSBuffer;
65     dword                                       m_dwDSBufferSize;
66
67     int RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, bool* pbWasRestored );
68 };
69
70 class idAudioHardwareWIN32 : public idAudioHardware {
71
72 public:
73     idAudioHardwareWIN32();
74     ~idAudioHardwareWIN32();
75
76     bool Initialize( );
77         bool InitializeSpeakers( byte *buffer, int bufferSize, dword dwPrimaryFreq, dword dwPrimaryBitRate, dword dwSpeakers );
78
79         void SetPrimaryBufferFormat( dword dwPrimaryFreq, dword dwPrimaryBitRate, dword dwSpeakers );
80
81     int Create( idWaveFile* pWaveFile, idAudioBuffer** ppiab );
82     int Create( idAudioBuffer** ppSound, const char* strWaveFileName, dword dwCreationFlags = 0 );
83     int CreateFromMemory( idAudioBufferWIN32** ppSound, byte* pbData, ulong ulDataSize, waveformatextensible_t *pwfx, dword dwCreationFlags = 0 );
84
85         bool Lock( void **pDSLockedBuffer, ulong *dwDSLockedBufferSize );
86         bool Unlock( void *pDSLockedBuffer, dword dwDSLockedBufferSize );
87         bool GetCurrentPosition( ulong *pdwCurrentWriteCursor );
88         
89         int                             GetNumberOfSpeakers()                           { return numSpeakers; }
90         int                             GetMixBufferSize()                                      { return MIXBUFFER_SAMPLES * blockAlign; }
91
92         // WIN32 driver doesn't support write API
93         bool Flush( void ) { return true; }
94         void Write( bool ) { }
95         short* GetMixBuffer( void ) { return NULL; }
96
97 private:
98     LPDIRECTSOUND                       m_pDS;
99     LPDIRECTSOUNDBUFFER         pDSBPrimary;
100         idAudioBufferWIN32              *speakers;
101
102         int                                             numSpeakers;
103         int                                             bitsPerSample;
104         int                                             bufferSize;             // allocate buffer handed over to DirectSound
105         int                                             blockAlign;             // channels * bits per sample / 8: sound frame size
106 };
107
108 idAudioHardware *idAudioHardware::Alloc() { return new idAudioHardwareWIN32; }
109 idAudioHardware::~idAudioHardware() {}
110
111 /*
112 ================
113 idAudioHardwareWIN32::idAudioHardware
114 ================
115 */
116 idAudioHardwareWIN32::idAudioHardwareWIN32() {
117     m_pDS = NULL;
118         pDSBPrimary = NULL;
119         speakers = NULL;
120 }
121
122 /*
123 ================
124 idAudioHardwareWIN32::~idAudioHardware
125 ================
126 */
127 idAudioHardwareWIN32::~idAudioHardwareWIN32() {
128         SAFE_DELETE( speakers );
129         SAFE_RELEASE( pDSBPrimary );
130     SAFE_RELEASE( m_pDS ); 
131 }
132
133 /*
134 ===============
135 idAudioHardwareWIN32::Initialize
136 ===============
137 */
138 bool idAudioHardwareWIN32::Initialize( void ) {
139     int             hr;
140
141         bufferSize = 0;
142         numSpeakers = 0;
143         blockAlign = 0;
144
145     SAFE_RELEASE( m_pDS );
146
147     // Create IDirectSound using the primary sound device
148     if( FAILED( hr = DirectSoundCreate( NULL, &m_pDS, NULL ) )) {
149         return false;
150         }
151
152     // Set primary buffer format
153         SetPrimaryBufferFormat( PRIMARYFREQ, 16, idSoundSystemLocal::s_numberOfSpeakers.GetInteger() );
154         return true;
155 }
156
157 /*
158 ===============
159 idAudioHardwareWIN32::InitializeSpeakers
160 ===============
161 */
162 bool idAudioHardwareWIN32::InitializeSpeakers( byte *speakerData, int bufferSize, dword dwPrimaryFreq, dword dwPrimaryBitRate, dword dwSpeakers ) {
163         if ( dwSpeakers == 2 ) {
164                 WAVEFORMATEXTENSIBLE wfx;
165                 ZeroMemory( &wfx, sizeof(WAVEFORMATEXTENSIBLE) ); 
166                 wfx.Format.wFormatTag      = WAVE_FORMAT_PCM; 
167                 wfx.Format.nChannels       = 2; 
168                 wfx.Format.nSamplesPerSec  = dwPrimaryFreq;
169                 wfx.Format.wBitsPerSample  = dwPrimaryBitRate; 
170                 wfx.Format.nBlockAlign     = wfx.Format.wBitsPerSample / 8 * wfx.Format.nChannels;
171                 wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
172                 wfx.Format.cbSize = sizeof(WAVEFORMATEX);
173
174                 CreateFromMemory( &speakers, speakerData, bufferSize, (waveformatextensible_t *)&wfx );
175
176                 common->Printf("sound: STEREO\n");
177         } else {
178                 WAVEFORMATEXTENSIBLE    waveFormatPCMEx;
179                 ZeroMemory( &waveFormatPCMEx, sizeof(WAVEFORMATEXTENSIBLE) ); 
180
181                 waveFormatPCMEx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
182                 waveFormatPCMEx.Format.nChannels = 6;
183                 waveFormatPCMEx.Format.nSamplesPerSec = dwPrimaryFreq;
184                 waveFormatPCMEx.Format.wBitsPerSample  = dwPrimaryBitRate;
185                 waveFormatPCMEx.Format.nBlockAlign = waveFormatPCMEx.Format.wBitsPerSample / 8 * waveFormatPCMEx.Format.nChannels;
186                 waveFormatPCMEx.Format.nAvgBytesPerSec = waveFormatPCMEx.Format.nSamplesPerSec * waveFormatPCMEx.Format.nBlockAlign;
187                 waveFormatPCMEx.dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
188                                                                          // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
189                                                                          // SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
190                                                                          // SPEAKER_BACK_LEFT  | SPEAKER_BACK_RIGHT
191                 waveFormatPCMEx.SubFormat =  KSDATAFORMAT_SUBTYPE_PCM;  // Specify PCM
192                 waveFormatPCMEx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE);
193                 waveFormatPCMEx.Samples.wValidBitsPerSample = 16;
194
195                 CreateFromMemory( &speakers, speakerData, bufferSize, (waveformatextensible_t *)&waveFormatPCMEx );
196
197                 common->Printf("sound: MULTICHANNEL\n");
198         }
199
200         if (!speakers) {
201                 return false;
202         }
203
204         speakers->Play(0,DSBPLAY_LOOPING);
205
206         return true;
207 }
208
209 /*
210 ===============
211 idAudioHardwareWIN32::SetPrimaryBufferFormat
212 Set primary buffer to a specified format 
213 For example, to set the primary buffer format to 22kHz stereo, 16-bit
214 then:   dwPrimaryChannels = 2
215         dwPrimaryFreq     = 22050, 
216         dwPrimaryBitRate  = 16
217 ===============
218 */
219 void idAudioHardwareWIN32::SetPrimaryBufferFormat( dword dwPrimaryFreq, dword dwPrimaryBitRate, dword dwSpeakers ) {
220     HRESULT             hr;
221
222     if( m_pDS == NULL ) {
223         return;
224         }
225
226         ulong cfgSpeakers;
227         m_pDS->GetSpeakerConfig( &cfgSpeakers );
228
229         DSCAPS dscaps; 
230         dscaps.dwSize = sizeof(DSCAPS); 
231     m_pDS->GetCaps(&dscaps); 
232
233         if (dscaps.dwFlags & DSCAPS_EMULDRIVER) {
234                 return;
235         }
236
237         // Get the primary buffer 
238     DSBUFFERDESC dsbd;
239     ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
240     dsbd.dwSize        = sizeof(DSBUFFERDESC);
241     dsbd.dwFlags       = DSBCAPS_PRIMARYBUFFER;
242     dsbd.dwBufferBytes = 0;
243     dsbd.lpwfxFormat   = NULL;
244        
245         // Obtain write-primary cooperative level. 
246         if( FAILED( hr = m_pDS->SetCooperativeLevel(win32.hWnd, DSSCL_PRIORITY ) ) ) {
247         DXTRACE_ERR( TEXT("SetPrimaryBufferFormat"), hr );
248                 return;
249         }
250
251         if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ) ) ) {
252                 return;
253         }
254
255         if ( dwSpeakers == 6 && (cfgSpeakers == DSSPEAKER_5POINT1 || cfgSpeakers == DSSPEAKER_SURROUND) ) {
256                 WAVEFORMATEXTENSIBLE    waveFormatPCMEx;
257                 ZeroMemory( &waveFormatPCMEx, sizeof(WAVEFORMATEXTENSIBLE) ); 
258
259                 waveFormatPCMEx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
260                 waveFormatPCMEx.Format.nChannels = 6;
261                 waveFormatPCMEx.Format.nSamplesPerSec = dwPrimaryFreq;
262                 waveFormatPCMEx.Format.wBitsPerSample  = (WORD) dwPrimaryBitRate; 
263                 waveFormatPCMEx.Format.nBlockAlign = waveFormatPCMEx.Format.wBitsPerSample / 8 * waveFormatPCMEx.Format.nChannels;
264                 waveFormatPCMEx.Format.nAvgBytesPerSec = waveFormatPCMEx.Format.nSamplesPerSec * waveFormatPCMEx.Format.nBlockAlign;
265                 waveFormatPCMEx.dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
266                                                                          // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
267                                                                          // SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
268                                                                          // SPEAKER_BACK_LEFT  | SPEAKER_BACK_RIGHT
269                 waveFormatPCMEx.SubFormat =  KSDATAFORMAT_SUBTYPE_PCM;  // Specify PCM
270                 waveFormatPCMEx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE);
271                 waveFormatPCMEx.Samples.wValidBitsPerSample = 16;
272
273                 if( FAILED( hr = pDSBPrimary->SetFormat((WAVEFORMATEX*)&waveFormatPCMEx) ) ) {
274                 DXTRACE_ERR( TEXT("SetPrimaryBufferFormat"), hr );
275                         return;
276                 }
277                 numSpeakers = 6;                // force it to think 5.1
278                 blockAlign = waveFormatPCMEx.Format.nBlockAlign;
279         } else {
280                 if (dwSpeakers == 6) {
281                         common->Printf("sound: hardware reported unable to use multisound, defaulted to stereo\n");
282                 }
283                 WAVEFORMATEX wfx;
284                 ZeroMemory( &wfx, sizeof(WAVEFORMATEX) ); 
285                 wfx.wFormatTag      = WAVE_FORMAT_PCM; 
286                 wfx.nChannels       = 2; 
287                 wfx.nSamplesPerSec  = dwPrimaryFreq; 
288                 wfx.wBitsPerSample  = (WORD) dwPrimaryBitRate; 
289                 wfx.nBlockAlign     = wfx.wBitsPerSample / 8 * wfx.nChannels;
290                 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
291                 wfx.cbSize = sizeof(WAVEFORMATEX);
292
293                 if( FAILED( hr = pDSBPrimary->SetFormat(&wfx) ) ) {
294                         return;
295                 }
296                 numSpeakers = 2;                // force it to think stereo
297                 blockAlign = wfx.nBlockAlign;
298         }
299
300         byte *speakerData;
301         bufferSize = MIXBUFFER_SAMPLES * sizeof(word) * numSpeakers * ROOM_SLICES_IN_BUFFER;
302         speakerData = (byte *)Mem_Alloc( bufferSize );
303         memset( speakerData, 0, bufferSize );
304
305         InitializeSpeakers( speakerData, bufferSize, dwPrimaryFreq, dwPrimaryBitRate, numSpeakers );
306 }
307
308 /*
309 ===============
310 idAudioHardwareWIN32::Create
311 ===============
312 */
313 int idAudioHardwareWIN32::Create( idAudioBuffer** ppSound, 
314                                const char* strWaveFileName, 
315                                                            dword dwCreationFlags ) {
316     int hr;
317     LPDIRECTSOUNDBUFFER   apDSBuffer     = NULL;
318     dword                 dwDSBufferSize = NULL;
319     idWaveFile*          pWaveFile      = NULL;
320
321     if( m_pDS == NULL )
322         return -1;
323     if( strWaveFileName == NULL || ppSound == NULL )
324         return -1;
325
326         pWaveFile = new idWaveFile();
327
328     pWaveFile->Open( strWaveFileName, NULL );
329
330     if( pWaveFile->GetOutputSize() == 0 )     {
331         // Wave is blank, so don't create it.
332         hr = E_FAIL;
333         goto LFail;
334     }
335
336     // Make the DirectSound buffer the same size as the wav file
337     dwDSBufferSize = pWaveFile->GetOutputSize();
338
339     // Create the direct sound buffer, and only request the flags needed
340     // since each requires some overhead and limits if the buffer can 
341     // be hardware accelerated
342     DSBUFFERDESC dsbd;
343     memset( &dsbd, 0, sizeof(DSBUFFERDESC) );
344     dsbd.dwSize          = sizeof(DSBUFFERDESC);
345     dsbd.dwFlags         = dwCreationFlags;
346     dsbd.dwBufferBytes   = dwDSBufferSize;
347     dsbd.guid3DAlgorithm = GUID_NULL;
348     dsbd.lpwfxFormat     = (WAVEFORMATEX*)&pWaveFile->mpwfx;
349
350     // DirectSound is only guarenteed to play PCM data.  Other
351     // formats may or may not work depending the sound card driver.
352     if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer, NULL ) ) )
353                 return -1;
354
355     // Create the sound
356     *ppSound = new idAudioBufferWIN32( apDSBuffer, dwDSBufferSize, pWaveFile );
357     
358     pWaveFile->Close();
359
360     return 0;
361
362 LFail:
363     // Cleanup
364     SAFE_DELETE( pWaveFile );
365     return -1;
366 }
367
368 /*
369 ===============
370 idAudioHardwareWIN32::Create
371 ===============
372 */
373 int idAudioHardwareWIN32::Create( idWaveFile* pWaveFile, idAudioBuffer** ppiab ) {
374     int hr;
375     LPDIRECTSOUNDBUFFER   apDSBuffer     = NULL;
376     dword                dwDSBufferSize = NULL;
377
378     if( m_pDS == NULL )
379         return -1;
380
381     if( pWaveFile == NULL )
382         return -1;
383
384         *ppiab = NULL;
385
386     if( pWaveFile->GetOutputSize() == 0 )     {
387         // Wave is blank, so don't create it.
388         hr = E_FAIL;
389         goto LFail;
390     }
391
392     // Make the DirectSound buffer the same size as the wav file
393     dwDSBufferSize = pWaveFile->GetOutputSize();
394
395     // Create the direct sound buffer, and only request the flags needed
396     // since each requires some overhead and limits if the buffer can 
397     // be hardware accelerated
398     DSBUFFERDESC dsbd;
399     memset( &dsbd, 0, sizeof(DSBUFFERDESC) );
400     dsbd.dwSize          = sizeof(DSBUFFERDESC);
401     dsbd.dwFlags         = 0;
402     dsbd.dwBufferBytes   = dwDSBufferSize;
403     dsbd.guid3DAlgorithm = GUID_NULL;
404     dsbd.lpwfxFormat     = (WAVEFORMATEX*)&pWaveFile->mpwfx;
405
406     // DirectSound is only guarenteed to play PCM data.  Other
407     // formats may or may not work depending the sound card driver.
408     if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer, NULL ) ) )
409                 return -1;
410
411     // Create the sound
412     *ppiab = new idAudioBufferWIN32( apDSBuffer, dwDSBufferSize, pWaveFile );
413     
414     return 0;
415
416 LFail:
417     // Cleanup
418     SAFE_DELETE( pWaveFile );
419     return -1;
420 }
421
422 //-----------------------------------------------------------------------------
423 // Name: idAudioHardwareWIN32::CreateFromMemory()
424 // Desc: 
425 //-----------------------------------------------------------------------------
426 int idAudioHardwareWIN32::CreateFromMemory( idAudioBufferWIN32** ppSound, 
427                                         byte* pbData,
428                                         ulong  ulDataSize,
429                                         waveformatextensible_t* pwfx,
430                                                                                 dword dwCreationFlags ) {
431     int hr;
432     LPDIRECTSOUNDBUFFER         apDSBuffer     = NULL;
433     dword                                       dwDSBufferSize = NULL;
434     idWaveFile*                 pWaveFile      = NULL;
435
436     if( m_pDS == NULL )
437         return -1;
438     if( pbData == NULL || ppSound == NULL )
439         return -1;
440
441     pWaveFile = new idWaveFile();
442
443     pWaveFile->OpenFromMemory( (short *)pbData, ulDataSize, (waveformatextensible_t *)pwfx);
444
445
446     // Make the DirectSound buffer the same size as the wav file
447     dwDSBufferSize = ulDataSize;
448
449     // Create the direct sound buffer, and only request the flags needed
450     // since each requires some overhead and limits if the buffer can 
451     // be hardware accelerated
452     DSBUFFERDESC dsbd;
453     memset( &dsbd, 0, sizeof(DSBUFFERDESC) );
454     dsbd.dwSize          = sizeof(DSBUFFERDESC);
455     dsbd.dwFlags         = dwCreationFlags | DSBCAPS_GETCURRENTPOSITION2;
456     dsbd.dwBufferBytes   = dwDSBufferSize;
457     dsbd.guid3DAlgorithm = GUID_NULL;
458     dsbd.lpwfxFormat     = (WAVEFORMATEX *)pwfx;
459
460     if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer, NULL ) ) )
461                 return -1;
462
463     // Create the sound
464     *ppSound = new idAudioBufferWIN32( apDSBuffer, dwDSBufferSize, pWaveFile );
465
466     return S_OK;
467 }
468
469 /*
470 ===============
471 idAudioHardwareWIN32::Lock
472 ===============
473 */
474 bool idAudioHardwareWIN32::Lock( void **pDSLockedBuffer, ulong *dwDSLockedBufferSize ) {
475         if (speakers) {
476                 return speakers->Lock( pDSLockedBuffer, dwDSLockedBufferSize );
477         }
478         return false;
479 }
480
481 /*
482 ===============
483 idAudioHardwareWIN32::Unlock
484 ===============
485 */
486 bool idAudioHardwareWIN32::Unlock(void *pDSLockedBuffer, dword dwDSLockedBufferSize ) {
487         if (speakers) {
488                 return speakers->Unlock( pDSLockedBuffer, dwDSLockedBufferSize );
489         }
490         return false;
491 }
492
493 /*
494 ===============
495 idAudioHardwareWIN32::GetCurrentPosition
496 ===============
497 */
498 bool idAudioHardwareWIN32::GetCurrentPosition( ulong *pdwCurrentWriteCursor ) {
499         if (speakers) {
500                 return speakers->GetCurrentPosition( pdwCurrentWriteCursor );
501         }
502         return false;
503 }
504
505 static HMODULE hOpenAL = NULL;
506
507 /*
508 ===============
509 Sys_LoadOpenAL
510 ===============
511 */
512 bool Sys_LoadOpenAL( void ) {
513 #if ID_OPENAL
514         const char *sym;
515
516         if ( hOpenAL ) {
517                 return true;
518         }
519
520         hOpenAL = LoadLibrary( idSoundSystemLocal::s_libOpenAL.GetString() );
521         if ( !hOpenAL ) {
522                 common->Warning( "LoadLibrary %s failed.", idSoundSystemLocal::s_libOpenAL.GetString() );
523                 return false;
524         }
525         if ( ( sym = InitializeIDAL( hOpenAL ) ) ) {
526                 common->Warning( "GetProcAddress %s failed.", sym );
527                 FreeLibrary( hOpenAL );
528                 hOpenAL = NULL;
529                 return false;
530         }
531         return true;
532 #else
533         return false;
534 #endif
535 }
536
537 /*
538 ===============
539 Sys_FreeOpenAL
540 ===============
541 */
542 void Sys_FreeOpenAL( void ) {
543         if ( hOpenAL ) {
544                 FreeLibrary( hOpenAL );
545                 hOpenAL = NULL;
546         }
547 }
548
549 /*
550 ===============
551 idAudioBufferWIN32::idAudioBuffer
552 ===============
553 */
554 idAudioBufferWIN32::idAudioBufferWIN32( LPDIRECTSOUNDBUFFER apDSBuffer, dword dwDSBufferSize, idWaveFile* pWaveFile ) {
555
556     m_apDSBuffer = apDSBuffer;
557
558     m_dwDSBufferSize = dwDSBufferSize;
559     m_pWaveFile      = pWaveFile;
560
561         if (pWaveFile) {
562             FillBufferWithSound( m_apDSBuffer, false );
563
564                 m_apDSBuffer->SetCurrentPosition(0);
565         }
566 }
567
568 /*
569 ===============
570 idAudioBufferWIN32::~idAudioBuffer
571 ===============
572 */
573 idAudioBufferWIN32::~idAudioBufferWIN32() {
574         SAFE_DELETE(m_pWaveFile);
575         SAFE_RELEASE( m_apDSBuffer ); 
576         m_pWaveFile = NULL;
577         m_apDSBuffer = NULL;
578 }
579
580 /*
581 ===============
582 idAudioBufferWIN32::FillBufferWithSound
583 ===============
584 */
585 int idAudioBufferWIN32::FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB, bool bRepeatWavIfBufferLarger ) {
586     int hr; 
587     void*   pDSLockedBuffer      = NULL; // Pointer to locked buffer memory
588     ulong   dwDSLockedBufferSize = 0;    // Size of the locked DirectSound buffer
589     int         dwWavDataRead        = 0;    // Amount of data read from the wav file 
590
591     if( pDSB == NULL )
592         return -1;
593
594         // we may not even have a wavefile
595         if (m_pWaveFile==NULL) {
596                 return -1;
597         }
598
599     // Make sure we have focus, and we didn't just switch in from
600     // an app which had a DirectSound device
601     if( FAILED( hr = RestoreBuffer( pDSB, NULL ) ) ) {
602         DXTRACE_ERR( TEXT("RestoreBuffer"), hr );
603                 return -1;
604         }
605
606     // Lock the buffer down
607     if( FAILED( hr = pDSB->Lock( 0, m_dwDSBufferSize, &pDSLockedBuffer, &dwDSLockedBufferSize, NULL, NULL, 0L ) ) ) {
608         DXTRACE_ERR( TEXT("Lock"), hr );
609                 return -1;
610         }
611
612     // Reset the wave file to the beginning 
613     m_pWaveFile->ResetFile();
614
615     if( FAILED( hr = m_pWaveFile->Read( (byte*) pDSLockedBuffer, dwDSLockedBufferSize, &dwWavDataRead ) ) ) {
616         return DXTRACE_ERR( TEXT("Read"), hr );
617         }
618
619     if( dwWavDataRead == 0 ) {
620         // Wav is blank, so just fill with silence
621         memset( pDSLockedBuffer, (byte)(m_pWaveFile->mpwfx.Format.wBitsPerSample == 8 ? 128 : 0 ), dwDSLockedBufferSize );
622     }  else if( dwWavDataRead < (int)dwDSLockedBufferSize ) {
623         // If the wav file was smaller than the DirectSound buffer, 
624         // we need to fill the remainder of the buffer with data 
625         if( bRepeatWavIfBufferLarger ) {       
626             // Reset the file and fill the buffer with wav data
627             int dwReadSoFar = dwWavDataRead;    // From previous call above.
628             while( dwReadSoFar < (int)dwDSLockedBufferSize ) {  
629                 // This will keep reading in until the buffer is full 
630                 // for very short files
631                 if( FAILED( hr = m_pWaveFile->ResetFile() ) ) {
632                     return DXTRACE_ERR( TEXT("ResetFile"), hr );
633                                 }
634
635                 hr = m_pWaveFile->Read( (byte*)pDSLockedBuffer + dwReadSoFar, dwDSLockedBufferSize - dwReadSoFar, &dwWavDataRead );
636                 if( FAILED(hr) ) {
637                     return DXTRACE_ERR( TEXT("Read"), hr );
638                                 }
639
640                 dwReadSoFar += dwWavDataRead;
641             } 
642         } else {
643             // Don't repeat the wav file, just fill in silence 
644             memset( (byte*) pDSLockedBuffer + dwWavDataRead, (byte)(m_pWaveFile->mpwfx.Format.wBitsPerSample == 8 ? 128 : 0 ), dwDSLockedBufferSize - dwWavDataRead);
645         }
646     }
647
648     // Unlock the buffer, we don't need it anymore.
649     pDSB->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
650
651     return S_OK;
652 }
653
654 /*
655 ===============
656 idAudioBufferWIN32::RestoreBuffer
657 Desc: Restores the lost buffer. *pbWasRestored returns true if the buffer was 
658       restored.  It can also NULL if the information is not needed.
659 ===============
660 */
661 int idAudioBufferWIN32::RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, bool* pbWasRestored ) {
662     int hr;
663
664     if( pDSB == NULL ) {
665         return -1;
666         }
667     if( pbWasRestored ) {
668         *pbWasRestored = false;
669         }
670
671     ulong dwStatus;
672     if( FAILED( hr = pDSB->GetStatus( &dwStatus ) ) ) {
673         return DXTRACE_ERR( TEXT("GetStatus"), hr );
674         }
675
676     if( dwStatus & DSBSTATUS_BUFFERLOST ) {
677         // Since the app could have just been activated, then
678         // DirectSound may not be giving us control yet, so 
679         // the restoring the buffer may fail.  
680         // If it does, sleep until DirectSound gives us control.
681         do {
682             hr = pDSB->Restore();
683             if( hr == DSERR_BUFFERLOST ) {
684                 Sleep( 10 );
685                         }
686                         hr = pDSB->Restore();
687         } while( hr );
688
689         if( pbWasRestored != NULL ) {
690             *pbWasRestored = true;
691                 }
692
693         return S_OK;
694     } else {
695         return S_FALSE;
696     }
697 }
698
699 /*
700 ===============
701 idAudioBufferWIN32::Play
702 Desc: Plays the sound using voice management flags.  Pass in DSBPLAY_LOOPING
703       in the dwFlags to loop the sound
704 ===============
705 */
706 int idAudioBufferWIN32::Play( dword dwPriority, dword dwFlags ) {
707     int hr;
708     bool    bRestored;
709
710     if( m_apDSBuffer == NULL ) {
711         return -1;
712         }
713
714     // Restore the buffer if it was lost
715     if( FAILED( hr = RestoreBuffer( m_apDSBuffer, &bRestored ) ) ) {
716         common->Error( TEXT("RestoreBuffer"), hr );
717         }
718
719     if( bRestored ) {
720         // The buffer was restored, so we need to fill it with new data
721         if( FAILED( hr = FillBufferWithSound( m_apDSBuffer, false ) ) ) {
722             common->Error( TEXT("FillBufferWithSound"), hr );
723                 }
724
725         // Make DirectSound do pre-processing on sound effects
726         Reset();
727     }
728
729     m_apDSBuffer->Play( 0, dwPriority, dwFlags );
730         return 0;
731 }
732
733 /*
734 ===============
735 idAudioBufferWIN32::Stop
736 Desc: Stops the sound from playing
737 ===============
738 */
739 int idAudioBufferWIN32::Stop() {
740     if( this == NULL || m_apDSBuffer == NULL ) {
741         return -1;
742         }
743
744         m_apDSBuffer->Stop();
745
746     return 0;
747 }
748
749 /*
750 ===============
751 idAudioBufferWIN32::Reset
752 Desc: Reset all of the sound buffers
753 ===============
754 */
755 int idAudioBufferWIN32::Reset() {
756     if( m_apDSBuffer == NULL ) {
757         return -1;
758         }
759
760     m_apDSBuffer->SetCurrentPosition( 0 );
761
762     return 0;
763 }
764
765 /*
766 ===============
767 idAudioBufferWIN32::IsSoundPlaying
768 Desc: Checks to see if a buffer is playing and returns true if it
769 ===============
770 */
771 bool idAudioBufferWIN32::IsSoundPlaying( ) {
772     if( m_apDSBuffer == NULL ) {
773         return false;
774         }
775
776         if( m_apDSBuffer ) {  
777                 ulong dwStatus = 0;
778         m_apDSBuffer->GetStatus( &dwStatus );
779         if ( dwStatus & DSBSTATUS_PLAYING ) {
780                         return true;
781                 }
782         }
783         return false;
784 }
785
786 /*
787 ===============
788 idAudioBufferWIN32::Lock
789 ===============
790 */
791 bool idAudioBufferWIN32::Lock( void **pDSLockedBuffer, ulong *dwDSLockedBufferSize ) {
792         int hr;
793     // Restore the buffer if it was lost
794     bool bRestored;
795     if( FAILED( hr = RestoreBuffer( m_apDSBuffer, &bRestored ) ) ) {
796         return false;
797         }
798
799     // Lock the DirectSound buffer
800     if( FAILED( hr = m_apDSBuffer->Lock( 0, m_dwDSBufferSize, pDSLockedBuffer, dwDSLockedBufferSize, NULL, NULL, 0 ) ) ) {
801         return false;
802         }
803         return true;
804 }
805
806 /*
807 ===============
808 idAudioBufferWIN32::Unlock
809 ===============
810 */
811 bool idAudioBufferWIN32::Unlock(void *pDSLockedBuffer, dword dwDSLockedBufferSize ) {
812     // Unlock the DirectSound buffer
813     m_apDSBuffer->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
814         return true;
815 }
816
817 /*
818 ===============
819 idAudioBufferWIN32::GetCurrentPosition
820 ===============
821 */
822 bool idAudioBufferWIN32::GetCurrentPosition( ulong *pdwCurrentWriteCursor ) {
823         int hr;
824
825     // Make sure we have focus, and we didn't just switch in from
826     // an app which had a DirectSound device
827     if( FAILED( hr = RestoreBuffer( m_apDSBuffer, NULL ) ) ) {
828         DXTRACE_ERR( TEXT("RestoreBuffer"), hr );
829                 return false;
830         }
831
832     if( FAILED( hr = m_apDSBuffer->GetCurrentPosition( NULL, pdwCurrentWriteCursor ) ) ) {
833         return false;
834         }
835         return true;
836 }
837
838 /*
839 ===============
840 idAudioBufferWIN32::SetVolume
841 ===============
842 */
843 void idAudioBufferWIN32::SetVolume( float x) {
844         if (m_apDSBuffer) {
845                 m_apDSBuffer->SetVolume(x);
846         }
847 }