Quake II RTX doxygen  1.0 dev
dsound.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18 
19 #include "client.h"
20 #include "client/sound/dma.h"
21 #include <mmsystem.h>
22 #include <dsound.h>
23 
24 typedef HRESULT(WINAPI *LPDIRECTSOUNDCREATE)(LPCGUID, LPDIRECTSOUND *, LPUNKNOWN);
25 
26 #define SECONDARY_BUFFER_SIZE 0x10000
27 
28 // starts at 0 for disabled
29 static int sample16;
30 
31 static DWORD locksize;
32 
33 static MMTIME mmstarttime;
34 
35 static LPDIRECTSOUND pDS;
36 static LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
37 
38 static HINSTANCE hInstDS;
39 
40 static DWORD gSndBufSize;
41 
42 static const char *DSoundError(int error)
43 {
44  switch (error) {
45  case DSERR_BUFFERLOST:
46  return "DSERR_BUFFERLOST";
47  case DSERR_INVALIDCALL:
48  return "DSERR_INVALIDCALL";
49  case DSERR_INVALIDPARAM:
50  return "DSERR_INVALIDPARAM";
51  case DSERR_PRIOLEVELNEEDED:
52  return "DSERR_PRIOLEVELNEEDED";
53  }
54 
55  return "<unknown error>";
56 }
57 
58 /*
59 ** DS_DestroyBuffers
60 */
61 static void DS_DestroyBuffers(void)
62 {
63  Com_DPrintf("Destroying DS buffers\n");
64  if (pDS) {
65  Com_DPrintf("...setting NORMAL coop level\n");
66  IDirectSound_SetCooperativeLevel(pDS, win.wnd, DSSCL_NORMAL);
67  }
68 
69  if (pDSBuf) {
70  Com_DPrintf("...stopping and releasing sound buffer\n");
71  IDirectSoundBuffer_Stop(pDSBuf);
72  IDirectSoundBuffer_Release(pDSBuf);
73  }
74 
75  // only release primary buffer if it's not also the mixing buffer we just released
76  if (pDSPBuf && (pDSBuf != pDSPBuf)) {
77  Com_DPrintf("...releasing primary buffer\n");
78  IDirectSoundBuffer_Release(pDSPBuf);
79  }
80  pDSBuf = NULL;
81  pDSPBuf = NULL;
82 
83  dma.buffer = NULL;
84 }
85 
86 /*
87 ==============
88 DS_Shutdown
89 
90 Reset the sound device for exiting
91 ===============
92 */
93 static void DS_Shutdown(void)
94 {
95  Com_Printf("Shutting down DirectSound\n");
96 
97  if (pDS) {
99 
100  Com_DPrintf("...releasing DS object\n");
101  IDirectSound_Release(pDS);
102  }
103 
104  if (hInstDS) {
105  Com_DPrintf("...freeing DSOUND.DLL\n");
106  FreeLibrary(hInstDS);
107  hInstDS = NULL;
108  }
109 
110  pDS = NULL;
111  pDSBuf = NULL;
112  pDSPBuf = NULL;
113 }
114 
115 /*
116 ** DS_CreateBuffers
117 */
118 static qboolean DS_CreateBuffers(void)
119 {
120  DSBUFFERDESC dsbuf;
121  DSBCAPS dsbcaps;
122  WAVEFORMATEX format;
123  DWORD dwWrite;
124 
125  memset(&format, 0, sizeof(format));
126  format.wFormatTag = WAVE_FORMAT_PCM;
127  format.nChannels = dma.channels;
128  format.wBitsPerSample = dma.samplebits;
129  format.nSamplesPerSec = dma.speed;
130  format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
131  format.cbSize = sizeof(format);
132  format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
133 
134  Com_DPrintf("Creating DS buffer\n");
135 
136  Com_DPrintf("...setting PRIORITY coop level: ");
137  if (DS_OK != IDirectSound_SetCooperativeLevel(pDS, win.wnd, DSSCL_PRIORITY)) {
138  Com_DPrintf("failed\n");
139  return qfalse;
140  }
141  Com_DPrintf("ok\n");
142 
143 // create the secondary buffer we'll actually work with
144  memset(&dsbuf, 0, sizeof(dsbuf));
145  dsbuf.dwSize = sizeof(DSBUFFERDESC);
146  dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCHARDWARE;
147  dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
148  dsbuf.lpwfxFormat = &format;
149 
150  memset(&dsbcaps, 0, sizeof(dsbcaps));
151  dsbcaps.dwSize = sizeof(dsbcaps);
152 
153  Com_DPrintf("...creating secondary buffer: ");
154  if (DS_OK != IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL)) {
155  dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
156  if (DS_OK != IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL)) {
157  Com_DPrintf("failed\n");
158  return qfalse;
159  }
160 
161  Com_DPrintf("ok\n...forced to software\n");
162  } else {
163  Com_DPrintf("ok\n...locked hardware\n");
164  }
165 
166  dma.channels = format.nChannels;
167  dma.samplebits = format.wBitsPerSample;
168  dma.speed = format.nSamplesPerSec;
169 
170  if (DS_OK != IDirectSoundBuffer_GetCaps(pDSBuf, &dsbcaps)) {
171  Com_DPrintf("*** GetCaps failed ***\n");
172  return qfalse;
173  }
174 
175  // Make sure mixer is active
176  if (DS_OK != IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING)) {
177  Com_DPrintf("*** Play failed ***\n");
178  return qfalse;
179  }
180 
181  Com_DPrintf(" %d channel(s)\n"
182  " %d bits/sample\n"
183  " %d bytes/sec\n",
184  dma.channels, dma.samplebits, dma.speed);
185 
186  gSndBufSize = dsbcaps.dwBufferBytes;
187 
188  IDirectSoundBuffer_Stop(pDSBuf);
189  IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite);
190  IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
191 
192  dma.samples = gSndBufSize / (dma.samplebits / 8);
193  dma.samplepos = 0;
194  dma.submission_chunk = 1;
195  dma.buffer = NULL;
196  sample16 = (dma.samplebits / 8) - 1;
197 
198  return qtrue;
199 }
200 
201 
202 
203 /*
204 ==================
205 DS_Init
206 
207 Direct-Sound support
208 ==================
209 */
210 static sndinitstat_t DS_Init(void)
211 {
212  DSCAPS dscaps;
213  HRESULT hresult;
214  LPDIRECTSOUNDCREATE pDirectSoundCreate;
215 
216  memset(&dma, 0, sizeof(dma));
217  dma.channels = 2;
218  dma.samplebits = 16;
219 
220  switch (s_khz->integer) {
221  case 48:
222  dma.speed = 48000;
223  break;
224  case 44:
225  dma.speed = 44100;
226  break;
227  case 22:
228  dma.speed = 22050;
229  break;
230  default:
231  dma.speed = 11025;
232  break;
233  }
234 
235  Com_DPrintf("Initializing DirectSound\n");
236 
237  if (!hInstDS) {
238  Com_DPrintf("...loading dsound.dll: ");
239  hInstDS = LoadLibrary("dsound.dll");
240  if (hInstDS == NULL) {
241  Com_DPrintf("failed\n");
242  return SIS_FAILURE;
243  }
244  Com_DPrintf("ok\n");
245  }
246 
247  pDirectSoundCreate = (LPDIRECTSOUNDCREATE)
248  GetProcAddress(hInstDS, "DirectSoundCreate");
249  if (!pDirectSoundCreate) {
250  Com_DPrintf("...couldn't get DS proc addr\n");
251  return SIS_FAILURE;
252  }
253 
254  Com_DPrintf("...creating DS object: ");
255  while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK) {
256  if (hresult != DSERR_ALLOCATED) {
257  Com_DPrintf("failed\n");
258  return SIS_FAILURE;
259  }
260 
261  if (MessageBox(NULL,
262  "The sound hardware is in use by another app.\n\n"
263  "Select Retry to try to start sound again or Cancel to run " PRODUCT " with no sound.",
264  "Sound not available",
265  MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY) {
266  Com_DPrintf("failed, hardware already in use\n");
267  return SIS_NOTAVAIL;
268  }
269  }
270  Com_DPrintf("ok\n");
271 
272  dscaps.dwSize = sizeof(dscaps);
273 
274  if (DS_OK != IDirectSound_GetCaps(pDS, &dscaps)) {
275  Com_DPrintf("...couldn't get DS caps\n");
276  DS_Shutdown();
277  return SIS_FAILURE;
278  }
279 
280  if (dscaps.dwFlags & DSCAPS_EMULDRIVER) {
281  Com_DPrintf("...no DSound driver found\n");
282  DS_Shutdown();
283  return SIS_FAILURE;
284  }
285 
286  if (!DS_CreateBuffers()) {
287  DS_Shutdown();
288  return SIS_FAILURE;
289  }
290 
291  Com_Printf("DirectSound initialized\n");
292 
293  return SIS_SUCCESS;
294 }
295 
296 /*
297 ==============
298 DS_BeginPainting
299 
300 Makes sure dma.buffer is valid.
301 
302 Returns the current sample position (in mono samples read)
303 inside the recirculating dma buffer, so the mixing code will know
304 how many sample are required to fill it up.
305 ===============
306 */
307 static void DS_BeginPainting(void)
308 {
309  int reps, s;
310  DWORD dwSize2;
311  DWORD *pbuf, *pbuf2;
312  HRESULT hresult;
313  DWORD dwStatus, dwWrite;
314  MMTIME mmtime;
315 
316  if (!pDSBuf)
317  return;
318 
319  // get sample pos
320  mmtime.wType = TIME_SAMPLES;
321  IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite);
322  s = (mmtime.u.sample - mmstarttime.u.sample) >> sample16;
323  dma.samplepos = s & (dma.samples - 1);
324 
325  // if the buffer was lost or stopped, restore it and/or restart it
326  if (IDirectSoundBuffer_GetStatus(pDSBuf, &dwStatus) != DS_OK) {
327  Com_EPrintf("DS_BeginPainting: Couldn't get sound buffer status\n");
328  DS_Shutdown();
329  return;
330  }
331 
332  if (dwStatus & DSBSTATUS_BUFFERLOST)
333  IDirectSoundBuffer_Restore(pDSBuf);
334 
335  if (!(dwStatus & DSBSTATUS_PLAYING))
336  IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
337 
338  // lock the dsound buffer
339 
340  reps = 0;
341  dma.buffer = NULL;
342 
343  while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (void **)&pbuf, &locksize,
344  (void **)&pbuf2, &dwSize2, 0)) != DS_OK) {
345  if (hresult != DSERR_BUFFERLOST) {
346  Com_EPrintf("DS_BeginPainting: Lock failed with error '%s'\n", DSoundError(hresult));
347  DS_Shutdown();
348  return;
349  }
350 
351  IDirectSoundBuffer_Restore(pDSBuf);
352 
353  if (++reps > 2)
354  return;
355  }
356  dma.buffer = (byte *)pbuf;
357 }
358 
359 /*
360 ==============
361 DS_Submit
362 
363 Send sound to device if buffer isn't really the dma buffer
364 Also unlocks the dsound buffer
365 ===============
366 */
367 static void DS_Submit(void)
368 {
369  if (!pDSBuf)
370  return;
371 
372  // unlock the dsound buffer
373  IDirectSoundBuffer_Unlock(pDSBuf, dma.buffer, locksize, NULL, 0);
374 }
375 
376 /*
377 ===========
378 DS_Activate
379 
380 Called when the main window gains or loses focus.
381 The window have been destroyed and recreated
382 between a deactivate and an activate.
383 ===========
384 */
385 static void DS_Activate(qboolean active)
386 {
387  if (!pDS) {
388  return;
389  }
390  if (active) {
391  if (!DS_CreateBuffers()) {
392  Com_EPrintf("DS_Activate: DS_CreateBuffers failed\n");
393  DS_Shutdown();
394  }
395  } else {
397  }
398 }
399 
400 void DS_FillAPI(snddmaAPI_t *api)
401 {
402  api->Init = DS_Init;
403  api->Shutdown = DS_Shutdown;
404  api->BeginPainting = DS_BeginPainting;
405  api->Submit = DS_Submit;
406  api->Activate = DS_Activate;
407 }
dma
dma_t dma
Definition: dma.c:22
s_khz
cvar_t * s_khz
Definition: dma.c:24
pDS
static LPDIRECTSOUND pDS
Definition: dsound.c:35
DSoundError
static const char * DSoundError(int error)
Definition: dsound.c:42
SECONDARY_BUFFER_SIZE
#define SECONDARY_BUFFER_SIZE
Definition: dsound.c:26
DS_BeginPainting
static void DS_BeginPainting(void)
Definition: dsound.c:307
mmstarttime
static MMTIME mmstarttime
Definition: dsound.c:33
locksize
static DWORD locksize
Definition: dsound.c:31
LPDIRECTSOUNDCREATE
HRESULT(WINAPI * LPDIRECTSOUNDCREATE)(LPCGUID, LPDIRECTSOUND *, LPUNKNOWN)
Definition: dsound.c:24
DS_Shutdown
static void DS_Shutdown(void)
Definition: dsound.c:93
DS_Activate
static void DS_Activate(qboolean active)
Definition: dsound.c:385
DS_FillAPI
void DS_FillAPI(snddmaAPI_t *api)
Definition: dsound.c:400
DS_Init
static sndinitstat_t DS_Init(void)
Definition: dsound.c:210
hInstDS
static HINSTANCE hInstDS
Definition: dsound.c:38
client.h
pDSPBuf
static LPDIRECTSOUNDBUFFER pDSPBuf
Definition: dsound.c:36
DS_CreateBuffers
static qboolean DS_CreateBuffers(void)
Definition: dsound.c:118
pDSBuf
static LPDIRECTSOUNDBUFFER pDSBuf
Definition: dsound.c:36
gSndBufSize
static DWORD gSndBufSize
Definition: dsound.c:40
DS_Submit
static void DS_Submit(void)
Definition: dsound.c:367
sample16
static int sample16
Definition: dsound.c:29
win
win_state_t win
Definition: client.c:33
DS_DestroyBuffers
static void DS_DestroyBuffers(void)
Definition: dsound.c:61