Quake II RTX doxygen  1.0 dev
wave.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 
23 // 64K is > 1 second at 16-bit, 22050 Hz
24 #define WAV_BUFFERS 64
25 #define WAV_MASK (WAV_BUFFERS - 1)
26 #define WAV_BUFFER_SIZE 0x0400
27 
28 static qboolean wav_init;
29 
30 // starts at 0 for disabled
31 static int sample16;
32 static int snd_sent, snd_completed;
33 
34 static HANDLE hData;
35 static HPSTR lpData;
36 
37 static HGLOBAL hWaveHdr;
38 static LPWAVEHDR lpWaveHdr;
39 
40 static HWAVEOUT hWaveOut;
41 
42 static DWORD gSndBufSize;
43 
44 /*
45 ==============
46 WAVE_Shutdown
47 
48 Reset the sound device for exiting
49 ===============
50 */
51 static void WAVE_Shutdown(void)
52 {
53  int i;
54 
55  Com_Printf("Shutting down wave sound\n");
56 
57  if (hWaveOut) {
58  Com_DPrintf("...resetting waveOut\n");
59  waveOutReset(hWaveOut);
60 
61  if (lpWaveHdr) {
62  Com_DPrintf("...unpreparing headers\n");
63  for (i = 0; i < WAV_BUFFERS; i++)
64  waveOutUnprepareHeader(hWaveOut, lpWaveHdr + i, sizeof(WAVEHDR));
65  }
66 
67  Com_DPrintf("...closing waveOut\n");
68  waveOutClose(hWaveOut);
69 
70  if (hWaveHdr) {
71  Com_DPrintf("...freeing WAV header\n");
72  GlobalUnlock(hWaveHdr);
73  GlobalFree(hWaveHdr);
74  }
75 
76  if (hData) {
77  Com_DPrintf("...freeing WAV buffer\n");
78  GlobalUnlock(hData);
79  GlobalFree(hData);
80  }
81 
82  }
83 
84  hWaveOut = 0;
85  hData = 0;
86  hWaveHdr = 0;
87  lpData = NULL;
88  lpWaveHdr = NULL;
89  wav_init = qfalse;
90 }
91 
92 
93 /*
94 ==================
95 WAVE_Init
96 
97 Crappy windows multimedia base
98 ==================
99 */
100 static sndinitstat_t WAVE_Init(void)
101 {
102  WAVEFORMATEX format;
103  int i;
104  HRESULT hr;
105 
106  Com_DPrintf("Initializing wave sound\n");
107 
108  snd_sent = 0;
109  snd_completed = 0;
110 
111  memset(&dma, 0, sizeof(dma));
112  dma.channels = 2;
113  dma.samplebits = 16;
114 
115  if (s_khz->integer == 44)
116  dma.speed = 44100;
117  else if (s_khz->integer == 22)
118  dma.speed = 22050;
119  else
120  dma.speed = 11025;
121 
122  memset(&format, 0, sizeof(format));
123  format.wFormatTag = WAVE_FORMAT_PCM;
124  format.nChannels = dma.channels;
125  format.wBitsPerSample = dma.samplebits;
126  format.nSamplesPerSec = dma.speed;
127  format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
128  format.cbSize = 0;
129  format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
130 
131  /* Open a waveform device for output using window callback. */
132  Com_DPrintf("...opening waveform device: ");
133  while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,
134  &format,
135  0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR) {
136  if (hr != MMSYSERR_ALLOCATED) {
137  Com_DPrintf("failed\n");
138  return SIS_FAILURE;
139  }
140 
141  if (MessageBox(NULL,
142  _T("The sound hardware is in use by another app.\n\n")
143  _T("Select Retry to try to start sound again or Cancel to run ") _T("q2pro") _T(" with no sound."),
144  _T("Sound not available"),
145  MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY) {
146  Com_DPrintf("hw in use\n");
147  return SIS_NOTAVAIL;
148  }
149  }
150  Com_DPrintf("ok\n");
151 
152  /*
153  * Allocate and lock memory for the waveform data. The memory
154  * for waveform data must be globally allocated with
155  * GMEM_MOVEABLE and GMEM_SHARE flags.
156 
157  */
158  Com_DPrintf("...allocating waveform buffer: ");
160  hData = GlobalAlloc(GMEM_MOVEABLE /*| GMEM_SHARE*/, gSndBufSize);
161  if (!hData) {
162  Com_DPrintf(" failed with error %#lx\n", GetLastError());
163  WAVE_Shutdown();
164  return SIS_FAILURE;
165  }
166  Com_DPrintf("ok\n");
167 
168  Com_DPrintf("...locking waveform buffer: ");
169  lpData = GlobalLock(hData);
170  if (!lpData) {
171  Com_DPrintf(" failed with error %#lx\n", GetLastError());
172  WAVE_Shutdown();
173  return SIS_FAILURE;
174  }
175  memset(lpData, 0, gSndBufSize);
176  Com_DPrintf("ok\n");
177 
178  /*
179  * Allocate and lock memory for the header. This memory must
180  * also be globally allocated with GMEM_MOVEABLE and
181  * GMEM_SHARE flags.
182  */
183  Com_DPrintf("...allocating waveform header: ");
184  hWaveHdr = GlobalAlloc(GMEM_MOVEABLE /*| GMEM_SHARE*/,
185  (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
186  if (hWaveHdr == NULL) {
187  Com_DPrintf("failed with error %#lx\n", GetLastError());
188  WAVE_Shutdown();
189  return SIS_FAILURE;
190  }
191  Com_DPrintf("ok\n");
192 
193  Com_DPrintf("...locking waveform header: ");
194  lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
195  if (lpWaveHdr == NULL) {
196  Com_DPrintf("failed with error %#lx\n", GetLastError());
197  WAVE_Shutdown();
198  return SIS_FAILURE;
199  }
200  memset(lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
201  Com_DPrintf("ok\n");
202 
203  /* After allocation, set up and prepare headers. */
204  Com_DPrintf("...preparing headers: ");
205  for (i = 0; i < WAV_BUFFERS; i++) {
206  lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE;
207  lpWaveHdr[i].lpData = lpData + i * WAV_BUFFER_SIZE;
208 
209  if (waveOutPrepareHeader(hWaveOut, lpWaveHdr + i, sizeof(WAVEHDR)) !=
210  MMSYSERR_NOERROR) {
211  Com_DPrintf("failed\n");
212  WAVE_Shutdown();
213  return SIS_FAILURE;
214  }
215  }
216  Com_DPrintf("ok\n");
217 
218  dma.samples = gSndBufSize / (dma.samplebits / 8);
219  dma.samplepos = 0;
220  dma.submission_chunk = 512;
221  dma.buffer = (byte *) lpData;
222  sample16 = (dma.samplebits / 8) - 1;
223 
224  Com_Printf("Wave sound initialized\n");
225  wav_init = qtrue;
226 
227  return SIS_SUCCESS;
228 }
229 
230 /*
231 ==============
232 WAVE_BeginPainting
233 
234 Makes sure dma.buffer is valid.
235 
236 Returns the current sample position (in mono samples read)
237 inside the recirculating dma buffer, so the mixing code will know
238 how many sample are required to fill it up.
239 ===============
240 */
241 static void WAVE_BeginPainting(void)
242 {
243  int s;
244 
245  if (!wav_init) {
246  return;
247  }
248 
249  s = (snd_sent * WAV_BUFFER_SIZE) >> sample16;
250  dma.samplepos = s & (dma.samples - 1);
251 }
252 
253 /*
254 ==============
255 WAVE_Submit
256 
257 Send sound to device if buffer isn't really the dma buffer
258 Also unlocks the dsound buffer
259 ===============
260 */
261 static void WAVE_Submit(void)
262 {
263  LPWAVEHDR h;
264  int wResult;
265 
266  if (!dma.buffer)
267  return;
268 
269  if (!wav_init)
270  return;
271 
272  //
273  // find which sound blocks have completed
274  //
275  while (1) {
276  if (snd_completed == snd_sent) {
277  Com_DPrintf("WAVE_Submit: Sound overrun\n");
278  break;
279  }
280 
281  if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE)) {
282  break;
283  }
284 
285  snd_completed++; // this buffer has been played
286  }
287 
288  //
289  // submit a few new sound blocks
290  //
291  while (((snd_sent - snd_completed) >> sample16) < 8) {
292  h = lpWaveHdr + (snd_sent & WAV_MASK);
293  if (paintedtime / 256 <= snd_sent)
294  break;
295  snd_sent++;
296  /*
297  * Now the data block can be sent to the output device. The
298  * waveOutWrite function returns immediately and waveform
299  * data is sent to the output device in the background.
300  */
301  wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
302 
303  if (wResult != MMSYSERR_NOERROR) {
304  Com_EPrintf("WAVE_Submit: Failed to write block to device\n");
305  WAVE_Shutdown();
306  return;
307  }
308  }
309 }
310 
311 
312 /*
313 ===========
314 WAVE_Activate
315 
316 Called when the main window gains or loses focus.
317 The window have been destroyed and recreated
318 between a deactivate and an activate.
319 ===========
320 */
321 static void WAVE_Activate(qboolean active)
322 {
323 }
324 
325 void WAVE_FillAPI(snddmaAPI_t *api)
326 {
327  api->Init = WAVE_Init;
328  api->Shutdown = WAVE_Shutdown;
329  api->BeginPainting = WAVE_BeginPainting;
330  api->Submit = WAVE_Submit;
331  api->Activate = WAVE_Activate;
332 }
WAVE_BeginPainting
static void WAVE_BeginPainting(void)
Definition: wave.c:241
dma
dma_t dma
Definition: dma.c:22
s_khz
cvar_t * s_khz
Definition: dma.c:24
WAVE_Activate
static void WAVE_Activate(qboolean active)
Definition: wave.c:321
gSndBufSize
static DWORD gSndBufSize
Definition: wave.c:42
WAV_BUFFER_SIZE
#define WAV_BUFFER_SIZE
Definition: wave.c:26
WAVE_FillAPI
void WAVE_FillAPI(snddmaAPI_t *api)
Definition: wave.c:325
lpData
static HPSTR lpData
Definition: wave.c:35
paintedtime
int paintedtime
Definition: main.c:43
snd_completed
static int snd_completed
Definition: wave.c:32
sample16
static int sample16
Definition: wave.c:31
snd_sent
static int snd_sent
Definition: wave.c:32
client.h
WAVE_Shutdown
static void WAVE_Shutdown(void)
Definition: wave.c:51
WAVE_Submit
static void WAVE_Submit(void)
Definition: wave.c:261
WAVE_Init
static sndinitstat_t WAVE_Init(void)
Definition: wave.c:100
hData
static HANDLE hData
Definition: wave.c:34
wav_init
static qboolean wav_init
Definition: wave.c:28
lpWaveHdr
static LPWAVEHDR lpWaveHdr
Definition: wave.c:38
L
#define L(name)
Definition: g_save.c:47
WAV_BUFFERS
#define WAV_BUFFERS
Definition: wave.c:24
hWaveOut
static HWAVEOUT hWaveOut
Definition: wave.c:40
WAV_MASK
#define WAV_MASK
Definition: wave.c:25
hWaveHdr
static HGLOBAL hWaveHdr
Definition: wave.c:37