1 /***************************************************************************
2 audiodrv.cpp - ``DirectSound for Windows''
3 specific audio driver interface.
4 -------------------
5 begin : Mon Jul 31 2000
6 copyright : (C) 2000 by Simon White
7 email : s_a_white@email.com
8 ***************************************************************************/
9
10 /***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18 /***************************************************************************
19 * $Log: directx.cpp,v $
20 * Revision 1.7 2002/03/04 19:07:48 s_a_white
21 * Fix C++ use of nothrow.
22 *
23 * Revision 1.6 2001/12/11 19:38:13 s_a_white
24 * More GCC3 Fixes.
25 *
26 * Revision 1.5 2001/11/21 00:04:18 s_a_white
27 * Buffer alignment issues fixed.
28 *
29 * Revision 1.4 2001/10/30 23:35:35 s_a_white
30 * Added pause support.
31 *
32 * Revision 1.3 2001/09/17 19:09:39 s_a_white
33 * Sample enconding support added.
34 *
35 * Revision 1.2 2001/07/03 17:54:35 s_a_white
36 * Support for new audio interface for better compatibility.
37 *
38 * Revision 1.1 2001/01/08 16:41:43 s_a_white
39 * App and Library Seperation
40 *
41 ***************************************************************************/
42
43 #include "directx.h"
44 #ifdef HAVE_DIRECTX
45
46 #include <stdio.h>
47 #ifdef HAVE_EXCEPTIONS
48 # include <new>
49 #endif
50
51 #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
52
Audio_DirectX()53 Audio_DirectX::Audio_DirectX ()
54 {
55 isOpen = false;
56 lpdsNotify = 0;
57 lpDsb = 0;
58 lpds = 0;
59 }
60
~Audio_DirectX()61 Audio_DirectX::~Audio_DirectX()
62 {
63 close();
64 }
65
66 // Need this to setup DirectX
GetConsoleHwnd()67 HWND Audio_DirectX::GetConsoleHwnd ()
68 { // Taken from Microsoft Knowledge Base
69 // Article ID: Q124103
70 #define MY_bufSize 1024 // buffer size for console window totles
71 HWND hwndFound; // this is whta is returned to the caller
72 char pszNewWindowTitle[MY_bufSize]; // contains fabricated WindowTitle
73 char pszOldWindowTitle[MY_bufSize]; // contains original WindowTitle
74
75 // fetch curent window title
76 GetConsoleTitle (pszOldWindowTitle, MY_bufSize);
77
78 // format a "unique" NewWindowTitle
79 wsprintf (pszNewWindowTitle, "%d/%d", GetTickCount (),
80 GetCurrentProcessId ());
81
82 // change the window title
83 SetConsoleTitle (pszNewWindowTitle);
84
85 // ensure window title has been updated
86 Sleep (40);
87
88 // look for NewWindowTitle
89 hwndFound = FindWindow (NULL, pszNewWindowTitle);
90
91 // restore original window title
92 SetConsoleTitle (pszOldWindowTitle);
93 return (hwndFound);
94 }
95
open(AudioConfig & cfg,const char * name)96 void *Audio_DirectX::open (AudioConfig &cfg, const char *name)
97 {
98 HWND hwnd;
99 // Assume we have a console. Use other other
100 // if we have a non console Window
101 hwnd = GetConsoleHwnd ();
102 return open (cfg, name, hwnd);
103 }
104
open(AudioConfig & cfg,const char *,HWND hwnd)105 void *Audio_DirectX::open (AudioConfig &cfg, const char *, HWND hwnd)
106 {
107 DSBUFFERDESC dsbdesc;
108 LPDIRECTSOUNDBUFFER lpDsbPrimary = 0;
109 WAVEFORMATEX wfm;
110 DWORD dwBytes;
111 int i;
112
113 if (isOpen)
114 {
115 _errorString = "DIRECTX ERROR: Audio device already open.";
116 goto Audio_DirectX_openError;
117 }
118
119 lpvData = 0;
120 isOpen = true;
121
122 for (i = 0; i < AUDIO_DIRECTX_BUFFERS; i++)
123 rghEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
124
125 if (FAILED (DirectSoundCreate (NULL, &lpds, NULL)))
126 {
127 _errorString = "DIRECTX ERROR: Could not open audio device.";
128 goto Audio_DirectX_openError;
129 }
130 if (FAILED (lpds->SetCooperativeLevel (hwnd, DSSCL_PRIORITY)))
131 {
132 _errorString = "DIRECTX ERROR: Could not set cooperative level.";
133 goto Audio_DirectX_openError;
134 }
135
136 // Primary Buffer Setup
137 memset (&dsbdesc, 0, sizeof(DSBUFFERDESC));
138 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
139 dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
140 dsbdesc.dwBufferBytes = 0;
141 dsbdesc.lpwfxFormat = NULL;
142
143 // Format
144 memset (&wfm, 0, sizeof(WAVEFORMATEX));
145 wfm.wFormatTag = WAVE_FORMAT_PCM;
146 wfm.nChannels = cfg.channels;
147 wfm.nSamplesPerSec = cfg.frequency;
148 wfm.wBitsPerSample = cfg.precision;
149 wfm.nBlockAlign = wfm.wBitsPerSample / 8 * wfm.nChannels;
150 wfm.nAvgBytesPerSec = wfm.nSamplesPerSec * wfm.nBlockAlign;
151
152 if (FAILED (lpds->CreateSoundBuffer(&dsbdesc, &lpDsbPrimary, NULL)))
153 {
154 _errorString = "DIRECTX ERROR: Unable to create sound buffer.";
155 goto Audio_DirectX_openError;
156 }
157 if (FAILED (lpDsbPrimary->SetFormat(&wfm)))
158 {
159 _errorString = "DIRECTX ERROR: Unable to setup required sampling format.";
160 goto Audio_DirectX_openError;
161 }
162 lpDsbPrimary->Release ();
163
164 // Buffer size reduced to 2 blocks of 500ms
165 bufSize = wfm.nSamplesPerSec / 2 * wfm.nBlockAlign;
166
167 // Allocate secondary buffers
168 memset (&dsbdesc, 0, sizeof(DSBUFFERDESC));
169 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
170 dsbdesc.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY |
171 DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPAN;
172 dsbdesc.dwBufferBytes = bufSize * AUDIO_DIRECTX_BUFFERS;
173 dsbdesc.lpwfxFormat = &wfm;
174
175 if (FAILED (lpds->CreateSoundBuffer(&dsbdesc, &lpDsb, NULL)))
176 {
177 _errorString = "DIRECTX ERROR: Could not create sound buffer.";
178 goto Audio_DirectX_openError;
179 }
180 lpDsb->Stop();
181
182 // Apparently this is used for timing ------------------------
183 DSBPOSITIONNOTIFY rgdscbpn[AUDIO_DIRECTX_BUFFERS];
184 // Buffer Start Notification
185 // Rev 2.0.4 (saw) - On starting to play a buffer
186 for (i = 0; i < AUDIO_DIRECTX_BUFFERS; i++)
187 { // Track one buffer ahead
188 rgdscbpn[i].dwOffset = bufSize * ((i + 1) % AUDIO_DIRECTX_BUFFERS);
189 rgdscbpn[i].hEventNotify = rghEvent[i];
190 }
191
192 if (FAILED (lpDsb->QueryInterface (IID_IDirectSoundNotify, (VOID **) &lpdsNotify)))
193 {
194 _errorString = "DIRECTX ERROR: Sound interface query failed.";
195 goto Audio_DirectX_openError;
196 }
197 if (FAILED (lpdsNotify->SetNotificationPositions(AUDIO_DIRECTX_BUFFERS, rgdscbpn)))
198 {
199 _errorString = "DIRECTX ERROR: Unable to set up sound notification positions.";
200 goto Audio_DirectX_openError;
201 }
202 // -----------------------------------------------------------
203
204 lpDsb->Stop ();
205 if (FAILED (lpDsb->Lock (0, bufSize, &lpvData, &dwBytes, NULL, NULL, 0)))
206 {
207 _errorString = "DIRECTX ERROR: Unable to lock sound buffer.";
208 goto Audio_DirectX_openError;
209 }
210
211 // Rev 1.7 (saw) - Set the play position back to the begining
212 if (FAILED (lpDsb->SetCurrentPosition(0)))
213 {
214 _errorString = "DIRECTX ERROR: Unable to set play position to start of buffer.";
215 return NULL;
216 }
217
218 // Update the users settings
219 cfg.bufSize = bufSize;
220 // Setup the required sample format encoding.
221 cfg.encoding = AUDIO_SIGNED_PCM;
222 if (cfg.precision == 8)
223 cfg.encoding = AUDIO_UNSIGNED_PCM;
224 _settings = cfg;
225 isPlaying = false;
226 _sampleBuffer = lpvData;
227 return _sampleBuffer;
228
229 Audio_DirectX_openError:
230 SAFE_RELEASE (lpDsbPrimary);
231 close ();
232 return NULL;
233 }
234
write()235 void *Audio_DirectX::write ()
236 {
237 DWORD dwEvt;
238 DWORD dwBytes;
239
240 if (!isOpen)
241 {
242 _errorString = "DIRECTX ERROR: Device not open.";
243 return NULL;
244 }
245 // Unlock the current buffer for playing
246 lpDsb->Unlock (lpvData, bufSize, NULL, 0);
247
248 // Check to see of the buffer is playing
249 // and if not start it off
250 if (!isPlaying)
251 {
252 isPlaying = true;
253 if (FAILED (lpDsb->Play (0,0,DSBPLAY_LOOPING)))
254 {
255 _errorString = "DIRECTX ERROR: Unable to start playback.";
256 return NULL;
257 }
258 }
259
260 // Check the incoming event to make sure it's one of our event messages and
261 // not something else
262 do
263 {
264 dwEvt = MsgWaitForMultipleObjects (AUDIO_DIRECTX_BUFFERS, rghEvent, FALSE, INFINITE, QS_ALLINPUT);
265 dwEvt -= WAIT_OBJECT_0;
266 } while (dwEvt >= AUDIO_DIRECTX_BUFFERS);
267
268 // printf ("Event - %lu\n", dwEvt);
269
270 // Lock the next buffer for filling
271 if (FAILED (lpDsb->Lock (bufSize * dwEvt, bufSize, &lpvData, &dwBytes, NULL, NULL, 0)))
272 {
273 _errorString = "DIRECTX ERROR: Unable to lock sound buffer.";
274 return NULL;
275 }
276 _sampleBuffer = lpvData;
277 return _sampleBuffer;
278 }
279
reset(void)280 void *Audio_DirectX::reset (void)
281 {
282 DWORD dwBytes;
283 if (!isOpen)
284 return NULL;
285
286 // Stop play and kill the current music.
287 // Start new music data being added at the begining of
288 // the first buffer
289 lpDsb->Stop ();
290 // Rev 1.7 (saw) - Prevents output going silent after reset
291 isPlaying = false;
292 lpDsb->Unlock (lpvData, bufSize, NULL, 0);
293
294 // Rev 1.4 (saw) - Added as lock can fail.
295 if (FAILED (lpDsb->Lock (0, bufSize, &lpvData, &dwBytes, NULL, NULL, 0)))
296 {
297 _errorString = "DIRECTX ERROR: Unable to lock sound buffer.";
298 return NULL;
299 }
300 _sampleBuffer = lpvData;
301 return _sampleBuffer;
302 }
303
304 // Rev 1.8 (saw) - Alias fix
close(void)305 void Audio_DirectX::close (void)
306 {
307 if (!isOpen)
308 return;
309
310 isOpen = false;
311 _sampleBuffer = NULL;
312
313 if (lpDsb)
314 {
315 lpDsb->Stop();
316 isPlaying = false;
317 if (lpvData)
318 {
319 // Rev 1.4 (iv) - Unlock before we release buffer.
320 lpDsb->Unlock (lpvData, bufSize, NULL, 0);
321 }
322 }
323
324 SAFE_RELEASE (lpdsNotify);
325 SAFE_RELEASE (lpDsb);
326 SAFE_RELEASE (lpds);
327
328 // Rev 1.3 (Ingve Vormestrand) - Changed "<=" to "<"
329 // as closing invalid handle.
330 for (int i=0;i < AUDIO_DIRECTX_BUFFERS; i++)
331 CloseHandle (rghEvent[i]);
332 }
333
pause(void)334 void Audio_DirectX::pause (void)
335 {
336 lpDsb->Stop ();
337 isPlaying = false;
338 }
339
340 #endif // HAVE_DIRECTX
341