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