cleaned up nearly all of the externs in .c files (moved to appropriate .h files)
[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 void Snd_WriteLinearBlastStereo16 (void)
40 {
41         int             i;
42         int             val;
43
44         if (snd_swapstereo.value)
45         {
46                 for (i=0 ; i<snd_linear_count ; i+=2)
47                 {
48                         val = (snd_p[i+1]*snd_vol)>>8;
49                         snd_out[i  ] = bound(-32768, val, 32767);
50                         val = (snd_p[i  ]*snd_vol)>>8;
51                         snd_out[i+1] = bound(-32768, val, 32767);
52                 }
53         }
54         else
55         {
56                 for (i=0 ; i<snd_linear_count ; i+=2)
57                 {
58                         val = (snd_p[i]*snd_vol)>>8;
59                         snd_out[i] = bound(-32768, val, 32767);
60                         val = (snd_p[i+1]*snd_vol)>>8;
61                         snd_out[i+1] = bound(-32768, val, 32767);
62                 }
63         }
64 }
65
66 void S_TransferStereo16 (int endtime)
67 {
68         int             lpos;
69         int             lpaintedtime;
70         DWORD   *pbuf;
71 #ifdef _WIN32
72         int             reps;
73         DWORD   dwSize,dwSize2;
74         DWORD   *pbuf2;
75         HRESULT hresult;
76 #endif
77         
78         snd_vol = volume.value*256;
79
80         snd_p = (int *) paintbuffer;
81         lpaintedtime = paintedtime;
82
83 #ifdef _WIN32
84         if (pDSBuf)
85         {
86                 reps = 0;
87
88                 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, &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
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, &pbuf2,&dwSize2, 0)) != DS_OK)
175                 {
176                         if (hresult != DSERR_BUFFERLOST)
177                         {
178                                 Con_Printf ("S_TransferPaintBuffer: DS::Lock Sound Buffer Failed\n");
179                                 S_Shutdown ();
180                                 S_Startup ();
181                                 return;
182                         }
183
184                         if (++reps > 10000)
185                         {
186                                 Con_Printf ("S_TransferPaintBuffer: DS: couldn't restore buffer\n");
187                                 S_Shutdown ();
188                                 S_Startup ();
189                                 return;
190                         }
191                 }
192         }
193         else
194 #endif
195         {
196                 pbuf = (DWORD *)shm->buffer;
197         }
198
199         if (shm->samplebits == 16)
200         {
201                 short *out = (short *) pbuf;
202                 while (count--)
203                 {
204                         val = (*p * snd_vol) >> 8;
205                         out[out_idx] = bound(-32768, val, 32767);
206                         p+= step;
207                         out_idx = (out_idx + 1) & out_mask;
208                 }
209         }
210         else if (shm->samplebits == 8)
211         {
212                 unsigned char *out = (unsigned char *) pbuf;
213                 while (count--)
214                 {
215                         val = ((*p * snd_vol) >> 16) + 128;
216                         out[out_idx] = bound(0, val, 255);
217                         p+= step;
218                         out_idx = (out_idx + 1) & out_mask;
219                 }
220         }
221
222 #ifdef _WIN32
223         if (pDSBuf)
224         {
225                 DWORD dwNewpos, dwWrite;
226                 int il = paintedtime;
227                 int ir = endtime - paintedtime;
228                 
229                 ir += il;
230
231                 pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0);
232
233                 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwNewpos, &dwWrite);
234
235 //              if ((dwNewpos >= il) && (dwNewpos <= ir))
236 //                      Con_Printf("%d-%d p %d c\n", il, ir, dwNewpos);
237         }
238 #endif
239 }
240
241
242 /*
243 ===============================================================================
244
245 CHANNEL MIXING
246
247 ===============================================================================
248 */
249
250 void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime);
251 void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime);
252
253 void S_PaintChannels(int endtime)
254 {
255         int     i;
256         int     end;
257         channel_t *ch;
258         sfxcache_t      *sc;
259         int             ltime, count;
260
261         while (paintedtime < endtime)
262         {
263                 // if paintbuffer is smaller than DMA buffer
264                 end = endtime;
265                 if (endtime - paintedtime > PAINTBUFFER_SIZE)
266                         end = paintedtime + PAINTBUFFER_SIZE;
267
268                 // clear the paint buffer
269                 memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t));
270
271                 // paint in the channels.
272                 ch = channels;
273                 for (i=0; i<total_channels ; i++, ch++)
274                 {
275                         if (!ch->sfx)
276                                 continue;
277                         if (!ch->leftvol && !ch->rightvol)
278                                 continue;
279                         sc = S_LoadSound (ch->sfx);
280                         if (!sc)
281                                 continue;
282
283                         ltime = paintedtime;
284
285                         while (ltime < end)
286                         {
287                                 // paint up to end
288                                 if (ch->end < end)
289                                         count = ch->end - ltime;
290                                 else
291                                         count = end - ltime;
292
293                                 if (count > 0)
294                                 {       
295                                         if (sc->width == 1)
296                                                 SND_PaintChannelFrom8(ch, sc, count);
297                                         else
298                                                 SND_PaintChannelFrom16(ch, sc, count);
299         
300                                         ltime += count;
301                                 }
302
303                                 // if at end of loop, restart
304                                 if (ltime >= ch->end)
305                                 {
306                                         if (sc->loopstart >= 0)
307                                         {
308                                                 ch->pos = sc->loopstart;
309                                                 ch->end = ltime + sc->length - ch->pos;
310                                         }
311                                         else                            
312                                         {
313                                                 // channel just stopped
314                                                 ch->sfx = NULL;
315                                                 break;
316                                         }
317                                 }
318                         }
319                                                                                                                           
320                 }
321
322                 // transfer out according to DMA format
323                 S_TransferPaintBuffer(end);
324                 paintedtime = end;
325         }
326 }
327
328 void SND_InitScaletable (void)
329 {
330         int             i, j;
331         
332         for (i=0 ; i<32 ; i++)
333                 for (j=0 ; j<256 ; j++)
334                         snd_scaletable[i][j] = ((signed char)j) * i * 8;
335 }
336
337
338 void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count)
339 {
340 //      int     data;
341         int             *lscale, *rscale;
342         unsigned char *sfx;
343         int             i;
344
345         if (ch->leftvol > 255)
346                 ch->leftvol = 255;
347         if (ch->rightvol > 255)
348                 ch->rightvol = 255;
349
350         lscale = snd_scaletable[ch->leftvol >> 3];
351         rscale = snd_scaletable[ch->rightvol >> 3];
352         if (sc->stereo)
353         {
354                 // LordHavoc: stereo sound support, and optimizations
355                 sfx = (unsigned char *)sc->data + ch->pos * 2;
356
357                 for (i=0 ; i<count ; i++)
358                 {
359                         paintbuffer[i].left += lscale[*sfx++];
360                         paintbuffer[i].right += rscale[*sfx++];
361                 }
362                 
363         }
364         else
365         {
366                 sfx = (unsigned char *)sc->data + ch->pos;
367
368                 for (i=0 ; i<count ; i++)
369                 {
370                         paintbuffer[i].left += lscale[*sfx];
371                         paintbuffer[i].right += rscale[*sfx++];
372                 }
373                 
374         }
375         ch->pos += count;
376 }
377
378 void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count)
379 {
380 //      int data;
381 //      int left, right;
382         int leftvol, rightvol;
383         signed short *sfx;
384         int     i;
385
386         leftvol = ch->leftvol;
387         rightvol = ch->rightvol;
388         if (sc->stereo)
389         {
390                 // LordHavoc: stereo sound support, and optimizations
391                 sfx = (signed short *)sc->data + ch->pos * 2;
392
393                 for (i=0 ; i<count ; i++)
394                 {
395                         paintbuffer[i].left += (*sfx++ * leftvol) >> 8;
396                         paintbuffer[i].right += (*sfx++ * rightvol) >> 8;
397                 }
398         }
399         else
400         {
401                 sfx = (signed short *)sc->data + ch->pos;
402
403                 for (i=0 ; i<count ; i++)
404                 {
405                         paintbuffer[i].left += (*sfx * leftvol) >> 8;
406                         paintbuffer[i].right += (*sfx++ * rightvol) >> 8;
407                 }
408         }
409
410         ch->pos += count;
411 }
412