DirectSound是DirectX中为游戏处理所有声音输出的组件,只需要告诉DirectSound要播放的声音,它会处理所有细节。使用DirectSound创建、初始化、装载及播放声音文件的代码比起之前的内容要复杂一些,为了避免重复劳动,这里将使用Microsoft自己的DirectSound包装器。有些时候,考虑时间因素,必须妥协并使用已有的东西。DirectX SDK包括一个称为DXUTsound的实用工具库,我们不准备使用,因为它需要的支持文件太多。我们将使用一个老一点的版本,来自以前的DirectX 9.0c版,老的DirectSound的DXUT版可以在名为dsutil.cpp和dsutil.h的这一对文件中找到;后来的DirectX Sound类库与DXUTsound.h和DXUTsound.cpp合并在了一起;最新版的DirectX将这些类藏到了另外一组文件中:SDKsound.h、SDKsound.cpp和SDKwavefile.h。由于这些不一致性,我们创建了一对新的音频文件DirectSound.h与DirectSound.cpp以供使用,包含来自老的dsutil的源代码,这些在最新DirectX中也没更改。
为使用DirectSound,首先要做的是创建CSoundManager类的实例,CSoundManager *dsound = new CSoundManager();下一步调用Initialize函数来初始化DirectSound管理器,dsound->Initialize(window_handle, DSSCL_PRIORITY);初始化后,我们必须设置音频缓冲区格式,dsound->SetPrimaryBufferFormat(2, 22050, 16);在初始化了DirectSound管理器后,我们要接着创建声音缓冲区,CSound *wave,wave是名为LPDIRECTSOUNDBUFFER8的第二声音缓冲区的包装器,由于存在实用工具类,我们无需自己来编程。可以把由DirectSound创建好管理的声音混音器当成声音的主缓冲区,如Direct3D一样,主缓冲区是输出发生的地方。只不过在DirectSound的情况中,第二缓冲区是声音数据而不是位图数据。将声音文件装载到DirectSound的第二缓冲区中只涉及对一个Create函数的调用,即dsound->Create(&wave, "snicker.wav");要播放声音,可以使用wave->Play(); 要循环播放,使用wave->Play(0, DSBPLAY_LOOPING); 要停止播放,使用wave->Stop()。
下面的例子基于前面的例子,同时必须使用下面的两个文件。
DirectSound.h:
1 //----------------------------------------------------------------------------- 2 // File: DSUtil.h 3 //
4 // Desc: 5 //
6 // Copyright (c) Microsoft Corp. All rights reserved. 7 //----------------------------------------------------------------------------- 8 //
9 // Note: This file has been edited for use in Beginning Game Programming, Third Edition, 10 // originally distributed with a 2004 release of DirectX 9.0c SDK. 11 // 12
13 #ifndef DSUTIL_H 14 #define DSUTIL_H
15
16 #include <windows.h>
17 #include <mmsystem.h>
18 #include <mmreg.h>
19 #include <dsound.h>
20 #include <dxerr.h>
21
22 #pragma comment(lib, "dxerr.lib")
23 #pragma comment(lib, "dsound.lib")
24
25
26 //----------------------------------------------------------------------------- 27 // Classes used by this header 28 //-----------------------------------------------------------------------------
29 class CSoundManager; 30 class CSound; 31 class CStreamingSound; 32 class CWaveFile; 33
34
35
36
37 //----------------------------------------------------------------------------- 38 // Typing macros 39 //-----------------------------------------------------------------------------
40 #define WAVEFILE_READ 1
41 #define WAVEFILE_WRITE 2
42
43 #define DSUtil_StopSound(s) { if(s) s->Stop(); }
44 #define DSUtil_PlaySound(s) { if(s) s->Play( 0, 0 ); }
45 #define DSUtil_PlaySoundLooping(s) { if(s) s->Play( 0, DSBPLAY_LOOPING ); }
46
47
48
49
50 //----------------------------------------------------------------------------- 51 // Name: class CSoundManager 52 // Desc: 53 //-----------------------------------------------------------------------------
54 class CSoundManager 55 { 56 protected: 57 LPDIRECTSOUND8 m_pDS; 58
59 public: 60 CSoundManager(); 61 ~CSoundManager(); 62
63 HRESULT Initialize( HWND hWnd, DWORD dwCoopLevel ); 64
65 inline LPDIRECTSOUND8 GetDirectSound() { return m_pDS; } 66
67 HRESULT SetPrimaryBufferFormat( DWORD dwPrimaryChannels, DWORD dwPrimaryFreq, 68 DWORD dwPrimaryBitRate ); 69
70 HRESULT Create( CSound** ppSound, LPTSTR strWaveFileName, DWORD dwCreationFlags = 0, 71 GUID guid3DAlgorithm = GUID_NULL, DWORD dwNumBuffers = 1 ); 72 }; 73
74
75
76
77 //----------------------------------------------------------------------------- 78 // Name: class CSound 79 // Desc: Encapsulates functionality of a DirectSound buffer. 80 //-----------------------------------------------------------------------------
81 class CSound 82 { 83 protected: 84 LPDIRECTSOUNDBUFFER* m_apDSBuffer; 85 DWORD m_dwDSBufferSize; 86 CWaveFile* m_pWaveFile; 87 DWORD m_dwNumBuffers; 88 DWORD m_dwCreationFlags; 89
90 HRESULT RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, BOOL* pbWasRestored ); 91
92 public: 93 CSound( LPDIRECTSOUNDBUFFER* apDSBuffer, DWORD dwDSBufferSize, DWORD dwNumBuffers, 94 CWaveFile* pWaveFile, DWORD dwCreationFlags ); 95 virtual ~CSound(); 96
97 HRESULT FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB, BOOL bRepeatWavIfBufferLarger ); 98 LPDIRECTSOUNDBUFFER GetFreeBuffer(); 99
100 HRESULT Play( DWORD dwPriority = 0, DWORD dwFlags = 0, LONG lVolume = 0, 101 LONG lFrequency = -1, LONG lPan = 0 ); 102 HRESULT Stop(); 103 HRESULT Reset(); 104 BOOL IsSoundPlaying(); 105 }; 106
107
108 //----------------------------------------------------------------------------- 109 // Name: class CWaveFile 110 // Desc: Encapsulates reading or writing sound data to or from a wave file 111 //-----------------------------------------------------------------------------
112 class CWaveFile 113 { 114 public: 115 WAVEFORMATEX* m_pwfx; // Pointer to WAVEFORMATEX structure
116 HMMIO m_hmmio; // MM I/O handle for the WAVE
117 MMCKINFO m_ck; // Multimedia RIFF chunk
118 MMCKINFO m_ckRiff; // Use in opening a WAVE file
119 DWORD m_dwSize; // The size of the wave file
120 MMIOINFO m_mmioinfoOut; 121 DWORD m_dwFlags; 122 BOOL m_bIsReadingFromMemory; 123 BYTE* m_pbData; 124 BYTE* m_pbDataCur; 125 ULONG m_ulDataSize; 126 CHAR* m_pResourceBuffer; 127
128 protected: 129 HRESULT ReadMMIO(); 130 HRESULT WriteMMIO( WAVEFORMATEX *pwfxDest ); 131
132 public: 133 CWaveFile(); 134 ~CWaveFile(); 135
136 HRESULT Open( LPTSTR strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags ); 137 HRESULT Close(); 138
139 HRESULT Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead ); 140 HRESULT Write( UINT nSizeToWrite, BYTE* pbData, UINT* pnSizeWrote ); 141
142 DWORD GetSize(); 143 HRESULT ResetFile(); 144 WAVEFORMATEX* GetFormat() { return m_pwfx; }; 145 }; 146
147
148
149
150 #endif // DSUTIL_H
DirectSound.cpp:
1 //----------------------------------------------------------------------------- 2 // File: DSUtil.cpp 3 //
4 // Desc: DirectSound framework classes for reading and writing wav files and 5 // playing them in DirectSound buffers. Feel free to use this class 6 // as a starting point for adding extra functionality. 7 //
8 // Copyright (c) Microsoft Corp. All rights reserved. 9 //----------------------------------------------------------------------------- 10 //
11 // Note: This file has been edited for use in Beginning Game Programming, Third Edition, 12 // originally distributed with a 2004 release of DirectX 9.0c SDK. 13 // 14
15 #define STRICT
16 #include <windows.h>
17 #include <mmsystem.h>
18 #include "DirectSound.h"
19
20
21 //----------------------------------------------------------------------------- 22 // Miscellaneous helper functions 23 //-----------------------------------------------------------------------------
24 #define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
25 #define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } }
26 #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
27
28
29 //----------------------------------------------------------------------------- 30 // Name: CSoundManager::CSoundManager() 31 // Desc: Constructs the class 32 //-----------------------------------------------------------------------------
33 CSoundManager::CSoundManager() 34 { 35 m_pDS = NULL; 36 } 37
38 //----------------------------------------------------------------------------- 39 // Name: CSoundManager::~CSoundManager() 40 // Desc: Destroys the class 41 //-----------------------------------------------------------------------------
42 CSoundManager::~CSoundManager() 43 { 44 SAFE_RELEASE( m_pDS ); 45 } 46
47 //----------------------------------------------------------------------------- 48 // Name: CSoundManager::Initialize() 49 // Desc: Initializes the IDirectSound object and also sets the primary buffer 50 // format. This function must be called before any others. 51 //-----------------------------------------------------------------------------
52 HRESULT CSoundManager::Initialize( HWND hWnd, 53 DWORD dwCoopLevel ) 54 { 55 HRESULT hr; 56
57 SAFE_RELEASE( m_pDS ); 58
59 // Create IDirectSound using the primary sound device
60 if( FAILED( hr = DirectSoundCreate8( NULL, &m_pDS, NULL ) ) ) 61 return DXTRACE_ERR( TEXT("DirectSoundCreate8"), hr ); 62
63 // Set DirectSound coop level
64 if( FAILED( hr = m_pDS->SetCooperativeLevel( hWnd, dwCoopLevel ) ) ) 65 return DXTRACE_ERR( TEXT("SetCooperativeLevel"), hr ); 66
67 return S_OK; 68 } 69
70
71
72
73 //----------------------------------------------------------------------------- 74 // Name: CSoundManager::SetPrimaryBufferFormat() 75 // Desc: Set primary buffer to a specified format 76 // !WARNING! - Setting the primary buffer format and then using this 77 // same dsound object for DirectMusic messes up DirectMusic! 78 // For example, to set the primary buffer format to 22kHz stereo, 16-bit 79 // then: dwPrimaryChannels = 2 80 // dwPrimaryFreq = 22050, 81 // dwPrimaryBitRate = 16 82 //-----------------------------------------------------------------------------
83 HRESULT CSoundManager::SetPrimaryBufferFormat( DWORD dwPrimaryChannels, 84 DWORD dwPrimaryFreq, 85 DWORD dwPrimaryBitRate ) 86 { 87 HRESULT hr; 88 LPDIRECTSOUNDBUFFER pDSBPrimary = NULL; 89
90 if( m_pDS == NULL ) 91 return CO_E_NOTINITIALIZED; 92
93 // Get the primary buffer
94 DSBUFFERDESC dsbd; 95 ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) ); 96 dsbd.dwSize = sizeof(DSBUFFERDESC); 97 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; 98 dsbd.dwBufferBytes = 0; 99 dsbd.lpwfxFormat = NULL; 100
101 if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ) ) ) 102 return DXTRACE_ERR( TEXT("CreateSoundBuffer"), hr ); 103
104 WAVEFORMATEX wfx; 105 ZeroMemory( &wfx, sizeof(WAVEFORMATEX) ); 106 wfx.wFormatTag = (WORD) WAVE_FORMAT_PCM; 107 wfx.nChannels = (WORD) dwPrimaryChannels; 108 wfx.nSamplesPerSec = (DWORD) dwPrimaryFreq; 109 wfx.wBitsPerSample = (WORD) dwPrimaryBitRate; 110 wfx.nBlockAlign = (WORD) (wfx.wBitsPerSample / 8 * wfx.nChannels); 111 wfx.nAvgBytesPerSec = (DWORD) (wfx.nSamplesPerSec * wfx.nBlockAlign); 112
113 if( FAILED( hr = pDSBPrimary->SetFormat(&wfx) ) ) 114 return DXTRACE_ERR( TEXT("SetFormat"), hr ); 115
116 SAFE_RELEASE( pDSBPrimary ); 117
118 return S_OK; 119 } 120
121
122
123 //----------------------------------------------------------------------------- 124 // Name: CSoundManager::Create() 125 // Desc: 126 //-----------------------------------------------------------------------------
127 HRESULT CSoundManager::Create( CSound** ppSound, 128 LPTSTR strWaveFileName, 129 DWORD dwCreationFlags, 130 GUID guid3DAlgorithm, 131 DWORD dwNumBuffers ) 132 { 133 HRESULT hr; 134 HRESULT hrRet = S_OK; 135 DWORD i; 136 LPDIRECTSOUNDBUFFER* apDSBuffer = NULL; 137 DWORD dwDSBufferSize = NULL; 138 CWaveFile* pWaveFile = NULL; 139
140 if( m_pDS == NULL ) 141 return CO_E_NOTINITIALIZED; 142 if( strWaveFileName == NULL || ppSound == NULL || dwNumBuffers < 1 ) 143 return E_INVALIDARG; 144
145 apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers]; 146 if( apDSBuffer == NULL ) 147 { 148 hr = E_OUTOFMEMORY; 149 goto LFail; 150 } 151
152 pWaveFile = new CWaveFile(); 153 if( pWaveFile == NULL ) 154 { 155 hr = E_OUTOFMEMORY; 156 goto LFail; 157 } 158
159 pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ ); 160
161 if( pWaveFile->GetSize() == 0 ) 162 { 163 // Wave is blank, so don't create it.
164 hr = E_FAIL; 165 goto LFail; 166 } 167
168 // Make the DirectSound buffer the same size as the wav file
169 dwDSBufferSize = pWaveFile->GetSize(); 170
171 // Create the direct sound buffer, and only request the flags needed 172 // since each requires some overhead and limits if the buffer can 173 // be hardware accelerated
174 DSBUFFERDESC dsbd; 175 ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) ); 176 dsbd.dwSize = sizeof(DSBUFFERDESC); 177 dsbd.dwFlags = dwCreationFlags; 178 dsbd.dwBufferBytes = dwDSBufferSize; 179 dsbd.guid3DAlgorithm = guid3DAlgorithm; 180 dsbd.lpwfxFormat = pWaveFile->m_pwfx; 181
182 // DirectSound is only guarenteed to play PCM data. Other 183 // formats may or may not work depending the sound card driver.
184 hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[0], NULL ); 185
186 // Be sure to return this error code if it occurs so the 187 // callers knows this happened.
188 if( hr == DS_NO_VIRTUALIZATION ) 189 hrRet = DS_NO_VIRTUALIZATION; 190
191 if( FAILED(hr) ) 192 { 193 // DSERR_BUFFERTOOSMALL will be returned if the buffer is 194 // less than DSBSIZE_FX_MIN and the buffer is created 195 // with DSBCAPS_CTRLFX. 196
197 // It might also fail if hardware buffer mixing was requested 198 // on a device that doesn't support it.
199 DXTRACE_ERR( TEXT("CreateSoundBuffer"), hr ); 200
201 goto LFail; 202 } 203
204 // Default to use DuplicateSoundBuffer() when created extra buffers since always 205 // create a buffer that uses the same memory however DuplicateSoundBuffer() will fail if 206 // DSBCAPS_CTRLFX is used, so use CreateSoundBuffer() instead in this case.
207 if( (dwCreationFlags & DSBCAPS_CTRLFX) == 0 ) 208 { 209 for( i=1; i<dwNumBuffers; i++ ) 210 { 211 if( FAILED( hr = m_pDS->DuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) ) 212 { 213 DXTRACE_ERR( TEXT("DuplicateSoundBuffer"), hr ); 214 goto LFail; 215 } 216 } 217 } 218 else
219 { 220 for( i=1; i<dwNumBuffers; i++ ) 221 { 222 hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[i], NULL ); 223 if( FAILED(hr) ) 224 { 225 DXTRACE_ERR( TEXT("CreateSoundBuffer"), hr ); 226 goto LFail; 227 } 228 } 229 } 230
231 // Create the sound
232 *ppSound = new CSound( apDSBuffer, dwDSBufferSize, dwNumBuffers, pWaveFile, dwCreationFlags ); 233
234 SAFE_DELETE_ARRAY( apDSBuffer ); 235 return hrRet; 236
237 LFail: 238 // Cleanup
239 SAFE_DELETE( pWaveFile ); 240 SAFE_DELETE_ARRAY( apDSBuffer ); 241 return hr; 242 } 243
244
245
246 //----------------------------------------------------------------------------- 247 // Name: CSound::CSound() 248 // Desc: Constructs the class 249 //-----------------------------------------------------------------------------
250 CSound::CSound( LPDIRECTSOUNDBUFFER* apDSBuffer, DWORD dwDSBufferSize, 251 DWORD dwNumBuffers, CWaveFile* pWaveFile, DWORD dwCreationFlags ) 252 { 253 DWORD i; 254
255 m_apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers]; 256 if( NULL != m_apDSBuffer ) 257 { 258 for( i=0; i<dwNumBuffers; i++ ) 259 m_apDSBuffer[i] = apDSBuffer[i]; 260
261 m_dwDSBufferSize = dwDSBufferSize; 262 m_dwNumBuffers = dwNumBuffers; 263 m_pWaveFile = pWaveFile; 264 m_dwCreationFlags = dwCreationFlags; 265
266 FillBufferWithSound( m_apDSBuffer[0], FALSE ); 267 } 268 } 269
270
271
272
273 //----------------------------------------------------------------------------- 274 // Name: CSound::~CSound() 275 // Desc: Destroys the class 276 //-----------------------------------------------------------------------------
277 CSound::~CSound() 278 { 279 for( DWORD i=0; i<m_dwNumBuffers; i++ ) 280 { 281 SAFE_RELEASE( m_apDSBuffer[i] ); 282 } 283
284 SAFE_DELETE_ARRAY( m_apDSBuffer ); 285 SAFE_DELETE( m_pWaveFile ); 286 } 287
288
289
290
291 //----------------------------------------------------------------------------- 292 // Name: CSound::FillBufferWithSound() 293 // Desc: Fills a DirectSound buffer with a sound file 294 //-----------------------------------------------------------------------------
295 HRESULT CSound::FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB, BOOL bRepeatWavIfBufferLarger ) 296 { 297 HRESULT hr; 298 VOID* pDSLockedBuffer = NULL; // Pointer to locked buffer memory
299 DWORD dwDSLockedBufferSize = 0; // Size of the locked DirectSound buffer
300 DWORD dwWavDataRead = 0; // Amount of data read from the wav file
301
302 if( pDSB == NULL ) 303 return CO_E_NOTINITIALIZED; 304
305 // Make sure we have focus, and we didn't just switch in from 306 // an app which had a DirectSound device
307 if( FAILED( hr = RestoreBuffer( pDSB, NULL ) ) ) 308 return DXTRACE_ERR( TEXT("RestoreBuffer"), hr ); 309
310 // Lock the buffer down
311 if( FAILED( hr = pDSB->Lock( 0, m_dwDSBufferSize, 312 &pDSLockedBuffer, &dwDSLockedBufferSize, 313 NULL, NULL, 0L ) ) ) 314 return DXTRACE_ERR( TEXT("Lock"), hr ); 315
316 // Reset the wave file to the beginning
317 m_pWaveFile->ResetFile(); 318
319 if( FAILED( hr = m_pWaveFile->Read( (BYTE*) pDSLockedBuffer, 320 dwDSLockedBufferSize, 321 &dwWavDataRead ) ) ) 322 return DXTRACE_ERR( TEXT("Read"), hr ); 323
324 if( dwWavDataRead == 0 ) 325 { 326 // Wav is blank, so just fill with silence
327 FillMemory( (BYTE*) pDSLockedBuffer, 328 dwDSLockedBufferSize, 329 (BYTE)(m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) ); 330 } 331 else if( dwWavDataRead < dwDSLockedBufferSize ) 332 { 333 // If the wav file was smaller than the DirectSound buffer, 334 // we need to fill the remainder of the buffer with data
335 if( bRepeatWavIfBufferLarger ) 336 { 337 // Reset the file and fill the buffer with wav data
338 DWORD dwReadSoFar = dwWavDataRead; // From previous call above.
339 while( dwReadSoFar < dwDSLockedBufferSize ) 340 { 341 // This will keep reading in until the buffer is full 342 // for very short files
343 if( FAILED( hr = m_pWaveFile->ResetFile() ) ) 344 return DXTRACE_ERR( TEXT("ResetFile"), hr ); 345
346 hr = m_pWaveFile->Read( (BYTE*)pDSLockedBuffer + dwReadSoFar, 347 dwDSLockedBufferSize - dwReadSoFar, 348 &dwWavDataRead ); 349 if( FAILED(hr) ) 350 return DXTRACE_ERR( TEXT("Read"), hr ); 351
352 dwReadSoFar += dwWavDataRead; 353 } 354 } 355 else
356 { 357 // Don't repeat the wav file, just fill in silence
358 FillMemory( (BYTE*) pDSLockedBuffer + dwWavDataRead, 359 dwDSLockedBufferSize - dwWavDataRead, 360 (BYTE)(m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) ); 361 } 362 } 363
364 // Unlock the buffer, we don't need it anymore.
365 pDSB->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 ); 366
367 return S_OK; 368 } 369
370
371
372
373 //----------------------------------------------------------------------------- 374 // Name: CSound::RestoreBuffer() 375 // Desc: Restores the lost buffer. *pbWasRestored returns TRUE if the buffer was 376 // restored. It can also NULL if the information is not needed. 377 //-----------------------------------------------------------------------------
378 HRESULT CSound::RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, BOOL* pbWasRestored ) 379 { 380 HRESULT hr; 381
382 if( pDSB == NULL ) 383 return CO_E_NOTINITIALIZED; 384 if( pbWasRestored ) 385 *pbWasRestored = FALSE; 386
387 DWORD dwStatus; 388 if( FAILED( hr = pDSB->GetStatus( &dwStatus ) ) ) 389 return DXTRACE_ERR( TEXT("GetStatus"), hr ); 390
391 if( dwStatus & DSBSTATUS_BUFFERLOST ) 392 { 393 // Since the app could have just been activated, then 394 // DirectSound may not be giving us control yet, so 395 // the restoring the buffer may fail. 396 // If it does, sleep until DirectSound gives us control.
397 do
398 { 399 hr = pDSB->Restore(); 400 if( hr == DSERR_BUFFERLOST ) 401 Sleep( 10 ); 402 } 403 while( ( hr = pDSB->Restore() ) == DSERR_BUFFERLOST ); 404
405 if( pbWasRestored != NULL ) 406 *pbWasRestored = TRUE; 407
408 return S_OK; 409 } 410 else
411 { 412 return S_FALSE; 413 } 414 } 415
416
417
418
419 //----------------------------------------------------------------------------- 420 // Name: CSound::GetFreeBuffer() 421 // Desc: Finding the first buffer that is not playing and return a pointer to 422 // it, or if all are playing return a pointer to a randomly selected buffer. 423 //-----------------------------------------------------------------------------
424 LPDIRECTSOUNDBUFFER CSound::GetFreeBuffer() 425 { 426 if( m_apDSBuffer == NULL ) 427 return FALSE; 428 DWORD i; 429 for( i=0; i<m_dwNumBuffers; i++ ) 430 { 431 if( m_apDSBuffer[i] ) 432 { 433 DWORD dwStatus = 0; 434 m_apDSBuffer[i]->GetStatus( &dwStatus ); 435 if ( ( dwStatus & DSBSTATUS_PLAYING ) == 0 ) 436 break; 437 } 438 } 439
440 if( i != m_dwNumBuffers ) 441 return m_apDSBuffer[ i ]; 442 else
443 return m_apDSBuffer[ rand() % m_dwNumBuffers ]; 444 } 445
446
447
448 //----------------------------------------------------------------------------- 449 // Name: CSound::Play() 450 // Desc: Plays the sound using voice management flags. Pass in DSBPLAY_LOOPING 451 // in the dwFlags to loop the sound 452 //-----------------------------------------------------------------------------
453 HRESULT CSound::Play( DWORD dwPriority, DWORD dwFlags, LONG lVolume, LONG lFrequency, LONG lPan ) 454 { 455 HRESULT hr; 456 BOOL bRestored; 457
458 if( m_apDSBuffer == NULL ) 459 return CO_E_NOTINITIALIZED; 460
461 LPDIRECTSOUNDBUFFER pDSB = GetFreeBuffer(); 462
463 if( pDSB == NULL ) 464 return DXTRACE_ERR( TEXT("GetFreeBuffer"), E_FAIL ); 465
466 // Restore the buffer if it was lost
467 if( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) ) 468 return DXTRACE_ERR( TEXT("RestoreBuffer"), hr ); 469
470 if( bRestored ) 471 { 472 // The buffer was restored, so we need to fill it with new data
473 if( FAILED( hr = FillBufferWithSound( pDSB, FALSE ) ) ) 474 return DXTRACE_ERR( TEXT("FillBufferWithSound"), hr ); 475 } 476
477 if( m_dwCreationFlags & DSBCAPS_CTRLVOLUME ) 478 { 479 pDSB->SetVolume( lVolume ); 480 } 481
482 if( lFrequency != -1 &&
483 (m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY) ) 484 { 485 pDSB->SetFrequency( lFrequency ); 486 } 487
488 if( m_dwCreationFlags & DSBCAPS_CTRLPAN ) 489 { 490 pDSB->SetPan( lPan ); 491 } 492
493 return pDSB->Play( 0, dwPriority, dwFlags ); 494 } 495
496
497
498 //----------------------------------------------------------------------------- 499 // Name: CSound::Stop() 500 // Desc: Stops the sound from playing 501 //-----------------------------------------------------------------------------
502 HRESULT CSound::Stop() 503 { 504 if( m_apDSBuffer == NULL ) 505 return CO_E_NOTINITIALIZED; 506
507 HRESULT hr = 0; 508
509 for( DWORD i=0; i<m_dwNumBuffers; i++ ) 510 hr |= m_apDSBuffer[i]->Stop(); 511
512 return hr; 513 } 514
515
516
517
518 //----------------------------------------------------------------------------- 519 // Name: CSound::Reset() 520 // Desc: Reset all of the sound buffers 521 //-----------------------------------------------------------------------------
522 HRESULT CSound::Reset() 523 { 524 if( m_apDSBuffer == NULL ) 525 return CO_E_NOTINITIALIZED; 526
527 HRESULT hr = 0; 528
529 for( DWORD i=0; i<m_dwNumBuffers; i++ ) 530 hr |= m_apDSBuffer[i]->SetCurrentPosition( 0 ); 531
532 return hr; 533 } 534
535
536
537
538 //----------------------------------------------------------------------------- 539 // Name: CSound::IsSoundPlaying() 540 // Desc: Checks to see if a buffer is playing and returns TRUE if it is. 541 //-----------------------------------------------------------------------------
542 BOOL CSound::IsSoundPlaying() 543 { 544 BOOL bIsPlaying = FALSE; 545
546 if( m_apDSBuffer == NULL ) 547 return FALSE; 548
549 for( DWORD i=0; i<m_dwNumBuffers; i++ ) 550 { 551 if( m_apDSBuffer[i] ) 552 { 553 DWORD dwStatus = 0; 554 m_apDSBuffer[i]->GetStatus( &dwStatus ); 555 bIsPlaying |= ( ( dwStatus & DSBSTATUS_PLAYING ) != 0 ); 556 } 557 } 558
559 return bIsPlaying; 560 } 561
562
563
564 //----------------------------------------------------------------------------- 565 // Name: CWaveFile::CWaveFile() 566 // Desc: Constructs the class. Call Open() to open a wave file for reading. 567 // Then call Read() as needed. Calling the destructor or Close() 568 // will close the file. 569 //-----------------------------------------------------------------------------
570 CWaveFile::CWaveFile() 571 { 572 m_pwfx = NULL; 573 m_hmmio = NULL; 574 m_pResourceBuffer = NULL; 575 m_dwSize = 0; 576 m_bIsReadingFromMemory = FALSE; 577 } 578
579
580
581
582 //----------------------------------------------------------------------------- 583 // Name: CWaveFile::~CWaveFile() 584 // Desc: Destructs the class 585 //-----------------------------------------------------------------------------
586 CWaveFile::~CWaveFile() 587 { 588 Close(); 589
590 if( !m_bIsReadingFromMemory ) 591 SAFE_DELETE_ARRAY( m_pwfx ); 592 } 593
594
595
596
597 //----------------------------------------------------------------------------- 598 // Name: CWaveFile::Open() 599 // Desc: Opens a wave file for reading 600 //-----------------------------------------------------------------------------
601 HRESULT CWaveFile::Open( LPTSTR strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags ) 602 { 603 HRESULT hr; 604
605 m_dwFlags = dwFlags; 606 m_bIsReadingFromMemory = FALSE; 607
608 if( m_dwFlags == WAVEFILE_READ ) 609 { 610 if( strFileName == NULL ) 611 return E_INVALIDARG; 612 SAFE_DELETE_ARRAY( m_pwfx ); 613
614 m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF | MMIO_READ ); 615
616 if( NULL == m_hmmio ) 617 { 618 HRSRC hResInfo; 619 HGLOBAL hResData; 620 DWORD dwSize; 621 VOID* pvRes; 622
623 // Loading it as a file failed, so try it as a resource
624 if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAVE") ) ) ) 625 { 626 if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAV") ) ) ) 627 return DXTRACE_ERR( TEXT("FindResource"), E_FAIL ); 628 } 629
630 if( NULL == ( hResData = LoadResource( NULL, hResInfo ) ) ) 631 return DXTRACE_ERR( TEXT("LoadResource"), E_FAIL ); 632
633 if( 0 == ( dwSize = SizeofResource( NULL, hResInfo ) ) ) 634 return DXTRACE_ERR( TEXT("SizeofResource"), E_FAIL ); 635
636 if( NULL == ( pvRes = LockResource( hResData ) ) ) 637 return DXTRACE_ERR( TEXT("LockResource"), E_FAIL ); 638
639 m_pResourceBuffer = new CHAR[ dwSize ]; 640 memcpy( m_pResourceBuffer, pvRes, dwSize ); 641
642 MMIOINFO mmioInfo; 643 ZeroMemory( &mmioInfo, sizeof(mmioInfo) ); 644 mmioInfo.fccIOProc = FOURCC_MEM; 645 mmioInfo.cchBuffer = dwSize; 646 mmioInfo.pchBuffer = (CHAR*) m_pResourceBuffer; 647
648 m_hmmio = mmioOpen( NULL, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ ); 649 } 650
651 if( FAILED( hr = ReadMMIO() ) ) 652 { 653 // ReadMMIO will fail if its an not a wave file
654 mmioClose( m_hmmio, 0 ); 655 return DXTRACE_ERR( TEXT("ReadMMIO"), hr ); 656 } 657
658 if( FAILED( hr = ResetFile() ) ) 659 return DXTRACE_ERR( TEXT("ResetFile"), hr ); 660
661 // After the reset, the size of the wav file is m_ck.cksize so store it now
662 m_dwSize = m_ck.cksize; 663 } 664 else
665 { 666 m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF |
667 MMIO_READWRITE |
668 MMIO_CREATE ); 669 if( NULL == m_hmmio ) 670 return DXTRACE_ERR( TEXT("mmioOpen"), E_FAIL ); 671
672 if( FAILED( hr = WriteMMIO( pwfx ) ) ) 673 { 674 mmioClose( m_hmmio, 0 ); 675 return DXTRACE_ERR( TEXT("WriteMMIO"), hr ); 676 } 677
678 if( FAILED( hr = ResetFile() ) ) 679 return DXTRACE_ERR( TEXT("ResetFile"), hr ); 680 } 681
682 return hr; 683 } 684
685
686
687 //----------------------------------------------------------------------------- 688 // Name: CWaveFile::ReadMMIO() 689 // Desc: Support function for reading from a multimedia I/O stream. 690 // m_hmmio must be valid before calling. This function uses it to 691 // update m_ckRiff, and m_pwfx. 692 //-----------------------------------------------------------------------------
693 HRESULT CWaveFile::ReadMMIO() 694 { 695 MMCKINFO ckIn; // chunk info. for general use.
696 PCMWAVEFORMAT pcmWaveFormat; // Temp PCM structure to load in.
697
698 m_pwfx = NULL; 699
700 if( ( 0 != mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) ) ) 701 return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL ); 702
703 // Check to make sure this is a valid wave file
704 if( (m_ckRiff.ckid != FOURCC_RIFF) ||
705 (m_ckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E') ) ) 706 return DXTRACE_ERR( TEXT("mmioFOURCC"), E_FAIL ); 707
708 // Search the input file for for the 'fmt ' chunk.
709 ckIn.ckid = mmioFOURCC('f', 'm', 't', ' '); 710 if( 0 != mmioDescend( m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK ) ) 711 return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL ); 712
713 // Expect the 'fmt' chunk to be at least as large as <PCMWAVEFORMAT>; 714 // if there are extra parameters at the end, we'll ignore them
715 if( ckIn.cksize < (LONG) sizeof(PCMWAVEFORMAT) ) 716 return DXTRACE_ERR( TEXT("sizeof(PCMWAVEFORMAT)"), E_FAIL ); 717
718 // Read the 'fmt ' chunk into <pcmWaveFormat>.
719 if( mmioRead( m_hmmio, (HPSTR) &pcmWaveFormat, 720 sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat) ) 721 return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL ); 722
723 // Allocate the waveformatex, but if its not pcm format, read the next 724 // word, and thats how many extra bytes to allocate.
725 if( pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM ) 726 { 727 m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) ]; 728 if( NULL == m_pwfx ) 729 return DXTRACE_ERR( TEXT("m_pwfx"), E_FAIL ); 730
731 // Copy the bytes from the pcm structure to the waveformatex structure
732 memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) ); 733 m_pwfx->cbSize = 0; 734 } 735 else
736 { 737 // Read in length of extra bytes.
738 WORD cbExtraBytes = 0L; 739 if( mmioRead( m_hmmio, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD) ) 740 return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL ); 741
742 m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) + cbExtraBytes ]; 743 if( NULL == m_pwfx ) 744 return DXTRACE_ERR( TEXT("new"), E_FAIL ); 745
746 // Copy the bytes from the pcm structure to the waveformatex structure
747 memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) ); 748 m_pwfx->cbSize = cbExtraBytes; 749
750 // Now, read those extra bytes into the structure, if cbExtraAlloc != 0.
751 if( mmioRead( m_hmmio, (CHAR*)(((BYTE*)&(m_pwfx->cbSize))+sizeof(WORD)), 752 cbExtraBytes ) != cbExtraBytes ) 753 { 754 SAFE_DELETE( m_pwfx ); 755 return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL ); 756 } 757 } 758
759 // Ascend the input file out of the 'fmt ' chunk.
760 if( 0 != mmioAscend( m_hmmio, &ckIn, 0 ) ) 761 { 762 SAFE_DELETE( m_pwfx ); 763 return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL ); 764 } 765
766 return S_OK; 767 } 768
769
770
771
772 //----------------------------------------------------------------------------- 773 // Name: CWaveFile::GetSize() 774 // Desc: Retuns the size of the read access wave file 775 //-----------------------------------------------------------------------------
776 DWORD CWaveFile::GetSize() 777 { 778 return m_dwSize; 779 } 780
781
782
783
784 //----------------------------------------------------------------------------- 785 // Name: CWaveFile::ResetFile() 786 // Desc: Resets the internal m_ck pointer so reading starts from the 787 // beginning of the file again 788 //-----------------------------------------------------------------------------
789 HRESULT CWaveFile::ResetFile() 790 { 791 if( m_bIsReadingFromMemory ) 792 { 793 m_pbDataCur = m_pbData; 794 } 795 else
796 { 797 if( m_hmmio == NULL ) 798 return CO_E_NOTINITIALIZED; 799
800 if( m_dwFlags == WAVEFILE_READ ) 801 { 802 // Seek to the data
803 if( -1 == mmioSeek( m_hmmio, m_ckRiff.dwDataOffset + sizeof(FOURCC), 804 SEEK_SET ) ) 805 return DXTRACE_ERR( TEXT("mmioSeek"), E_FAIL ); 806
807 // Search the input file for the 'data' chunk.
808 m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a'); 809 if( 0 != mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) ) 810 return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL ); 811 } 812 else
813 { 814 // Create the 'data' chunk that holds the waveform samples.
815 m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a'); 816 m_ck.cksize = 0; 817
818 if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) ) 819 return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL ); 820
821 if( 0 != mmioGetInfo( m_hmmio, &m_mmioinfoOut, 0 ) ) 822 return DXTRACE_ERR( TEXT("mmioGetInfo"), E_FAIL ); 823 } 824 } 825
826 return S_OK; 827 } 828
829
830
831
832 //----------------------------------------------------------------------------- 833 // Name: CWaveFile::Read() 834 // Desc: Reads section of data from a wave file into pBuffer and returns 835 // how much read in pdwSizeRead, reading not more than dwSizeToRead. 836 // This uses m_ck to determine where to start reading from. So 837 // subsequent calls will be continue where the last left off unless 838 // Reset() is called. 839 //-----------------------------------------------------------------------------
840 HRESULT CWaveFile::Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead ) 841 { 842 if( m_bIsReadingFromMemory ) 843 { 844 if( m_pbDataCur == NULL ) 845 return CO_E_NOTINITIALIZED; 846 if( pdwSizeRead != NULL ) 847 *pdwSizeRead = 0; 848
849 if( (BYTE*)(m_pbDataCur + dwSizeToRead) >
850 (BYTE*)(m_pbData + m_ulDataSize) ) 851 { 852 dwSizeToRead = m_ulDataSize - (DWORD)(m_pbDataCur - m_pbData); 853 } 854
855 CopyMemory( pBuffer, m_pbDataCur, dwSizeToRead ); 856
857 if( pdwSizeRead != NULL ) 858 *pdwSizeRead = dwSizeToRead; 859
860 return S_OK; 861 } 862 else
863 { 864 MMIOINFO mmioinfoIn; // current status of m_hmmio
865
866 if( m_hmmio == NULL ) 867 return CO_E_NOTINITIALIZED; 868 if( pBuffer == NULL || pdwSizeRead == NULL ) 869 return E_INVALIDARG; 870
871 if( pdwSizeRead != NULL ) 872 *pdwSizeRead = 0; 873
874 if( 0 != mmioGetInfo( m_hmmio, &mmioinfoIn, 0 ) ) 875 return DXTRACE_ERR( TEXT("mmioGetInfo"), E_FAIL ); 876
877 UINT cbDataIn = dwSizeToRead; 878 if( cbDataIn > m_ck.cksize ) 879 cbDataIn = m_ck.cksize; 880
881 m_ck.cksize -= cbDataIn; 882
883 for( DWORD cT = 0; cT < cbDataIn; cT++ ) 884 { 885 // Copy the bytes from the io to the buffer.
886 if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead ) 887 { 888 if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) ) 889 return DXTRACE_ERR( TEXT("mmioAdvance"), E_FAIL ); 890
891 if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead ) 892 return DXTRACE_ERR( TEXT("mmioinfoIn.pchNext"), E_FAIL ); 893 } 894
895 // Actual copy.
896 *((BYTE*)pBuffer+cT) = *((BYTE*)mmioinfoIn.pchNext); 897 mmioinfoIn.pchNext++; 898 } 899
900 if( 0 != mmioSetInfo( m_hmmio, &mmioinfoIn, 0 ) ) 901 return DXTRACE_ERR( TEXT("mmioSetInfo"), E_FAIL ); 902
903 if( pdwSizeRead != NULL ) 904 *pdwSizeRead = cbDataIn; 905
906 return S_OK; 907 } 908 } 909
910
911
912
913 //----------------------------------------------------------------------------- 914 // Name: CWaveFile::Close() 915 // Desc: Closes the wave file 916 //-----------------------------------------------------------------------------
917 HRESULT CWaveFile::Close() 918 { 919 if( m_dwFlags == WAVEFILE_READ ) 920 { 921 mmioClose( m_hmmio, 0 ); 922 m_hmmio = NULL; 923 SAFE_DELETE_ARRAY( m_pResourceBuffer ); 924 } 925 else
926 { 927 m_mmioinfoOut.dwFlags |= MMIO_DIRTY; 928
929 if( m_hmmio == NULL ) 930 return CO_E_NOTINITIALIZED; 931
932 if( 0 != mmioSetInfo( m_hmmio, &m_mmioinfoOut, 0 ) ) 933 return DXTRACE_ERR( TEXT("mmioSetInfo"), E_FAIL ); 934
935 // Ascend the output file out of the 'data' chunk -- this will cause 936 // the chunk size of the 'data' chunk to be written.
937 if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) ) 938 return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL ); 939
940 // Do this here instead...
941 if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) ) 942 return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL ); 943
944 mmioSeek( m_hmmio, 0, SEEK_SET ); 945
946 if( 0 != (INT)mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) ) 947 return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL ); 948
949 m_ck.ckid = mmioFOURCC('f', 'a', 'c', 't'); 950
951 if( 0 == mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) ) 952 { 953 DWORD dwSamples = 0; 954 mmioWrite( m_hmmio, (HPSTR)&dwSamples, sizeof(DWORD) ); 955 mmioAscend( m_hmmio, &m_ck, 0 ); 956 } 957
958 // Ascend the output file out of the 'RIFF' chunk -- this will cause 959 // the chunk size of the 'RIFF' chunk to be written.
960 if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) ) 961 return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL ); 962
963 mmioClose( m_hmmio, 0 ); 964 m_hmmio = NULL; 965 } 966
967 return S_OK; 968 } 969
970
971
972
973 //----------------------------------------------------------------------------- 974 // Name: CWaveFile::WriteMMIO() 975 // Desc: Support function for reading from a multimedia I/O stream 976 // pwfxDest is the WAVEFORMATEX for this new wave file. 977 // m_hmmio must be valid before calling. This function uses it to 978 // update m_ckRiff, and m_ck. 979 //-----------------------------------------------------------------------------
980 HRESULT CWaveFile::WriteMMIO( WAVEFORMATEX *pwfxDest ) 981 { 982 DWORD dwFactChunk; // Contains the actual fact chunk. Garbage until WaveCloseWriteFile.
983 MMCKINFO ckOut1; 984
985 dwFactChunk = (DWORD)-1; 986
987 // Create the output file RIFF chunk of form type 'WAVE'.
988 m_ckRiff.fccType = mmioFOURCC('W', 'A', 'V', 'E'); 989 m_ckRiff.cksize = 0; 990
991 if( 0 != mmioCreateChunk( m_hmmio, &m_ckRiff, MMIO_CREATERIFF ) ) 992 return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL ); 993
994 // We are now descended into the 'RIFF' chunk we just created. 995 // Now create the 'fmt ' chunk. Since we know the size of this chunk, 996 // specify it in the MMCKINFO structure so MMIO doesn't have to seek 997 // back and set the chunk size after ascending from the chunk.
998 m_ck.ckid = mmioFOURCC('f', 'm', 't', ' '); 999 m_ck.cksize = sizeof(PCMWAVEFORMAT); 1000
1001 if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) ) 1002 return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL ); 1003
1004 // Write the PCMWAVEFORMAT structure to the 'fmt ' chunk if its that type.
1005 if( pwfxDest->wFormatTag == WAVE_FORMAT_PCM ) 1006 { 1007 if( mmioWrite( m_hmmio, (HPSTR) pwfxDest, 1008 sizeof(PCMWAVEFORMAT)) != sizeof(PCMWAVEFORMAT)) 1009 return DXTRACE_ERR( TEXT("mmioWrite"), E_FAIL ); 1010 } 1011 else
1012 { 1013 // Write the variable length size.
1014 if( (UINT)mmioWrite( m_hmmio, (HPSTR) pwfxDest, 1015 sizeof(*pwfxDest) + pwfxDest->cbSize ) !=
1016 ( sizeof(*pwfxDest) + pwfxDest->cbSize ) ) 1017 return DXTRACE_ERR( TEXT("mmioWrite"), E_FAIL ); 1018 } 1019
1020 // Ascend out of the 'fmt ' chunk, back into the 'RIFF' chunk.
1021 if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) ) 1022 return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL ); 1023
1024 // Now create the fact chunk, not required for PCM but nice to have. This is filled 1025 // in when the close routine is called.
1026 ckOut1.ckid = mmioFOURCC('f', 'a', 'c', 't'); 1027 ckOut1.cksize = 0; 1028
1029 if( 0 != mmioCreateChunk( m_hmmio, &ckOut1, 0 ) ) 1030 return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL ); 1031
1032 if( mmioWrite( m_hmmio, (HPSTR)&dwFactChunk, sizeof(dwFactChunk)) !=
1033 sizeof(dwFactChunk) ) 1034 return DXTRACE_ERR( TEXT("mmioWrite"), E_FAIL ); 1035
1036 // Now ascend out of the fact chunk...
1037 if( 0 != mmioAscend( m_hmmio, &ckOut1, 0 ) ) 1038 return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL ); 1039
1040 return S_OK; 1041 } 1042
1043
1044
1045
1046 //----------------------------------------------------------------------------- 1047 // Name: CWaveFile::Write() 1048 // Desc: Writes data to the open wave file 1049 //-----------------------------------------------------------------------------
1050 HRESULT CWaveFile::Write( UINT nSizeToWrite, BYTE* pbSrcData, UINT* pnSizeWrote ) 1051 { 1052 UINT cT; 1053
1054 if( m_bIsReadingFromMemory ) 1055 return E_NOTIMPL; 1056 if( m_hmmio == NULL ) 1057 return CO_E_NOTINITIALIZED; 1058 if( pnSizeWrote == NULL || pbSrcData == NULL ) 1059 return E_INVALIDARG; 1060
1061 *pnSizeWrote = 0; 1062
1063 for( cT = 0; cT < nSizeToWrite; cT++ ) 1064 { 1065 if( m_mmioinfoOut.pchNext == m_mmioinfoOut.pchEndWrite ) 1066 { 1067 m_mmioinfoOut.dwFlags |= MMIO_DIRTY; 1068 if( 0 != mmioAdvance( m_hmmio, &m_mmioinfoOut, MMIO_WRITE ) ) 1069 return DXTRACE_ERR( TEXT("mmioAdvance"), E_FAIL ); 1070 } 1071
1072 *((BYTE*)m_mmioinfoOut.pchNext) = *((BYTE*)pbSrcData+cT); 1073 (BYTE*)m_mmioinfoOut.pchNext++; 1074
1075 (*pnSizeWrote)++; 1076 } 1077
1078 return S_OK; 1079 }
然后是我们自己的文件,先看到头文件DirectX.h,加入了新的支持声音播放的函数
1 #pragma once
2
3 //header files
4 #define WIN32_EXTRA_LEAN
5 #define DIRECTINPUT_VERSION 0x0800
6 #include <windows.h>
7 #include <d3d9.h>
8 #include <d3dx9.h>
9 #include <dxerr.h>
10 #include <dinput.h>
11 #include <xinput.h>
12 #include <ctime>
13 #include <iostream>
14 #include <iomanip>
15 #include "DirectSound.h"
16 using namespace std; 17
18 //libraries
19 #pragma comment(lib,"winmm.lib")
20 #pragma comment(lib,"user32.lib")
21 #pragma comment(lib,"gdi32.lib")
22 #pragma comment(lib,"dxguid.lib")
23 #pragma comment(lib,"d3d9.lib")
24 #pragma comment(lib,"d3dx9.lib")
25 #pragma comment(lib, "dxerr.lib")
26 #pragma comment(lib,"dinput8.lib")
27 #pragma comment(lib,"xinput.lib")
28
29 //program values
30 extern const string APPTITLE; 31 extern const int SCREENW; 32 extern const int SCREENH; 33 extern bool gameover; 34
35 //macro to detect key presses
36 #define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
37
38 //Direct3D objects
39 extern LPDIRECT3D9 d3d; 40 extern LPDIRECT3DDEVICE9 d3ddev; 41 extern LPDIRECT3DSURFACE9 backbuffer; 42 extern LPD3DXSPRITE spriteobj; 43
44 //DirectSound object
45 extern CSoundManager *dsound; 46
47 //sprite structure
48 struct SPRITE 49 { 50 float x,y; 51 int frame, columns; 52 int width, height; 53 float scaling, rotation; 54 int startframe, endframe; 55 int starttime, delay; 56 int direction; 57 float velx, vely; 58 D3DCOLOR color; 59
60 SPRITE() 61 { 62 frame = 0; 63 columns = 1; 64 width = height = 0; 65 scaling = 1.0f; 66 rotation = 0.0f; 67 startframe = endframe = 0; 68 direction = 1; 69 starttime = delay = 0; 70 velx = vely = 0.0f; 71 color = D3DCOLOR_XRGB(255,255,255); 72 } 73 }; 74
75 //Direct3D functions
76 bool Direct3D_Init(HWND hwnd, int width, int height, bool fullscreen); 77 void Direct3D_Shutdown(); 78 LPDIRECT3DSURFACE9 LoadSurface(string filename); 79 void DrawSurface(LPDIRECT3DSURFACE9 dest, float x, float y, LPDIRECT3DSURFACE9 source); 80 LPDIRECT3DTEXTURE9 LoadTexture(string filename, D3DCOLOR transcolor = D3DCOLOR_XRGB(0,0,0)); 81 void Sprite_Draw_Frame(LPDIRECT3DTEXTURE9 texture, int destx, int desty, 82 int framenum, int framew, int frameh, int columns); 83 void Sprite_Animate(int &frame, int startframe, int endframe, int direction, int &starttime, int delay); 84
85 void Sprite_Transform_Draw(LPDIRECT3DTEXTURE9 image, int x, int y, int width, int height, 86 int frame = 0, int columns = 1, float rotation = 0.0f, float scaling = 1.0f, 87 D3DCOLOR color = D3DCOLOR_XRGB(255,255,255)); 88
89 //bounding box collision detection
90 int Collision(SPRITE sprite1, SPRITE sprite2); 91
92 //distance based collision detection
93 bool CollisionD(SPRITE sprite1, SPRITE sprite2); 94
95 //DirectInput objects, devices, and states
96 extern LPDIRECTINPUT8 dinput; 97 extern LPDIRECTINPUTDEVICE8 dimouse; 98 extern LPDIRECTINPUTDEVICE8 dikeyboard; 99 extern DIMOUSESTATE mouse_state; 100 extern XINPUT_GAMEPAD controllers[4]; 101
102 //DirectInput functions
103 bool DirectInput_Init(HWND); 104 void DirectInput_Update(); 105 void DirectInput_Shutdown(); 106 bool Key_Down(int); 107 int Mouse_Button(int); 108 int Mouse_X(); 109 int Mouse_Y(); 110 void XInput_Vibrate(int contNum = 0, int amount = 65535); 111 bool XInput_Controller_Found(); 112
113 //game functions
114 bool Game_Init(HWND window); 115 void Game_Run(HWND window); 116 void Game_End(); 117
118
119 //font functions
120 LPD3DXFONT MakeFont(string name, int size); 121 void FontPrint(LPD3DXFONT font, int x, int y, string text, D3DCOLOR color = D3DCOLOR_XRGB(255,255,255)); 122
123 //DirectSound functions
124 bool DirectSound_Init(HWND hwnd); 125 void DirectSound_Shutdown(); 126 CSound* LoadSound(string filename); 127 void PlaySound(CSound *sound); 128 void LoopSound(CSound *sound); 129 void StopSound(CSound *sound);
接着是DirectX.cpp文件,先看看前面的一部分,和之前的一样
1 #include "DirectX.h"
2 #include <iostream>
3 #include <string>
4 using namespace std; 5
6 //DirectSound object
7 CSoundManager *dsound = NULL; 8
9 //Direct3D variables
10 LPDIRECT3D9 d3d = NULL; 11 LPDIRECT3DDEVICE9 d3ddev = NULL; 12 LPDIRECT3DSURFACE9 backbuffer = NULL; 13 LPD3DXSPRITE spriteobj; 14
15 //DirectInput variables
16 LPDIRECTINPUT8 dinput = NULL; 17 LPDIRECTINPUTDEVICE8 dimouse = NULL; 18 LPDIRECTINPUTDEVICE8 dikeyboard = NULL; 19 DIMOUSESTATE mouse_state; 20 char keys[256]; 21 XINPUT_GAMEPAD controllers[4]; 22
23
24 bool Direct3D_Init(HWND window, int width, int height, bool fullscreen) 25 { 26 //initialize Direct3D
27 d3d = Direct3DCreate9(D3D_SDK_VERSION); 28 if (!d3d) return false; 29
30 //set Direct3D presentation parameters
31 D3DPRESENT_PARAMETERS d3dpp; 32 ZeroMemory(&d3dpp, sizeof(d3dpp)); 33 d3dpp.hDeviceWindow = window; 34 d3dpp.Windowed = (!fullscreen); 35 d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; 36 d3dpp.EnableAutoDepthStencil = 1; 37 d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; 38 d3dpp.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL; 39 d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; 40 d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8; 41 d3dpp.BackBufferCount = 1; 42 d3dpp.BackBufferWidth = width; 43 d3dpp.BackBufferHeight = height; 44
45 //create Direct3D device
46 d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window, 47 D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev); 48 if (!d3ddev) return false; 49
50
51 //get a pointer to the back buffer surface
52 d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer); 53
54 //create sprite object
55 D3DXCreateSprite(d3ddev, &spriteobj); 56
57 return 1; 58 } 59
60 void Direct3D_Shutdown() 61 { 62 if (spriteobj) spriteobj->Release(); 63
64 if (d3ddev) d3ddev->Release(); 65 if (d3d) d3d->Release(); 66 } 67
68 void DrawSurface(LPDIRECT3DSURFACE9 dest, float x, float y, LPDIRECT3DSURFACE9 source) 69 { 70 //get width/height from source surface
71 D3DSURFACE_DESC desc; 72 source->GetDesc(&desc); 73
74 //create rects for drawing
75 RECT source_rect = {0, 0, (long)desc.Width, (long)desc.Height }; 76 RECT dest_rect = { (long)x, (long)y, (long)x+desc.Width, (long)y+desc.Height}; 77
78 //draw the source surface onto the dest
79 d3ddev->StretchRect(source, &source_rect, dest, &dest_rect, D3DTEXF_NONE); 80
81 } 82
83 LPDIRECT3DSURFACE9 LoadSurface(string filename) 84 { 85 LPDIRECT3DSURFACE9 image = NULL; 86
87 //get width and height from bitmap file
88 D3DXIMAGE_INFO info; 89 HRESULT result = D3DXGetImageInfoFromFile(filename.c_str(), &info); 90 if (result != D3D_OK) 91 return NULL; 92
93 //create surface
94 result = d3ddev->CreateOffscreenPlainSurface( 95 info.Width, //width of the surface
96 info.Height, //height of the surface
97 D3DFMT_X8R8G8B8, //surface format
98 D3DPOOL_DEFAULT, //memory pool to use
99 &image, //pointer to the surface
100 NULL); //reserved (always NULL)
101
102 if (result != D3D_OK) return NULL; 103
104 //load surface from file into newly created surface
105 result = D3DXLoadSurfaceFromFile( 106 image, //destination surface
107 NULL, //destination palette
108 NULL, //destination rectangle
109 filename.c_str(), //source filename
110 NULL, //source rectangle
111 D3DX_DEFAULT, //controls how image is filtered
112 D3DCOLOR_XRGB(0,0,0), //for transparency (0 for none)
113 NULL); //source image info (usually NULL) 114
115 //make sure file was loaded okay
116 if (result != D3D_OK) return NULL; 117
118 return image; 119 } 120
121
122 LPDIRECT3DTEXTURE9 LoadTexture(std::string filename, D3DCOLOR transcolor) 123 { 124 LPDIRECT3DTEXTURE9 texture = NULL; 125
126 //get width and height from bitmap file
127 D3DXIMAGE_INFO info; 128 HRESULT result = D3DXGetImageInfoFromFile(filename.c_str(), &info); 129 if (result != D3D_OK) return NULL; 130
131 //create the new texture by loading a bitmap image file
132 D3DXCreateTextureFromFileEx( 133 d3ddev, //Direct3D device object
134 filename.c_str(), //bitmap filename
135 info.Width, //bitmap image width
136 info.Height, //bitmap image height
137 1, //mip-map levels (1 for no chain)
138 D3DPOOL_DEFAULT, //the type of surface (standard)
139 D3DFMT_UNKNOWN, //surface format (default)
140 D3DPOOL_DEFAULT, //memory class for the texture
141 D3DX_DEFAULT, //image filter
142 D3DX_DEFAULT, //mip filter
143 transcolor, //color key for transparency
144 &info, //bitmap file info (from loaded file)
145 NULL, //color palette
146 &texture ); //destination texture 147
148 //make sure the bitmap textre was loaded correctly
149 if (result != D3D_OK) return NULL; 150
151 return texture; 152 } 153
154
155 void Sprite_Draw_Frame(LPDIRECT3DTEXTURE9 texture, int destx, int desty, int framenum, int framew, int frameh, int columns) 156 { 157 D3DXVECTOR3 position( (float)destx, (float)desty, 0 ); 158 D3DCOLOR white = D3DCOLOR_XRGB(255,255,255); 159
160 RECT rect; 161 rect.left = (framenum % columns) * framew; 162 rect.top = (framenum / columns) * frameh; 163 rect.right = rect.left + framew; 164 rect.bottom = rect.top + frameh; 165
166 spriteobj->Draw( texture, &rect, NULL, &position, white); 167 } 168
169 void Sprite_Animate(int &frame, int startframe, int endframe, int direction, int &starttime, int delay) 170 { 171 if ((int)GetTickCount() > starttime + delay) 172 { 173 starttime = GetTickCount(); 174
175 frame += direction; 176 if (frame > endframe) frame = startframe; 177 if (frame < startframe) frame = endframe; 178 } 179 } 180 void Sprite_Transform_Draw(LPDIRECT3DTEXTURE9 image, int x, int y, int width, int height, 181 int frame, int columns, float rotation, float scaling, D3DCOLOR color) 182 { 183 //create a scale vector
184 D3DXVECTOR2 scale( scaling, scaling ); 185
186 //create a translate vector
187 D3DXVECTOR2 trans( x, y ); 188
189 //set center by dividing width and height by two
190 D3DXVECTOR2 center( (float)( width * scaling )/2, (float)( height * scaling )/2); 191
192 //create 2D transformation matrix
193 D3DXMATRIX mat; 194 D3DXMatrixTransformation2D( &mat, NULL, 0, &scale, ¢er, rotation, &trans ); 195
196 //tell sprite object to use the transform
197 spriteobj->SetTransform( &mat ); 198
199 //calculate frame location in source image
200 int fx = (frame % columns) * width; 201 int fy = (frame / columns) * height; 202 RECT srcRect = {fx, fy, fx + width, fy + height}; 203
204 //draw the sprite frame
205 spriteobj->Draw( image, &srcRect, NULL, NULL, color ); 206 } 207
208 //bounding box collision detection
209 int Collision(SPRITE sprite1, SPRITE sprite2) 210 { 211 RECT rect1; 212 rect1.left = (long)sprite1.x; 213 rect1.top = (long)sprite1.y; 214 rect1.right = (long)sprite1.x + sprite1.width * sprite1.scaling; 215 rect1.bottom = (long)sprite1.y + sprite1.height * sprite1.scaling; 216
217 RECT rect2; 218 rect2.left = (long)sprite2.x; 219 rect2.top = (long)sprite2.y; 220 rect2.right = (long)sprite2.x + sprite2.width * sprite2.scaling; 221 rect2.bottom = (long)sprite2.y + sprite2.height * sprite2.scaling; 222
223 RECT dest; //ignored
224 return IntersectRect(&dest, &rect1, &rect2); 225 } 226
227
228 bool CollisionD(SPRITE sprite1, SPRITE sprite2) 229 { 230 double radius1, radius2; 231
232 //calculate radius 1
233 if (sprite1.width > sprite1.height) 234 radius1 = (sprite1.width * sprite1.scaling) / 2.0; 235 else
236 radius1 = (sprite1.height * sprite1.scaling) / 2.0; 237
238 //center point 1
239 double x1 = sprite1.x + radius1; 240 double y1 = sprite1.y + radius1; 241 D3DXVECTOR2 vector1(x1, y1); 242
243 //calculate radius 2
244 if (sprite2.width > sprite2.height) 245 radius2 = (sprite2.width * sprite2.scaling) / 2.0; 246 else
247 radius2 = (sprite2.height * sprite2.scaling) / 2.0; 248
249 //center point 2
250 double x2 = sprite2.x + radius2; 251 double y2 = sprite2.y + radius2; 252 D3DXVECTOR2 vector2(x2, y2); 253
254 //calculate distance
255 double deltax = vector1.x - vector2.x; 256 double deltay = vector2.y - vector1.y; 257 double dist = sqrt((deltax * deltax) + (deltay * deltay)); 258
259 //return distance comparison
260 return (dist < radius1 + radius2); 261 } 262
263 bool DirectInput_Init(HWND hwnd) 264 { 265 //initialize DirectInput object
266 DirectInput8Create( 267 GetModuleHandle(NULL), 268 DIRECTINPUT_VERSION, 269 IID_IDirectInput8, 270 (void**)&dinput, 271 NULL); 272
273 //initialize the keyboard
274 dinput->CreateDevice(GUID_SysKeyboard, &dikeyboard, NULL); 275 dikeyboard->SetDataFormat(&c_dfDIKeyboard); 276 dikeyboard->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); 277 dikeyboard->Acquire(); 278
279 //initialize the mouse
280 dinput->CreateDevice(GUID_SysMouse, &dimouse, NULL); 281 dimouse->SetDataFormat(&c_dfDIMouse); 282 dimouse->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); 283 dimouse->Acquire(); 284 d3ddev->ShowCursor(false); 285
286 return true; 287 } 288
289 void DirectInput_Update() 290 { 291 //update mouse
292 dimouse->Poll(); 293 if (!SUCCEEDED(dimouse->GetDeviceState(sizeof(DIMOUSESTATE),&mouse_state))) 294 { 295 //mouse device lose, try to re-acquire
296 dimouse->Acquire(); 297 } 298
299 //update keyboard
300 dikeyboard->Poll(); 301 if (!SUCCEEDED(dikeyboard->GetDeviceState(256,(LPVOID)&keys))) 302 { 303 //keyboard device lost, try to re-acquire
304 dikeyboard->Acquire(); 305 } 306
307 //update controllers
308 for (int i=0; i< 4; i++ ) 309 { 310 ZeroMemory( &controllers[i], sizeof(XINPUT_STATE) ); 311
312 //get the state of the controller
313 XINPUT_STATE state; 314 DWORD result = XInputGetState( i, &state ); 315
316 //store state in global controllers array
317 if (result == 0) controllers[i] = state.Gamepad; 318 } 319 } 320
321
322 int Mouse_X() 323 { 324 return mouse_state.lX; 325 } 326
327 int Mouse_Y() 328 { 329 return mouse_state.lY; 330 } 331
332 int Mouse_Button(int button) 333 { 334 return mouse_state.rgbButtons[button] & 0x80; 335 } 336
337 bool Key_Down(int key) 338 { 339 return (bool)(keys[key] & 0x80); 340 } 341
342
343 void DirectInput_Shutdown() 344 { 345 if (dikeyboard) 346 { 347 dikeyboard->Unacquire(); 348 dikeyboard->Release(); 349 dikeyboard = NULL; 350 } 351 if (dimouse) 352 { 353 dimouse->Unacquire(); 354 dimouse->Release(); 355 dimouse = NULL; 356 } 357 } 358
359 bool XInput_Controller_Found() 360 { 361 XINPUT_CAPABILITIES caps; 362 ZeroMemory(&caps, sizeof(XINPUT_CAPABILITIES)); 363 XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps); 364 if (caps.Type != 0) return false; 365
366 return true; 367 } 368
369 void XInput_Vibrate(int contNum, int amount) 370 { 371 XINPUT_VIBRATION vibration; 372 ZeroMemory( &vibration, sizeof(XINPUT_VIBRATION) ); 373 vibration.wLeftMotorSpeed = amount; 374 vibration.wRightMotorSpeed = amount; 375 XInputSetState( contNum, &vibration ); 376 } 377
378 LPD3DXFONT MakeFont(string name, int size) 379 { 380 LPD3DXFONT font = NULL; 381
382 D3DXFONT_DESC desc = { 383 size, //height
384 0, //width
385 0, //weight
386 0, //miplevels
387 false, //italic
388 DEFAULT_CHARSET, //charset
389 OUT_TT_PRECIS, //output precision
390 CLIP_DEFAULT_PRECIS, //quality
391 DEFAULT_PITCH, //pitch and family
392 "" //font name
393 }; 394
395 strcpy(desc.FaceName, name.c_str()); 396
397 D3DXCreateFontIndirect(d3ddev, &desc, &font); 398
399 return font; 400 } 401
402 void FontPrint(LPD3DXFONT font, int x, int y, string text, D3DCOLOR color) 403 { 404 //figure out the text boundary
405 RECT rect = { x, y, 0, 0 }; 406 font->DrawText( NULL, text.c_str(), text.length(), &rect, DT_CALCRECT, color); 407
408 //print the text
409 font->DrawText(spriteobj, text.c_str(), text.length(), &rect, DT_LEFT, color); 410 }
下面是一些DirectSound方面的函数实现。在DirectSound_Init中,用类CSoundManager创建了DirectSound对象dsound,接着调用dsound->Initialize来初始化dsound,最后要设置音频缓冲区的格式dsound->SetPrimaryBufferFormat。
1 bool DirectSound_Init(HWND hwnd) 2 { 3 //create DirectSound manager object
4 dsound = new CSoundManager(); 5
6 //initialize DirectSound
7 HRESULT result; 8 result = dsound->Initialize(hwnd, DSSCL_PRIORITY); 9 if (FAILED(result)) 10 return false; 11
12 //set the primary buffer format
13 result = dsound->SetPrimaryBufferFormat(2, 22050, 16); 14 if (FAILED(result)) 15 return false; 16
17 return true; 18 } 19
20 void DirectSound_Shutdown() 21 { 22 if (dsound) 23 delete dsound; 24 } 25
26 CSound* LoadSound(string filename) 27 { 28 HRESULT result; 29
30 //create local reference to wave data
31 CSound *wave = NULL; 32
33 //attempt to load the wave file
34 char s[255]; 35 sprintf(s, "%s", filename.c_str()); 36 result = dsound->Create(&wave, s); 37 if (FAILED(result)) 38 wave = NULL; 39
40 return wave; 41 } 42
43 void PlaySound(CSound *sound) 44 { 45 sound->Play(); 46 } 47
48 void LoopSound(CSound *sound) 49 { 50 sound->Play(0, DSBPLAY_LOOPING); 51 } 52
53 void StopSound(CSound *sound) 54 { 55 sound->Stop(); 56 }
main.cpp文件依然没什么改变
1 #include "DirectX.h"
2 using namespace std; 3
4 bool gameover = false; 5
6 //windows event handling function
7 LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 8 { 9 switch (message) 10 { 11 case WM_DESTROY: 12 gameover = true; 13 PostQuitMessage(0); 14 return 0; 15 } 16
17 return DefWindowProc(hwnd, message, wParam, lParam); 18 } 19
20
21 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow) 22 { 23 //set the windows properties
24 WNDCLASSEX wc; 25 wc.cbSize = sizeof(WNDCLASSEX); 26 wc.style = CS_HREDRAW | CS_VREDRAW; 27 wc.lpfnWndProc = (WNDPROC)WinProc; 28 wc.cbClsExtra = 0; 29 wc.cbWndExtra = 0; 30 wc.hInstance = hInstance; 31 wc.hIcon = NULL; 32 wc.hCursor = LoadCursor(NULL, IDC_ARROW); 33 wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 34 wc.lpszMenuName = NULL; 35 wc.lpszClassName = APPTITLE.c_str(); 36 wc.hIconSm = NULL; 37 RegisterClassEx(&wc); 38
39 //determine the resolution of the clients desktop screen
40 int screenWidth = GetSystemMetrics(SM_CXSCREEN); 41 int screenHeight = GetSystemMetrics(SM_CYSCREEN); 42
43 //place the window in the middle of screen
44 int posX = (GetSystemMetrics(SM_CXSCREEN) - SCREENW) / 2; 45 int posY = (GetSystemMetrics(SM_CYSCREEN) - SCREENH) / 2; 46
47 //Create a window
48 HWND window; 49 window = CreateWindow(APPTITLE.c_str(), APPTITLE.c_str(), 50 WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU, posX, posY, SCREENW, SCREENH, 51 NULL, NULL, hInstance, NULL); 52 if (window == 0) 53 return false; 54
55 //display the window
56 ShowWindow(window, nCmdShow); 57 UpdateWindow(window); 58
59 //initialize the game
60 if (!Game_Init(window)) 61 return 0; 62
63 //main message loop
64 MSG msg; 65 while (!gameover) 66 { 67 //process windows events
68 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 69 { 70 //handle any event messages
71 TranslateMessage(&msg); 72 DispatchMessage(&msg); 73 } 74
75 //process game loop
76 Game_Run(window); 77 } 78
79 //free game resources
80 Game_End(); 81
82 return msg.wParam; 83 }
game.cpp文件如下,先看开头部分。这里定义了3张图片,ball_image、bumper_image、background,待会儿要实现一个简单的碰撞模拟。ball_image代表移动的小球,bumper_image代表缓冲器转置,如小球撞上了缓冲器装置就会发出声音,小球会简单的反弹。
1 #include "DirectX.h"
2 using namespace std; 3
4 const string APPTITLE = "Play Sound Demo"; 5 const int SCREENW = 1024; 6 const int SCREENH = 576; 7
8 LPDIRECT3DTEXTURE9 ball_image = NULL; 9 LPDIRECT3DTEXTURE9 bumper_image = NULL; 10 LPDIRECT3DTEXTURE9 background = NULL; 11
12 //balls
13 const int NUMBALLS = 10; 14 SPRITE balls[NUMBALLS]; 15
16 //bumpers
17 const int NUMBUMPERS = 3; 18 SPRITE bumpers[NUMBUMPERS]; 19
20 //timing variable
21 DWORD screentimer = timeGetTime(); 22 DWORD coretimer = timeGetTime(); 23 DWORD bumpertimer = timeGetTime(); 24
25 //the wave sound
26 CSound *sound_bounce = NULL;
下面的Game_Init函数先用DirectSound_Init初始化了DirectSound,然后加载3张图片。接着用随机数设置小球出现的位置与初始速度,而3个缓冲器的位置是固定的,参数velx、vely决定了小球的速度。
1 bool Game_Init(HWND window) 2 { 3 srand(time(NULL)); 4
5 //initialize Direct3D
6 if (!Direct3D_Init(window, SCREENW, SCREENH, false)) 7 { 8 MessageBox(window,"Error initializing Direct3D",APPTITLE.c_str(),0); 9 return false; 10 } 11
12 //initialize DirectInput
13 if (!DirectInput_Init(window)) 14 { 15 MessageBox(window,"Error initializing DirectInput",APPTITLE.c_str(),0); 16 return false; 17 } 18
19 //initialize DirectSound
20 if (!DirectSound_Init(window)) 21 { 22 MessageBox(window,"Error initializing DirectSound",APPTITLE.c_str(),0); 23 return false; 24 } 25
26
27 //load the background image
28 background = LoadTexture("craters.tga"); 29 if (!background) 30 { 31 MessageBox(window,"Error loading craters.tga",APPTITLE.c_str(),0); 32 return false; 33 } 34
35 //load the ball image
36 ball_image = LoadTexture("lightningball.tga"); 37 if (!ball_image) 38 { 39 MessageBox(window,"Error loading lightningball.tga",APPTITLE.c_str(),0); 40 return false; 41 } 42
43 //load the bumper image
44 bumper_image = LoadTexture("bumper.tga"); 45 if (!ball_image) 46 { 47 MessageBox(window,"Error loading bumper.tga",APPTITLE.c_str(),0); 48 return false; 49 } 50
51 //set the balls' properties
52 for (int n=0; n<NUMBALLS; n++) 53 { 54 balls[n].x = (float)(rand() % (SCREENW-200)); 55 balls[n].y = (float)(rand() % (SCREENH-200)); 56 balls[n].width = 64; 57 balls[n].height = 64; 58 balls[n].velx = (float)(rand() % 6 - 3); 59 balls[n].vely = (float)(rand() % 6 - 3); 60 } 61
62 //set the bumpers' properties
63 for (int n=0; n<NUMBUMPERS; n++) 64 { 65 bumpers[n].width = 128; 66 bumpers[n].height = 128; 67 bumpers[n].columns = 2; 68 bumpers[n].frame = 0; 69 } 70 bumpers[0].x = SCREENW/2; 71 bumpers[0].y = 64; 72 bumpers[1].x = 128; 73 bumpers[1].y = SCREENH - 256; 74 bumpers[2].x = SCREENW - 256; 75 bumpers[2].y = SCREENH - 256; 76
77 //load bounce wave file
78 sound_bounce = LoadSound("bounce.wav"); 79 if (!sound_bounce) 80 { 81 MessageBox(window,"Error loading wav file",APPTITLE.c_str(),0); 82 return false; 83 } 84
85 return true; 86 }
下面这个函数表示两个精灵碰撞后的反弹效果,就是简单的乘以-1来改变移动方向。
1 void rebound(SPRITE &sprite1, SPRITE &sprite2) 2 { 3 float centerx1 = sprite1.x + sprite1.width/2; 4 float centery1 = sprite1.y + sprite1.height/2; 5
6 float centerx2 = sprite2.x + sprite2.width/2; 7 float centery2 = sprite2.y + sprite2.height/2; 8
9 if (centerx1 < centerx2) 10 { 11 sprite1.velx = fabs(sprite1.velx) * -1; 12 } 13 else if (centerx1 > centerx2) 14 { 15 sprite1.velx = fabs(sprite1.velx); 16 } 17
18 if (centery1 < centery2) 19 { 20 sprite1.vely = fabs(sprite1.vely) * -1; 21 } 22 else { 23 sprite1.vely = fabs(sprite1.vely); 24 } 25
26 sprite1.x += sprite1.velx; 27 sprite1.y += sprite1.vely; 28
29 }
这里先介绍Game_End函数
1 void Game_End() 2 { 3
4 if (ball_image) ball_image->Release(); 5 if (bumper_image) bumper_image->Release(); 6 if (background) background->Release(); 7
8 if (sound_bounce) delete sound_bounce; 9
10 DirectSound_Shutdown(); 11 DirectInput_Shutdown(); 12 Direct3D_Shutdown(); 13 }
最后介绍的是很长的Game_Run函数,其实这样的代码风格很差,更别提这种C语言风格的C++代码了,但我没时间去修改它。在这段代码中,先看到第一个for循环,这里对小球的运动做了些设计,如果小球移出边界,则会从屏幕另一侧出来。然后看到bumpertimer = timeGetTime()这段,由于小球撞击缓冲器时,缓冲器要产生一个变亮的动画效果,这段代码中把frame改为0是为了结束动画效果。接下来的for循环语句用来检测小球与缓冲器的碰撞,既小球先反弹rebound(balls, bumpers),然后把frame设为1产生动画效果,紧接着播放声音。注意rebound函数,这里的调用只会使第一个参数小球的移动方向改变,缓冲器时不动的。下面的循环用来检测小球之间的碰撞,两个for循环中各自定义两个小球,由于不能与自己碰撞,所以one != two。那么,如果发生碰撞了,就依次用两个rebound来反弹两个小球。作者在里面使用了一个While,这里其实没什么用处,可以去掉。接下来开始绘制图像了,调用之前的Sprite_Transform_Draw函数便可产生动画效果。
1 void Game_Run(HWND window) 2 { 3 int n; 4
5 if (!d3ddev) return; 6 DirectInput_Update(); 7 d3ddev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,100), 1.0f, 0); 8
9
10 /*
11 * slow ball movement 12 */
13 if (timeGetTime() > coretimer + 10) 14 { 15 //reset timing
16 coretimer = GetTickCount(); 17
18 int width = balls[0].width; 19 int height = balls[0].height; 20
21 //move the ball sprites
22 for (n=0; n<NUMBALLS; n++) 23 { 24 balls[n].x += balls[n].velx; 25 balls[n].y += balls[n].vely; 26
27 //warp the ball at screen edges
28 if (balls[n].x > SCREENW) 29 { 30 balls[n].x = -width; 31 } 32 else if (balls[n].x < -width) 33 { 34 balls[n].x = SCREENW+width; 35 } 36
37 if (balls[n].y > SCREENH+height) 38 { 39 balls[n].y = -height; 40 } 41 else if (balls[n].y < -height) 42 { 43 balls[n].y = SCREENH+height; 44 } 45 } 46 } 47
48
49 //reset bumper frames
50 if (timeGetTime() > bumpertimer + 250) 51 { 52 bumpertimer = timeGetTime(); 53 for (int bumper=0; bumper<NUMBUMPERS; bumper++) 54 { 55 bumpers[bumper].frame = 0; 56 } 57 } 58
59 /*
60 * check for ball collisions with bumpers 61 */
62 for (int ball=0; ball<NUMBALLS; ball++) 63 { 64 for (int bumper=0; bumper<NUMBUMPERS; bumper++) 65 { 66 if (CollisionD(balls[ball], bumpers[bumper])) 67 { 68 rebound(balls[ball], bumpers[bumper]); 69 bumpers[bumper].frame = 1; 70 PlaySound(sound_bounce); 71 } 72 } 73 } 74
75 /*
76 * check for sprite collisions with each other 77 * (as fast as possible--with no time limiter) 78 */
79 for (int one=0; one<NUMBALLS; one++) 80 { 81 for (int two=0; two<NUMBALLS; two++) 82 { 83 if (one != two) 84 { 85 if (CollisionD(balls[one], balls[two])) 86 { 87 while (CollisionD(balls[one], balls[two])) 88 { 89 //rebound ball one
90 rebound(balls[one], balls[two]); 91
92 //rebound ball two
93 rebound(balls[two], balls[one]); 94 } 95 } 96 } 97 } 98 } 99
100
101 /*
102 * slow rendering to approximately 60 fps 103 */
104 if (timeGetTime() > screentimer + 14) 105 { 106 screentimer = GetTickCount(); 107
108 //start rendering
109 if (d3ddev->BeginScene()) 110 { 111 //start sprite handler
112 spriteobj->Begin(D3DXSPRITE_ALPHABLEND); 113
114 //draw background
115 Sprite_Transform_Draw(background, 0, 0, SCREENW, SCREENH); 116
117 //draw the balls
118 for (n=0; n<NUMBALLS; n++) 119 { 120 Sprite_Transform_Draw(ball_image, 121 balls[n].x, balls[n].y, 122 balls[n].width, balls[n].height); 123 } 124
125 //draw the bumpers
126 for (n=0; n<NUMBUMPERS; n++) 127 { 128 Sprite_Transform_Draw(bumper_image, 129 bumpers[n].x, 130 bumpers[n].y, 131 bumpers[n].width, 132 bumpers[n].height, 133 bumpers[n].frame, 134 bumpers[n].columns); 135 } 136
137 //stop drawing
138 spriteobj->End(); 139
140
141 //stop rendering
142 d3ddev->EndScene(); 143 d3ddev->Present(NULL, NULL, NULL, NULL); 144 } 145 } 146
147 //exit with escape key or controller Back button
148 if (KEY_DOWN(VK_ESCAPE)) gameover = true; 149 if (controllers[0].wButtons & XINPUT_GAMEPAD_BACK) gameover = true; 150
151 }
最后放出截图,源代码,参考自游戏编程入门。