1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (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.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20 // snd_mix.c -- portable code to mix sounds for snd_dma.c
21
22 #include "quakedef.h"
23
24 #ifdef _WIN32
25 #include "winquake.h"
26 #else
27 #define DWORD unsigned long
28 #endif
29
30 #define PAINTBUFFER_SIZE 512
31
32 typedef union {
33 portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
34 int intbuf[PAINTBUFFER_SIZE * sizeof(portable_samplepair_t) / sizeof(int)];
35 } portableOrInt_t;
36
37 portableOrInt_t paintbuffer;
38 int snd_scaletable[32][256];
39 int *snd_p, snd_linear_count, snd_vol;
40 short *snd_out;
41
42 void Snd_WriteLinearBlastStereo16 (void);
43
44 extern void SNDDMA_ReportWrite(size_t lengthBytes);
45 #if !id386
Snd_WriteLinearBlastStereo16(void)46 void Snd_WriteLinearBlastStereo16 (void)
47 {
48 int i;
49 int val;
50
51 for (i=0 ; i<snd_linear_count ; i+=2)
52 {
53 val = (snd_p[i]*snd_vol)>>8;
54 if (val > 0x7fff)
55 snd_out[i] = 0x7fff;
56 else if (val < (short)0x8000)
57 snd_out[i] = (short)0x8000;
58 else
59 snd_out[i] = val;
60
61 val = (snd_p[i+1]*snd_vol)>>8;
62 if (val > 0x7fff)
63 snd_out[i+1] = 0x7fff;
64 else if (val < (short)0x8000)
65 snd_out[i+1] = (short)0x8000;
66 else
67 snd_out[i+1] = val;
68 }
69 SNDDMA_ReportWrite(snd_linear_count << 1);
70 }
71 #endif
72
S_TransferStereo16(int endtime)73 void S_TransferStereo16 (int endtime)
74 {
75 int lpos;
76 int lpaintedtime;
77 DWORD *pbuf;
78 #ifdef _WIN32
79 int reps;
80 DWORD dwSize,dwSize2;
81 DWORD *pbuf2;
82 HRESULT hresult;
83 #endif
84
85 snd_vol = (int)(volume.value*256);
86
87 snd_p = paintbuffer.intbuf;
88 lpaintedtime = paintedtime;
89
90 #ifdef _WIN32
91 if (pDSBuf)
92 {
93 reps = 0;
94
95 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize,
96 &pbuf2, &dwSize2, 0)) != DS_OK)
97 {
98 if (hresult != DSERR_BUFFERLOST)
99 {
100 Con_Printf ("S_TransferStereo16: DS::Lock Sound Buffer Failed\n");
101 S_Shutdown ();
102 S_Startup ();
103 return;
104 }
105
106 if (++reps > 10000)
107 {
108 Con_Printf ("S_TransferStereo16: DS: couldn't restore buffer\n");
109 S_Shutdown ();
110 S_Startup ();
111 return;
112 }
113 }
114 }
115 else
116 #endif
117 {
118 pbuf = (DWORD *)shm->buffer;
119 }
120
121 while (lpaintedtime < endtime)
122 {
123 // handle recirculating buffer issues
124 lpos = lpaintedtime & ((shm->samples>>1)-1);
125
126 snd_out = (short *) pbuf + (lpos<<1);
127
128 snd_linear_count = (shm->samples>>1) - lpos;
129 if (lpaintedtime + snd_linear_count > endtime)
130 snd_linear_count = endtime - lpaintedtime;
131
132 snd_linear_count <<= 1;
133
134 // write a linear blast of samples
135 Snd_WriteLinearBlastStereo16 ();
136
137 snd_p += snd_linear_count;
138 lpaintedtime += (snd_linear_count>>1);
139 }
140
141 #ifdef _WIN32
142 if (pDSBuf)
143 pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0);
144 #endif
145 }
146
S_TransferPaintBuffer(int endtime)147 void S_TransferPaintBuffer(int endtime)
148 {
149 int out_idx;
150 int count;
151 int out_mask;
152 int *p;
153 int step;
154 int val;
155 int snd_vol;
156 DWORD *pbuf;
157 #ifdef _WIN32
158 int reps;
159 DWORD dwSize,dwSize2;
160 DWORD *pbuf2;
161 HRESULT hresult;
162 #endif
163
164 if (shm->samplebits == 16 && shm->channels == 2)
165 {
166 S_TransferStereo16 (endtime);
167 return;
168 }
169
170 p = paintbuffer.intbuf;
171 count = (endtime - paintedtime) * shm->channels;
172 out_mask = shm->samples - 1;
173 out_idx = paintedtime * shm->channels & out_mask;
174 step = 3 - shm->channels;
175 snd_vol = (int)(volume.value*256);
176
177 #ifdef _WIN32
178 if (pDSBuf)
179 {
180 reps = 0;
181
182 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize,
183 &pbuf2,&dwSize2, 0)) != DS_OK)
184 {
185 if (hresult != DSERR_BUFFERLOST)
186 {
187 Con_Printf ("S_TransferPaintBuffer: DS::Lock Sound Buffer Failed\n");
188 S_Shutdown ();
189 S_Startup ();
190 return;
191 }
192
193 if (++reps > 10000)
194 {
195 Con_Printf ("S_TransferPaintBuffer: DS: couldn't restore buffer\n");
196 S_Shutdown ();
197 S_Startup ();
198 return;
199 }
200 }
201 }
202 else
203 #endif
204 {
205 pbuf = (DWORD *)shm->buffer;
206 }
207
208 if (shm->samplebits == 16)
209 {
210 short *out = (short *) pbuf;
211 while (count--)
212 {
213 val = (*p * snd_vol) >> 8;
214 p+= step;
215 if (val > 0x7fff)
216 val = 0x7fff;
217 else if (val < (short)0x8000)
218 val = (short)0x8000;
219 out[out_idx] = val;
220 out_idx = (out_idx + 1) & out_mask;
221 }
222 }
223 else if (shm->samplebits == 8)
224 {
225 unsigned char *out = (unsigned char *) pbuf;
226 while (count--)
227 {
228 val = (*p * snd_vol) >> 8;
229 p+= step;
230 if (val > 0x7fff)
231 val = 0x7fff;
232 else if (val < (short)0x8000)
233 val = (short)0x8000;
234 out[out_idx] = (val>>8) + 128;
235 out_idx = (out_idx + 1) & out_mask;
236 }
237 }
238
239 #ifdef _WIN32
240 if (pDSBuf) {
241 DWORD dwNewpos, dwWrite;
242 int il = paintedtime;
243 int ir = endtime - paintedtime;
244
245 ir += il;
246
247 pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0);
248
249 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwNewpos, &dwWrite);
250
251 // if ((dwNewpos >= il) && (dwNewpos <= ir))
252 // Con_Printf("%d-%d p %d c\n", il, ir, dwNewpos);
253 }
254 #endif
255 }
256
257
258 /*
259 ===============================================================================
260
261 CHANNEL MIXING
262
263 ===============================================================================
264 */
265
266 void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime);
267 void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime);
268
S_PaintChannels(int endtime)269 void S_PaintChannels(int endtime)
270 {
271 int i;
272 int end;
273 channel_t *ch;
274 sfxcache_t *sc;
275 int ltime, count;
276
277 while (paintedtime < endtime)
278 {
279 // if paintbuffer is smaller than DMA buffer
280 end = endtime;
281 if (endtime - paintedtime > PAINTBUFFER_SIZE)
282 end = paintedtime + PAINTBUFFER_SIZE;
283
284 // clear the paint buffer
285 Q_memset(paintbuffer.paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t));
286
287 // paint in the channels.
288 ch = channels;
289 for (i=0; i<total_channels ; i++, ch++)
290 {
291 if (!ch->sfx)
292 continue;
293 if (!ch->leftvol && !ch->rightvol)
294 continue;
295 sc = S_LoadSound (ch->sfx);
296 if (!sc)
297 continue;
298
299 ltime = paintedtime;
300
301 while (ltime < end)
302 { // paint up to end
303 if (ch->end < end)
304 count = ch->end - ltime;
305 else
306 count = end - ltime;
307
308 if (count > 0)
309 {
310 if (sc->width == 1)
311 SND_PaintChannelFrom8(ch, sc, count);
312 else
313 SND_PaintChannelFrom16(ch, sc, count);
314
315 ltime += count;
316 }
317
318 // if at end of loop, restart
319 if (ltime >= ch->end)
320 {
321 if (sc->loopstart >= 0)
322 {
323 ch->pos = sc->loopstart;
324 ch->end = ltime + sc->length - ch->pos;
325 }
326 else
327 { // channel just stopped
328 ch->sfx = NULL;
329 break;
330 }
331 }
332 }
333
334 }
335
336 // transfer out according to DMA format
337 S_TransferPaintBuffer(end);
338 paintedtime = end;
339 }
340 }
341
SND_InitScaletable(void)342 void SND_InitScaletable (void)
343 {
344 int i, j;
345
346 for (i=0 ; i<32 ; i++)
347 for (j=0 ; j<256 ; j++)
348 snd_scaletable[i][j] = ((signed char)j) * i * 8;
349 }
350
351
352 #if !id386
353
SND_PaintChannelFrom8(channel_t * ch,sfxcache_t * sc,int count)354 void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count)
355 {
356 int data;
357 int *lscale, *rscale;
358 unsigned char *sfx;
359 int i;
360
361 if (ch->leftvol > 255)
362 ch->leftvol = 255;
363 if (ch->rightvol > 255)
364 ch->rightvol = 255;
365
366 lscale = snd_scaletable[ch->leftvol >> 3];
367 rscale = snd_scaletable[ch->rightvol >> 3];
368 sfx = sc->data.uc + ch->pos;
369
370 for (i=0 ; i<count ; i++)
371 {
372 data = sfx[i];
373 paintbuffer.paintbuffer[i].left += lscale[data];
374 paintbuffer.paintbuffer[i].right += rscale[data];
375 }
376
377 ch->pos += count;
378 }
379
380 #endif // !id386
381
382
SND_PaintChannelFrom16(channel_t * ch,sfxcache_t * sc,int count)383 void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count)
384 {
385 int data;
386 int left, right;
387 int leftvol, rightvol;
388 signed short *sfx;
389 int i;
390
391 leftvol = ch->leftvol;
392 rightvol = ch->rightvol;
393 sfx = sc->data.ss + ch->pos;
394
395 for (i=0 ; i<count ; i++)
396 {
397 data = sfx[i];
398 left = (data * leftvol) >> 8;
399 right = (data * rightvol) >> 8;
400 paintbuffer.paintbuffer[i].left += left;
401 paintbuffer.paintbuffer[i].right += right;
402 }
403
404 ch->pos += count;
405 }
406
407