]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - snd_mix.c
When below 1 fps, the fps counter instead counts spf (seconds per frame). Note that...
[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
21
22 #include "quakedef.h"
23 #include "snd_main.h"
24
25 typedef struct
26 {
27         int left;
28         int right;
29 } portable_samplepair_t;
30
31 // LordHavoc: was 512, expanded to 2048
32 #define PAINTBUFFER_SIZE 2048
33 portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
34
35 // FIXME: this desyncs with the video too easily
36 extern qboolean cl_capturevideo_active;
37 extern void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate);
38 void S_CaptureAVISound(portable_samplepair_t *buf, size_t length)
39 {
40         int n;
41         size_t i;
42         qbyte out[PAINTBUFFER_SIZE * 4];
43         if (!cl_capturevideo_active)
44                 return;
45         // write the sound buffer as little endian 16bit interleaved stereo
46         for(i = 0;i < length;i++)
47         {
48                 n = buf[i].left;
49                 n = bound(-32768, n, 32767);
50                 out[i*4+0] = n & 0xFF;
51                 out[i*4+1] = (n >> 8) & 0xFF;
52                 n = buf[i].right;
53                 n = bound(-32768, n, 32767);
54                 out[i*4+2] = n & 0xFF;
55                 out[i*4+3] = (n >> 8) & 0xFF;
56         }
57         SCR_CaptureVideo_SoundFrame(out, length, shm->format.speed);
58 }
59
60 // TODO: rewrite this function
61 void S_TransferPaintBuffer(int endtime)
62 {
63         void *pbuf;
64         if ((pbuf = S_LockBuffer()))
65         {
66                 int i;
67                 int *snd_p;
68                 int lpaintedtime;
69                 int snd_linear_count;
70                 int val;
71                 snd_p = (int *) paintbuffer;
72                 lpaintedtime = paintedtime;
73                 if (shm->format.width == 2)
74                 {
75                         // 16bit
76                         short *snd_out;
77                         if (shm->format.channels == 2)
78                         {
79                                 // 16bit 2 channels (stereo)
80                                 while (lpaintedtime < endtime)
81                                 {
82                                         // handle recirculating buffer issues
83                                         i = lpaintedtime & ((shm->samples >> 1) - 1);
84                                         snd_out = (short *) pbuf + (i << 1);
85                                         snd_linear_count = (shm->samples >> 1) - i;
86                                         if (snd_linear_count > endtime - lpaintedtime)
87                                                 snd_linear_count = endtime - lpaintedtime;
88                                         snd_linear_count <<= 1;
89                                         if (snd_swapstereo.value)
90                                         {
91                                                 for (i = 0;i < snd_linear_count;i += 2)
92                                                 {
93                                                         snd_out[i    ] = bound(-32768, snd_p[i + 1], 32767);
94                                                         snd_out[i + 1] = bound(-32768, snd_p[i    ], 32767);
95                                                 }
96                                         }
97                                         else
98                                         {
99                                                 for (i = 0;i < snd_linear_count;i += 2)
100                                                 {
101                                                         snd_out[i    ] = bound(-32768, snd_p[i    ], 32767);
102                                                         snd_out[i + 1] = bound(-32768, snd_p[i + 1], 32767);
103                                                 }
104                                         }
105                                         snd_p += snd_linear_count;
106                                         lpaintedtime += (snd_linear_count >> 1);
107                                 }
108                         }
109                         else
110                         {
111                                 // 16bit 1 channel (mono)
112                                 while (lpaintedtime < endtime)
113                                 {
114                                         // handle recirculating buffer issues
115                                         i = lpaintedtime & (shm->samples - 1);
116                                         snd_out = (short *) pbuf + i;
117                                         snd_linear_count = shm->samples - i;
118                                         if (snd_linear_count > endtime - lpaintedtime)
119                                                 snd_linear_count = endtime - lpaintedtime;
120                                         for (i = 0;i < snd_linear_count;i++)
121                                         {
122                                                 val = (snd_p[i * 2 + 0] + snd_p[i * 2 + 1]) >> 1;
123                                                 snd_out[i] = bound(-32768, val, 32767);
124                                         }
125                                         snd_p += snd_linear_count << 1;
126                                         lpaintedtime += snd_linear_count;
127                                 }
128                         }
129                 }
130                 else
131                 {
132                         // 8bit
133                         unsigned char *snd_out;
134                         if (shm->format.channels == 2)
135                         {
136                                 // 8bit 2 channels (stereo)
137                                 while (lpaintedtime < endtime)
138                                 {
139                                         // handle recirculating buffer issues
140                                         i = lpaintedtime & ((shm->samples >> 1) - 1);
141                                         snd_out = (unsigned char *) pbuf + (i << 1);
142                                         snd_linear_count = (shm->samples >> 1) - i;
143                                         if (snd_linear_count > endtime - lpaintedtime)
144                                                 snd_linear_count = endtime - lpaintedtime;
145                                         snd_linear_count <<= 1;
146                                         if (snd_swapstereo.value)
147                                         {
148                                                 for (i = 0;i < snd_linear_count;i += 2)
149                                                 {
150                                                         val = (snd_p[i + 1] >> 8) + 128;
151                                                         snd_out[i    ] = bound(0, val, 255);
152                                                         val = (snd_p[i    ] >> 8) + 128;
153                                                         snd_out[i + 1] = bound(0, val, 255);
154                                                 }
155                                         }
156                                         else
157                                         {
158                                                 for (i = 0;i < snd_linear_count;i += 2)
159                                                 {
160                                                         val = (snd_p[i    ] >> 8) + 128;
161                                                         snd_out[i    ] = bound(0, val, 255);
162                                                         val = (snd_p[i + 1] >> 8) + 128;
163                                                         snd_out[i + 1] = bound(0, val, 255);
164                                                 }
165                                         }
166                                         snd_p += snd_linear_count;
167                                         lpaintedtime += (snd_linear_count >> 1);
168                                 }
169                         }
170                         else
171                         {
172                                 // 8bit 1 channel (mono)
173                                 while (lpaintedtime < endtime)
174                                 {
175                                         // handle recirculating buffer issues
176                                         i = lpaintedtime & (shm->samples - 1);
177                                         snd_out = (unsigned char *) pbuf + i;
178                                         snd_linear_count = shm->samples - i;
179                                         if (snd_linear_count > endtime - lpaintedtime)
180                                                 snd_linear_count = endtime - lpaintedtime;
181                                         for (i = 0;i < snd_linear_count;i++)
182                                         {
183                                                 val = ((snd_p[i * 2] + snd_p[i * 2 + 1]) >> 9) + 128;
184                                                 snd_out[i    ] = bound(0, val, 255);
185                                         }
186                                         snd_p += snd_linear_count << 1;
187                                         lpaintedtime += snd_linear_count;
188                                 }
189                         }
190                 }
191
192                 S_UnlockBuffer();
193         }
194 }
195
196
197 /*
198 ===============================================================================
199
200 CHANNEL MIXING
201
202 ===============================================================================
203 */
204
205 qboolean SND_PaintChannelFrom8 (channel_t *ch, int endtime);
206 qboolean SND_PaintChannelFrom16 (channel_t *ch, int endtime);
207
208 void S_PaintChannels(int endtime)
209 {
210         unsigned int i;
211         int end;
212         channel_t *ch;
213         sfx_t *sfx;
214         int ltime, count;
215
216         while (paintedtime < endtime)
217         {
218                 // if paintbuffer is smaller than DMA buffer
219                 end = endtime;
220                 if (endtime - paintedtime > PAINTBUFFER_SIZE)
221                         end = paintedtime + PAINTBUFFER_SIZE;
222
223                 // clear the paint buffer
224                 memset (&paintbuffer, 0, (end - paintedtime) * sizeof (paintbuffer[0]));
225
226                 // paint in the channels.
227                 ch = channels;
228                 for (i=0; i<total_channels ; i++, ch++)
229                 {
230                         sfx = ch->sfx;
231                         if (!sfx)
232                                 continue;
233                         if (!ch->leftvol && !ch->rightvol)
234                                 continue;
235                         if (!S_LoadSound (sfx, true))
236                                 continue;
237
238                         // if the channel is paused
239                         if (ch->flags & CHANNELFLAG_PAUSED)
240                         {
241                                 int pausedtime = end - paintedtime;
242                                 ch->lastptime += pausedtime;
243                                 ch->end += pausedtime;
244                                 continue;
245                         }
246
247                         // if the sound hasn't been painted last time, update his position
248                         if (ch->lastptime < paintedtime)
249                         {
250                                 ch->pos += paintedtime - ch->lastptime;
251
252                                 // If the sound should have ended by then
253                                 if ((unsigned int)ch->pos > sfx->total_length)
254                                 {
255                                         int loopstart;
256
257                                         if (ch->flags & CHANNELFLAG_FORCELOOP)
258                                                 loopstart = 0;
259                                         else
260                                                 loopstart = -1;
261                                         if (sfx->loopstart >= 0)
262                                                 loopstart = sfx->loopstart;
263
264                                         // If the sound is looped
265                                         if (loopstart >= 0)
266                                                 ch->pos = (ch->pos - sfx->total_length) % (sfx->total_length - loopstart) + loopstart;
267                                         else
268                                                 ch->pos = sfx->total_length;
269                                         ch->end = paintedtime + sfx->total_length - ch->pos;
270                                 }
271                         }
272
273                         ltime = paintedtime;
274                         while (ltime < end)
275                         {
276                                 qboolean stop_paint;
277
278                                 // paint up to end
279                                 if (ch->end < end)
280                                         count = (int)ch->end - ltime;
281                                 else
282                                         count = end - ltime;
283
284                                 if (count > 0)
285                                 {
286                                         if (ch->leftvol > 255)
287                                                 ch->leftvol = 255;
288                                         if (ch->rightvol > 255)
289                                                 ch->rightvol = 255;
290
291                                         if (sfx->format.width == 1)
292                                                 stop_paint = !SND_PaintChannelFrom8 (ch, count);
293                                         else
294                                                 stop_paint = !SND_PaintChannelFrom16 (ch, count);
295
296                                         if (!stop_paint)
297                                         {
298                                                 ltime += count;
299                                                 ch->lastptime = ltime;
300                                         }
301                                 }
302                                 else
303                                         stop_paint = false;
304
305                                 if (ltime >= ch->end)
306                                 {
307                                         // if at end of loop, restart
308                                         if ((sfx->loopstart >= 0 || (ch->flags & CHANNELFLAG_FORCELOOP)) && !stop_paint)
309                                         {
310                                                 ch->pos = bound(0, sfx->loopstart, (int)sfx->total_length - 1);
311                                                 ch->end = ltime + sfx->total_length - ch->pos;
312                                         }
313                                         // channel just stopped
314                                         else
315                                                 stop_paint = true;
316                                 }
317
318                                 if (stop_paint)
319                                 {
320                                         S_StopChannel (ch - channels);
321                                         break;
322                                 }
323                         }
324                 }
325
326                 // transfer out according to DMA format
327                 S_CaptureAVISound (paintbuffer, end - paintedtime);
328                 S_TransferPaintBuffer(end);
329                 paintedtime = end;
330         }
331 }
332
333
334 // TODO: Try to merge SND_PaintChannelFrom8 and SND_PaintChannelFrom16
335 qboolean SND_PaintChannelFrom8 (channel_t *ch, int count)
336 {
337         int snd_vol, leftvol, rightvol;
338         const signed char *sfx;
339         const sfxbuffer_t *sb;
340         int i;
341
342         // If this channel manages its own volume
343         if (ch->flags & CHANNELFLAG_FULLVOLUME)
344                 snd_vol = 256;
345         else
346                 snd_vol = volume.value * 256;
347
348         leftvol = ch->leftvol * snd_vol;
349         rightvol = ch->rightvol * snd_vol;
350
351         sb = ch->sfx->fetcher->getsb (ch, ch->pos, count);
352         if (sb == NULL)
353                 return false;
354
355         // Stereo sound support
356         if (ch->sfx->format.channels == 2)
357         {
358                 sfx = sb->data + (ch->pos - sb->offset) * 2;
359                 for (i = 0;i < count;i++)
360                 {
361                         paintbuffer[i].left += (*sfx++ * leftvol) >> 8;
362                         paintbuffer[i].right += (*sfx++ * rightvol) >> 8;
363                 }
364         }
365         else
366         {
367                 sfx = sb->data + ch->pos - sb->offset;
368                 for (i = 0;i < count;i++)
369                 {
370                         paintbuffer[i].left += (*sfx * leftvol) >> 8;
371                         paintbuffer[i].right += (*sfx++ * rightvol) >> 8;
372                 }
373
374         }
375         ch->pos += count;
376         return true;
377 }
378
379 qboolean SND_PaintChannelFrom16 (channel_t *ch, int count)
380 {
381         int snd_vol, leftvol, rightvol;
382         signed short *sfx;
383         const sfxbuffer_t *sb;
384         int i;
385
386         // If this channel manages its own volume
387         if (ch->flags & CHANNELFLAG_FULLVOLUME)
388                 snd_vol = 256;
389         else
390                 snd_vol = volume.value * 256;
391
392         leftvol = ch->leftvol * snd_vol;
393         rightvol = ch->rightvol * snd_vol;
394
395         sb = ch->sfx->fetcher->getsb (ch, ch->pos, count);
396         if (sb == NULL)
397                 return false;
398
399         // Stereo sound support
400         if (ch->sfx->format.channels == 2)
401         {
402                 sfx = (signed short *)sb->data + (ch->pos - sb->offset) * 2;
403
404                 for (i=0 ; i<count ; i++)
405                 {
406                         paintbuffer[i].left += (*sfx++ * leftvol) >> 16;
407                         paintbuffer[i].right += (*sfx++ * rightvol) >> 16;
408                 }
409         }
410         else
411         {
412                 sfx = (signed short *)sb->data + ch->pos - sb->offset;
413
414                 for (i=0 ; i<count ; i++)
415                 {
416                         paintbuffer[i].left += (*sfx * leftvol) >> 16;
417                         paintbuffer[i].right += (*sfx++ * rightvol) >> 16;
418                 }
419         }
420
421         ch->pos += count;
422         return true;
423 }
424