1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, 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
32typedef union {
33    portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
34    int intbuf[PAINTBUFFER_SIZE * sizeof(portable_samplepair_t) / sizeof(int)];
35} portableOrInt_t;
36
37portableOrInt_t paintbuffer;
38int		snd_scaletable[32][256];
39int 	*snd_p, snd_linear_count, snd_vol;
40short	*snd_out;
41
42void Snd_WriteLinearBlastStereo16 (void);
43
44extern void SNDDMA_ReportWrite(size_t lengthBytes);
45#if	!id386
46void Snd_WriteLinearBlastStereo16 (void)
47{
48	int		i;
49	int		val;
50
51	for (i=0 ; i<snd_linear_count ; i+=2)
52	{
53		val = (snd_p[i]*snd_vol)>>8;
54		if (val > 0x7fff)
55			snd_out[i] = 0x7fff;
56		else if (val < (short)0x8000)
57			snd_out[i] = (short)0x8000;
58		else
59			snd_out[i] = val;
60
61		val = (snd_p[i+1]*snd_vol)>>8;
62		if (val > 0x7fff)
63			snd_out[i+1] = 0x7fff;
64		else if (val < (short)0x8000)
65			snd_out[i+1] = (short)0x8000;
66		else
67			snd_out[i+1] = val;
68	}
69	SNDDMA_ReportWrite(snd_linear_count << 1);
70}
71#endif
72
73void S_TransferStereo16 (int endtime)
74{
75	int		lpos;
76	int		lpaintedtime;
77	DWORD	*pbuf;
78#ifdef _WIN32
79	int		reps;
80	DWORD	dwSize,dwSize2;
81	DWORD	*pbuf2;
82	HRESULT	hresult;
83#endif
84
85	snd_vol = (int)(volume.value*256);
86
87	snd_p = paintbuffer.intbuf;
88	lpaintedtime = paintedtime;
89
90#ifdef _WIN32
91	if (pDSBuf)
92	{
93		reps = 0;
94
95		while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize,
96									   &pbuf2, &dwSize2, 0)) != DS_OK)
97		{
98			if (hresult != DSERR_BUFFERLOST)
99			{
100				Con_Printf ("S_TransferStereo16: DS::Lock Sound Buffer Failed\n");
101				S_Shutdown ();
102				S_Startup ();
103				return;
104			}
105
106			if (++reps > 10000)
107			{
108				Con_Printf ("S_TransferStereo16: DS: couldn't restore buffer\n");
109				S_Shutdown ();
110				S_Startup ();
111				return;
112			}
113		}
114	}
115	else
116#endif
117	{
118		pbuf = (DWORD *)shm->buffer;
119	}
120
121	while (lpaintedtime < endtime)
122	{
123	// handle recirculating buffer issues
124		lpos = lpaintedtime & ((shm->samples>>1)-1);
125
126		snd_out = (short *) pbuf + (lpos<<1);
127
128		snd_linear_count = (shm->samples>>1) - lpos;
129		if (lpaintedtime + snd_linear_count > endtime)
130			snd_linear_count = endtime - lpaintedtime;
131
132		snd_linear_count <<= 1;
133
134	// write a linear blast of samples
135		Snd_WriteLinearBlastStereo16 ();
136
137		snd_p += snd_linear_count;
138		lpaintedtime += (snd_linear_count>>1);
139	}
140
141#ifdef _WIN32
142	if (pDSBuf)
143		pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0);
144#endif
145}
146
147void S_TransferPaintBuffer(int endtime)
148{
149	int 	out_idx;
150	int 	count;
151	int 	out_mask;
152	int 	*p;
153	int 	step;
154	int		val;
155	int		snd_vol;
156	DWORD	*pbuf;
157#ifdef _WIN32
158	int		reps;
159	DWORD	dwSize,dwSize2;
160	DWORD	*pbuf2;
161	HRESULT	hresult;
162#endif
163
164	if (shm->samplebits == 16 && shm->channels == 2)
165	{
166		S_TransferStereo16 (endtime);
167		return;
168	}
169
170	p = paintbuffer.intbuf;
171	count = (endtime - paintedtime) * shm->channels;
172	out_mask = shm->samples - 1;
173	out_idx = paintedtime * shm->channels & out_mask;
174	step = 3 - shm->channels;
175	snd_vol = (int)(volume.value*256);
176
177#ifdef _WIN32
178	if (pDSBuf)
179	{
180		reps = 0;
181
182		while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize,
183									   &pbuf2,&dwSize2, 0)) != DS_OK)
184		{
185			if (hresult != DSERR_BUFFERLOST)
186			{
187				Con_Printf ("S_TransferPaintBuffer: DS::Lock Sound Buffer Failed\n");
188				S_Shutdown ();
189				S_Startup ();
190				return;
191			}
192
193			if (++reps > 10000)
194			{
195				Con_Printf ("S_TransferPaintBuffer: DS: couldn't restore buffer\n");
196				S_Shutdown ();
197				S_Startup ();
198				return;
199			}
200		}
201	}
202	else
203#endif
204	{
205		pbuf = (DWORD *)shm->buffer;
206	}
207
208	if (shm->samplebits == 16)
209	{
210		short *out = (short *) pbuf;
211		while (count--)
212		{
213			val = (*p * snd_vol) >> 8;
214			p+= step;
215			if (val > 0x7fff)
216				val = 0x7fff;
217			else if (val < (short)0x8000)
218				val = (short)0x8000;
219			out[out_idx] = val;
220			out_idx = (out_idx + 1) & out_mask;
221		}
222	}
223	else if (shm->samplebits == 8)
224	{
225		unsigned char *out = (unsigned char *) pbuf;
226		while (count--)
227		{
228			val = (*p * snd_vol) >> 8;
229			p+= step;
230			if (val > 0x7fff)
231				val = 0x7fff;
232			else if (val < (short)0x8000)
233				val = (short)0x8000;
234			out[out_idx] = (val>>8) + 128;
235			out_idx = (out_idx + 1) & out_mask;
236		}
237	}
238
239#ifdef _WIN32
240	if (pDSBuf) {
241		DWORD dwNewpos, dwWrite;
242		int il = paintedtime;
243		int ir = endtime - paintedtime;
244
245		ir += il;
246
247		pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0);
248
249		pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwNewpos, &dwWrite);
250
251//		if ((dwNewpos >= il) && (dwNewpos <= ir))
252//			Con_Printf("%d-%d p %d c\n", il, ir, dwNewpos);
253	}
254#endif
255}
256
257
258/*
259===============================================================================
260
261CHANNEL MIXING
262
263===============================================================================
264*/
265
266void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime);
267void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime);
268
269void S_PaintChannels(int endtime)
270{
271	int 	i;
272	int 	end;
273	channel_t *ch;
274	sfxcache_t	*sc;
275	int		ltime, count;
276
277	while (paintedtime < endtime)
278	{
279	// if paintbuffer is smaller than DMA buffer
280		end = endtime;
281		if (endtime - paintedtime > PAINTBUFFER_SIZE)
282			end = paintedtime + PAINTBUFFER_SIZE;
283
284	// clear the paint buffer
285		Q_memset(paintbuffer.paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t));
286
287	// paint in the channels.
288		ch = channels;
289		for (i=0; i<total_channels ; i++, ch++)
290		{
291			if (!ch->sfx)
292				continue;
293			if (!ch->leftvol && !ch->rightvol)
294				continue;
295			sc = S_LoadSound (ch->sfx);
296			if (!sc)
297				continue;
298
299			ltime = paintedtime;
300
301			while (ltime < end)
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					{	// channel just stopped
328						ch->sfx = NULL;
329						break;
330					}
331				}
332			}
333
334		}
335
336	// transfer out according to DMA format
337		S_TransferPaintBuffer(end);
338		paintedtime = end;
339	}
340}
341
342void SND_InitScaletable (void)
343{
344	int		i, j;
345
346	for (i=0 ; i<32 ; i++)
347		for (j=0 ; j<256 ; j++)
348			snd_scaletable[i][j] = ((signed char)j) * i * 8;
349}
350
351
352#if	!id386
353
354void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count)
355{
356	int 	data;
357	int		*lscale, *rscale;
358	unsigned char *sfx;
359	int		i;
360
361	if (ch->leftvol > 255)
362		ch->leftvol = 255;
363	if (ch->rightvol > 255)
364		ch->rightvol = 255;
365
366	lscale = snd_scaletable[ch->leftvol >> 3];
367	rscale = snd_scaletable[ch->rightvol >> 3];
368	sfx = sc->data.uc + ch->pos;
369
370	for (i=0 ; i<count ; i++)
371	{
372		data = sfx[i];
373		paintbuffer.paintbuffer[i].left += lscale[data];
374		paintbuffer.paintbuffer[i].right += rscale[data];
375	}
376
377	ch->pos += count;
378}
379
380#endif	// !id386
381
382
383void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count)
384{
385	int data;
386	int left, right;
387	int leftvol, rightvol;
388	signed short *sfx;
389	int	i;
390
391	leftvol = ch->leftvol;
392	rightvol = ch->rightvol;
393	sfx = sc->data.ss + ch->pos;
394
395	for (i=0 ; i<count ; i++)
396	{
397		data = sfx[i];
398		left = (data * leftvol) >> 8;
399		right = (data * rightvol) >> 8;
400		paintbuffer.paintbuffer[i].left += left;
401		paintbuffer.paintbuffer[i].right += right;
402	}
403
404	ch->pos += count;
405}
406
407