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