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