whitespace
[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 #endif
27
28 // LordHavoc: was 512, expanded to 2048
29 #define PAINTBUFFER_SIZE 2048
30 portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
31 int snd_scaletable[32][256];
32
33 /*
34 // LordHavoc: disabled this because it desyncs with the video too easily
35 extern cvar_t cl_avidemo;
36 static FILE *cl_avidemo_soundfile = NULL;
37 void S_CaptureAVISound(portable_samplepair_t *buf, int length)
38 {
39         int i, n;
40         qbyte out[PAINTBUFFER_SIZE * 4];
41         char filename[MAX_OSPATH];
42
43         if (cl_avidemo.value >= 0.1f)
44         {
45                 if (cl_avidemo_soundfile == NULL)
46                 {
47                         sprintf (filename, "%s/dpavi.wav", com_gamedir);
48                         cl_avidemo_soundfile = fopen(filename, "wb");
49                         memset(out, 0, 44);
50                         fwrite(out, 1, 44, cl_avidemo_soundfile);
51                         // header will be filled out when file is closed
52                 }
53                 fseek(cl_avidemo_soundfile, 0, SEEK_END);
54                 // write the sound buffer as little endian 16bit interleaved stereo
55                 for(i = 0;i < length;i++)
56                 {
57                         n = buf[i].left >> 2; // quiet enough to prevent clipping most of the time
58                         n = bound(-32768, n, 32767);
59                         out[i*4+0] = n & 0xFF;
60                         out[i*4+1] = (n >> 8) & 0xFF;
61                         n = buf[i].right >> 2; // quiet enough to prevent clipping most of the time
62                         n = bound(-32768, n, 32767);
63                         out[i*4+2] = n & 0xFF;
64                         out[i*4+3] = (n >> 8) & 0xFF;
65                 }
66                 if (fwrite(out, 4, length, cl_avidemo_soundfile) < length)
67                 {
68                         Cvar_SetValueQuick(&cl_avidemo, 0);
69                         Con_Printf("avi saving sound failed, out of disk space?  stopping avi demo capture.\n");
70                 }
71         }
72         else if (cl_avidemo_soundfile)
73         {
74                 // file has not been closed yet, close it
75                 fseek(cl_avidemo_soundfile, 0, SEEK_END);
76                 i = ftell(cl_avidemo_soundfile);
77
78                 //"RIFF", (int) unknown (chunk size), "WAVE",
79                 //"fmt ", (int) 16 (chunk size), (short) format 1 (uncompressed PCM), (short) 2 channels, (int) unknown rate, (int) unknown bytes per second, (short) 4 bytes per sample (channels * bytes per channel), (short) 16 bits per channel
80                 //"data", (int) unknown (chunk size)
81                 memcpy(out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\x00data****", 44);
82                 // the length of the whole RIFF chunk
83                 n = i - 8;
84                 out[4] = (n) & 0xFF;
85                 out[5] = (n >> 8) & 0xFF;
86                 out[6] = (n >> 16) & 0xFF;
87                 out[7] = (n >> 24) & 0xFF;
88                 // rate
89                 n = shm->speed;
90                 out[24] = (n) & 0xFF;
91                 out[25] = (n >> 8) & 0xFF;
92                 out[26] = (n >> 16) & 0xFF;
93                 out[27] = (n >> 24) & 0xFF;
94                 // bytes per second (rate * channels * bytes per channel)
95                 n = shm->speed * 4;
96                 out[28] = (n) & 0xFF;
97                 out[29] = (n >> 8) & 0xFF;
98                 out[30] = (n >> 16) & 0xFF;
99                 out[31] = (n >> 24) & 0xFF;
100                 // the length of the data chunk
101                 n = i - 44;
102                 out[40] = (n) & 0xFF;
103                 out[41] = (n >> 8) & 0xFF;
104                 out[42] = (n >> 16) & 0xFF;
105                 out[43] = (n >> 24) & 0xFF;
106
107                 fseek(cl_avidemo_soundfile, 0, SEEK_SET);
108                 fwrite(out, 1, 44, cl_avidemo_soundfile);
109                 fclose(cl_avidemo_soundfile);
110                 cl_avidemo_soundfile = NULL;
111         }
112 }
113 */
114
115 void S_TransferPaintBuffer(int endtime)
116 {
117         void *pbuf;
118         if ((pbuf = S_LockBuffer()))
119         {
120                 int i;
121                 int *snd_p;
122                 int snd_vol;
123                 int lpaintedtime;
124                 int snd_linear_count;
125                 int val;
126                 snd_p = (int *) paintbuffer;
127                 snd_vol = volume.value*256;
128                 lpaintedtime = paintedtime;
129                 if (shm->samplebits == 16)
130                 {
131                         // 16bit
132                         short *snd_out;
133                         if (shm->channels == 2)
134                         {
135                                 // 16bit 2 channels (stereo)
136                                 while (lpaintedtime < endtime)
137                                 {
138                                         // handle recirculating buffer issues
139                                         i = lpaintedtime & ((shm->samples >> 1) - 1);
140                                         snd_out = (short *) pbuf + (i << 1);
141                                         snd_linear_count = (shm->samples >> 1) - i;
142                                         if (snd_linear_count > endtime - lpaintedtime)
143                                                 snd_linear_count = endtime - lpaintedtime;
144                                         snd_linear_count <<= 1;
145                                         if (snd_swapstereo.value)
146                                         {
147                                                 for (i = 0;i < snd_linear_count;i += 2)
148                                                 {
149                                                         val = (snd_p[i + 1] * snd_vol) >> 8;
150                                                         snd_out[i    ] = bound(-32768, val, 32767);
151                                                         val = (snd_p[i    ] * snd_vol) >> 8;
152                                                         snd_out[i + 1] = bound(-32768, val, 32767);
153                                                 }
154                                         }
155                                         else
156                                         {
157                                                 for (i = 0;i < snd_linear_count;i += 2)
158                                                 {
159                                                         val = (snd_p[i    ] * snd_vol) >> 8;
160                                                         snd_out[i    ] = bound(-32768, val, 32767);
161                                                         val = (snd_p[i + 1] * snd_vol) >> 8;
162                                                         snd_out[i + 1] = bound(-32768, val, 32767);
163                                                 }
164                                         }
165                                         snd_p += snd_linear_count;
166                                         lpaintedtime += (snd_linear_count >> 1);
167                                 }
168                         }
169                         else
170                         {
171                                 // 16bit 1 channel (mono)
172                                 while (lpaintedtime < endtime)
173                                 {
174                                         // handle recirculating buffer issues
175                                         i = lpaintedtime & (shm->samples - 1);
176                                         snd_out = (short *) pbuf + i;
177                                         snd_linear_count = shm->samples - i;
178                                         if (snd_linear_count > endtime - lpaintedtime)
179                                                 snd_linear_count = endtime - lpaintedtime;
180                                         for (i = 0;i < snd_linear_count;i++)
181                                         {
182                                                 val = ((snd_p[i * 2 + 0] + snd_p[i * 2 + 1]) * snd_vol) >> 9;
183                                                 snd_out[i] = bound(-32768, val, 32767);
184                                         }
185                                         snd_p += snd_linear_count << 1;
186                                         lpaintedtime += snd_linear_count;
187                                 }
188                         }
189                 }
190                 else
191                 {
192                         // 8bit
193                         unsigned char *snd_out;
194                         if (shm->channels == 2)
195                         {
196                                 // 8bit 2 channels (stereo)
197                                 while (lpaintedtime < endtime)
198                                 {
199                                         // handle recirculating buffer issues
200                                         i = lpaintedtime & ((shm->samples >> 1) - 1);
201                                         snd_out = (unsigned char *) pbuf + (i << 1);
202                                         snd_linear_count = (shm->samples >> 1) - i;
203                                         if (snd_linear_count > endtime - lpaintedtime)
204                                                 snd_linear_count = endtime - lpaintedtime;
205                                         snd_linear_count <<= 1;
206                                         if (snd_swapstereo.value)
207                                         {
208                                                 for (i = 0;i < snd_linear_count;i += 2)
209                                                 {
210                                                         val = ((snd_p[i + 1] * snd_vol) >> 16) + 128;
211                                                         snd_out[i    ] = bound(0, val, 255);
212                                                         val = ((snd_p[i    ] * snd_vol) >> 16) + 128;
213                                                         snd_out[i + 1] = bound(0, val, 255);
214                                                 }
215                                         }
216                                         else
217                                         {
218                                                 for (i = 0;i < snd_linear_count;i += 2)
219                                                 {
220                                                         val = ((snd_p[i    ] * snd_vol) >> 16) + 128;
221                                                         snd_out[i    ] = bound(0, val, 255);
222                                                         val = ((snd_p[i + 1] * snd_vol) >> 16) + 128;
223                                                         snd_out[i + 1] = bound(0, val, 255);
224                                                 }
225                                         }
226                                         snd_p += snd_linear_count;
227                                         lpaintedtime += (snd_linear_count >> 1);
228                                 }
229                         }
230                         else
231                         {
232                                 // 8bit 1 channel (mono)
233                                 while (lpaintedtime < endtime)
234                                 {
235                                         // handle recirculating buffer issues
236                                         i = lpaintedtime & (shm->samples - 1);
237                                         snd_out = (unsigned char *) pbuf + i;
238                                         snd_linear_count = shm->samples - i;
239                                         if (snd_linear_count > endtime - lpaintedtime)
240                                                 snd_linear_count = endtime - lpaintedtime;
241                                         for (i = 0;i < snd_linear_count;i++)
242                                         {
243                                                 val = (((snd_p[i * 2] + snd_p[i * 2 + 1]) * snd_vol) >> 17) + 128;
244                                                 snd_out[i    ] = bound(0, val, 255);
245                                         }
246                                         snd_p += snd_linear_count << 1;
247                                         lpaintedtime += snd_linear_count;
248                                 }
249                         }
250                 }
251
252                 S_UnlockBuffer();
253         }
254 }
255
256
257 /*
258 ===============================================================================
259
260 CHANNEL MIXING
261
262 ===============================================================================
263 */
264
265 void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime);
266 void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime);
267
268 void S_PaintChannels(int endtime)
269 {
270         int i;
271         int end;
272         channel_t *ch;
273         sfxcache_t *sc;
274         int ltime, count;
275
276         while (paintedtime < endtime)
277         {
278                 // if paintbuffer is smaller than DMA buffer
279                 end = endtime;
280                 if (endtime - paintedtime > PAINTBUFFER_SIZE)
281                         end = paintedtime + PAINTBUFFER_SIZE;
282
283                 // clear the paint buffer, filling it with data from rawsamples (music/video/whatever)
284                 S_RawSamples_Dequeue(&paintbuffer->left, end - paintedtime);
285
286                 // paint in the channels.
287                 ch = channels;
288                 for (i=0; i<total_channels ; i++, ch++)
289                 {
290                         if (!ch->sfx)
291                                 continue;
292                         if (!ch->leftvol && !ch->rightvol)
293                                 continue;
294                         sc = S_LoadSound (ch->sfx, true);
295                         if (!sc)
296                                 continue;
297
298                         ltime = paintedtime;
299
300                         while (ltime < end)
301                         {
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                                         {
328                                                 // channel just stopped
329                                                 ch->sfx = NULL;
330                                                 break;
331                                         }
332                                 }
333                         }
334                 }
335
336                 // transfer out according to DMA format
337                 //S_CaptureAVISound(paintbuffer, end - paintedtime);
338                 S_TransferPaintBuffer(end);
339                 paintedtime = end;
340         }
341 }
342
343 void SND_InitScaletable (void)
344 {
345         int i, j;
346
347         for (i = 0;i < 32;i++)
348                 for (j = 0;j < 256;j++)
349                         snd_scaletable[i][j] = ((signed char)j) * i * 8;
350 }
351
352
353 void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count)
354 {
355         int *lscale, *rscale;
356         unsigned char *sfx;
357         int i;
358
359         if (ch->leftvol > 255)
360                 ch->leftvol = 255;
361         if (ch->rightvol > 255)
362                 ch->rightvol = 255;
363
364         lscale = snd_scaletable[ch->leftvol >> 3];
365         rscale = snd_scaletable[ch->rightvol >> 3];
366         if (sc->stereo)
367         {
368                 // LordHavoc: stereo sound support, and optimizations
369                 sfx = (unsigned char *)sc->data + ch->pos * 2;
370
371                 for (i=0 ; i<count ; i++)
372                 {
373                         paintbuffer[i].left += lscale[*sfx++];
374                         paintbuffer[i].right += rscale[*sfx++];
375                 }
376                 
377         }
378         else
379         {
380                 sfx = (unsigned char *)sc->data + ch->pos;
381
382                 for (i=0 ; i<count ; i++)
383                 {
384                         paintbuffer[i].left += lscale[*sfx];
385                         paintbuffer[i].right += rscale[*sfx++];
386                 }
387
388         }
389         ch->pos += count;
390 }
391
392 void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count)
393 {
394         int leftvol, rightvol;
395         signed short *sfx;
396         int i;
397
398         leftvol = ch->leftvol;
399         rightvol = ch->rightvol;
400         if (sc->stereo)
401         {
402                 // LordHavoc: stereo sound support, and optimizations
403                 sfx = (signed short *)sc->data + ch->pos * 2;
404
405                 for (i=0 ; i<count ; i++)
406                 {
407                         paintbuffer[i].left += (*sfx++ * leftvol) >> 8;
408                         paintbuffer[i].right += (*sfx++ * rightvol) >> 8;
409                 }
410         }
411         else
412         {
413                 sfx = (signed short *)sc->data + ch->pos;
414
415                 for (i=0 ; i<count ; i++)
416                 {
417                         paintbuffer[i].left += (*sfx * leftvol) >> 8;
418                         paintbuffer[i].right += (*sfx++ * rightvol) >> 8;
419                 }
420         }
421
422         ch->pos += count;
423 }
424