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