1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 Copyright (C) 2000-2006 Tim Angus
5 
6 This file is part of Tremulous.
7 
8 Tremulous is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12 
13 Tremulous is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with Tremulous; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 ===========================================================================
22 */
23 
24 /*****************************************************************************
25  * name:		snd_mem.c
26  *
27  * desc:		sound caching
28  *
29  * $Archive: /MissionPack/code/client/snd_mem.c $
30  *
31  *****************************************************************************/
32 
33 #include "snd_local.h"
34 #include "snd_codec.h"
35 
36 #define DEF_COMSOUNDMEGS "8"
37 
38 /*
39 ===============================================================================
40 
41 memory management
42 
43 ===============================================================================
44 */
45 
46 static	sndBuffer	*buffer = NULL;
47 static	sndBuffer	*freelist = NULL;
48 static	int inUse = 0;
49 static	int totalInUse = 0;
50 
51 short *sfxScratchBuffer = NULL;
52 sfx_t *sfxScratchPointer = NULL;
53 int	   sfxScratchIndex = 0;
54 
SND_free(sndBuffer * v)55 void	SND_free(sndBuffer *v) {
56 	*(sndBuffer **)v = freelist;
57 	freelist = (sndBuffer*)v;
58 	inUse += sizeof(sndBuffer);
59 }
60 
SND_malloc(void)61 sndBuffer*	SND_malloc(void) {
62 	sndBuffer *v;
63 redo:
64 	if (freelist == NULL) {
65 		S_FreeOldestSound();
66 		goto redo;
67 	}
68 
69 	inUse -= sizeof(sndBuffer);
70 	totalInUse += sizeof(sndBuffer);
71 
72 	v = freelist;
73 	freelist = *(sndBuffer **)freelist;
74 	v->next = NULL;
75 	return v;
76 }
77 
SND_setup(void)78 void SND_setup(void) {
79 	sndBuffer *p, *q;
80 	cvar_t	*cv;
81 	int scs;
82 
83 	cv = Cvar_Get( "com_soundMegs", DEF_COMSOUNDMEGS, CVAR_LATCH | CVAR_ARCHIVE );
84 
85 	scs = (cv->integer*1536);
86 
87 	buffer = malloc(scs*sizeof(sndBuffer) );
88 	// allocate the stack based hunk allocator
89 	sfxScratchBuffer = malloc(SND_CHUNK_SIZE * sizeof(short) * 4);	//Hunk_Alloc(SND_CHUNK_SIZE * sizeof(short) * 4);
90 	sfxScratchPointer = NULL;
91 
92 	inUse = scs*sizeof(sndBuffer);
93 	p = buffer;;
94 	q = p + scs;
95 	while (--q > p)
96 		*(sndBuffer **)q = q-1;
97 
98 	*(sndBuffer **)q = NULL;
99 	freelist = p + scs - 1;
100 
101 	Com_Printf("Sound memory manager started\n");
102 }
103 
104 /*
105 ================
106 ResampleSfx
107 
108 resample / decimate to the current source rate
109 ================
110 */
ResampleSfx(sfx_t * sfx,int inrate,int inwidth,byte * data,qboolean compressed)111 static void ResampleSfx( sfx_t *sfx, int inrate, int inwidth, byte *data, qboolean compressed ) {
112 	int		outcount;
113 	int		srcsample;
114 	float	stepscale;
115 	int		i;
116 	int		sample, samplefrac, fracstep;
117 	int			part;
118 	sndBuffer	*chunk;
119 
120 	stepscale = (float)inrate / dma.speed;	// this is usually 0.5, 1, or 2
121 
122 	outcount = sfx->soundLength / stepscale;
123 	sfx->soundLength = outcount;
124 
125 	samplefrac = 0;
126 	fracstep = stepscale * 256;
127 	chunk = sfx->soundData;
128 
129 	for (i=0 ; i<outcount ; i++)
130 	{
131 		srcsample = samplefrac >> 8;
132 		samplefrac += fracstep;
133 		if( inwidth == 2 ) {
134 			sample = ( ((short *)data)[srcsample] );
135 		} else {
136 			sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
137 		}
138 		part  = (i&(SND_CHUNK_SIZE-1));
139 		if (part == 0) {
140 			sndBuffer	*newchunk;
141 			newchunk = SND_malloc();
142 			if (chunk == NULL) {
143 				sfx->soundData = newchunk;
144 			} else {
145 				chunk->next = newchunk;
146 			}
147 			chunk = newchunk;
148 		}
149 
150 		chunk->sndChunk[part] = sample;
151 	}
152 }
153 
154 /*
155 ================
156 ResampleSfx
157 
158 resample / decimate to the current source rate
159 ================
160 */
ResampleSfxRaw(short * sfx,int inrate,int inwidth,int samples,byte * data)161 static int ResampleSfxRaw( short *sfx, int inrate, int inwidth, int samples, byte *data ) {
162 	int			outcount;
163 	int			srcsample;
164 	float		stepscale;
165 	int			i;
166 	int			sample, samplefrac, fracstep;
167 
168 	stepscale = (float)inrate / dma.speed;	// this is usually 0.5, 1, or 2
169 
170 	outcount = samples / stepscale;
171 
172 	samplefrac = 0;
173 	fracstep = stepscale * 256;
174 
175 	for (i=0 ; i<outcount ; i++)
176 	{
177 		srcsample = samplefrac >> 8;
178 		samplefrac += fracstep;
179 		if( inwidth == 2 ) {
180 			sample = LittleShort ( ((short *)data)[srcsample] );
181 		} else {
182 			sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
183 		}
184 		sfx[i] = sample;
185 	}
186 	return outcount;
187 }
188 
189 //=============================================================================
190 
191 /*
192 ==============
193 S_LoadSound
194 
195 The filename may be different than sfx->name in the case
196 of a forced fallback of a player specific sound
197 ==============
198 */
S_LoadSound(sfx_t * sfx)199 qboolean S_LoadSound( sfx_t *sfx )
200 {
201 	byte	*data;
202 	short	*samples;
203 	snd_info_t	info;
204 //	int		size;
205 
206 	// player specific sounds are never directly loaded
207 	if ( sfx->soundName[0] == '*') {
208 		return qfalse;
209 	}
210 
211 	// load it in
212 	data = S_CodecLoad(sfx->soundName, &info);
213 	if(!data)
214 		return qfalse;
215 
216 	if ( info.width == 1 ) {
217 		Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is a 8 bit wav file\n", sfx->soundName);
218 	}
219 
220 	if ( info.rate != 22050 ) {
221 		Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is not a 22kHz wav file\n", sfx->soundName);
222 	}
223 
224 	samples = Hunk_AllocateTempMemory(info.samples * sizeof(short) * 2);
225 
226 	sfx->lastTimeUsed = Com_Milliseconds()+1;
227 
228 	// each of these compression schemes works just fine
229 	// but the 16bit quality is much nicer and with a local
230 	// install assured we can rely upon the sound memory
231 	// manager to do the right thing for us and page
232 	// sound in as needed
233 
234 	if( sfx->soundCompressed == qtrue) {
235 		sfx->soundCompressionMethod = 1;
236 		sfx->soundData = NULL;
237 		sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, data + info.dataofs );
238 		S_AdpcmEncodeSound(sfx, samples);
239 #if 0
240 	} else if (info.samples>(SND_CHUNK_SIZE*16) && info.width >1) {
241 		sfx->soundCompressionMethod = 3;
242 		sfx->soundData = NULL;
243 		sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
244 		encodeMuLaw( sfx, samples);
245 	} else if (info.samples>(SND_CHUNK_SIZE*6400) && info.width >1) {
246 		sfx->soundCompressionMethod = 2;
247 		sfx->soundData = NULL;
248 		sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
249 		encodeWavelet( sfx, samples);
250 #endif
251 	} else {
252 		sfx->soundCompressionMethod = 0;
253 		sfx->soundLength = info.samples;
254 		sfx->soundData = NULL;
255 		ResampleSfx( sfx, info.rate, info.width, data + info.dataofs, qfalse );
256 	}
257 
258 	Hunk_FreeTempMemory(samples);
259 	Z_Free(data);
260 
261 	return qtrue;
262 }
263 
S_DisplayFreeMemory(void)264 void S_DisplayFreeMemory(void) {
265 	Com_Printf("%d bytes free sound buffer memory, %d total used\n", inUse, totalInUse);
266 }
267