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