Modified ResampleSfx for a small speed gain
[xonotic/darkplaces.git] / snd_mem.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
21
22 #include "quakedef.h"
23
24 #include "snd_main.h"
25 #include "snd_ogg.h"
26 #include "snd_wav.h"
27
28
29 /*
30 ================
31 ResampleSfx
32 ================
33 */
34 size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_format_t* in_format, unsigned char *out_data, const char* sfxname)
35 {
36         size_t srclength, outcount;
37
38         srclength = in_length * in_format->channels;
39         outcount = (double)in_length * shm->format.speed / in_format->speed;
40
41         //Con_DPrintf("ResampleSfx(%s): %d samples @ %dHz -> %d samples @ %dHz\n",
42         //                      sfxname, in_length, in_format->speed, outcount, shm->format.speed);
43
44         // Trivial case (direct transfer)
45         if (in_format->speed == shm->format.speed)
46         {
47                 if (in_format->width == 1)
48                 {
49                         size_t i;
50
51                         for (i = 0; i < srclength; i++)
52                                 ((signed char*)out_data)[i] = in_data[i] - 128;
53                 }
54                 else  // if (in_format->width == 2)
55                         memcpy (out_data, in_data, srclength * in_format->width);
56         }
57
58         // General case (linear interpolation with a fixed-point fractional
59         // step, 18-bit integer part and 14-bit fractional part)
60         // Can handle up to 2^18 (262144) samples per second (> 96KHz stereo)
61 #       define FRACTIONAL_BITS 14
62 #       define FRACTIONAL_MASK ((1 << FRACTIONAL_BITS) - 1)
63 #       define INTEGER_BITS (sizeof(samplefrac)*8 - FRACTIONAL_BITS)
64         else
65         {
66                 const unsigned int fracstep = (double)in_format->speed / shm->format.speed * (1 << FRACTIONAL_BITS);
67                 size_t remain_in = srclength, total_out = 0;
68                 unsigned int samplefrac;
69                 const unsigned char *in_ptr = in_data;
70                 unsigned char *out_ptr = out_data;
71
72                 // Check that we can handle one second of that sound
73                 if (in_format->speed * in_format->channels > (1 << INTEGER_BITS))
74                 {
75                         Con_Printf ("ResampleSfx: sound quality too high for resampling (%uHz, %u channel(s))\n",
76                                            in_format->speed, in_format->channels);
77                         return 0;
78                 }
79
80                 // We work 1 sec at a time to make sure we don't accumulate any
81                 // significant error when adding "fracstep" over several seconds, and
82                 // also to be able to handle very long sounds.
83                 while (total_out < outcount)
84                 {
85                         size_t tmpcount, i, j;
86                         unsigned int interpolation_limit, srcsample;
87
88                         samplefrac = 0;
89
90                         // If more than 1 sec of sound remains to be converted
91                         if (outcount - total_out > shm->format.speed)
92                         {
93                                 tmpcount = shm->format.speed;
94                                 interpolation_limit = tmpcount;  // all samples can be interpolated
95                         }
96                         else
97                         {
98                                 tmpcount = outcount - total_out;
99                                 interpolation_limit = ceil((double)(((remain_in / in_format->channels) - 1) << FRACTIONAL_BITS) / fracstep);
100                                 if (interpolation_limit > tmpcount)
101                                         interpolation_limit = tmpcount;
102                         }
103
104                         // 16 bit samples
105                         if (in_format->width == 2)
106                         {
107                                 const short* in_ptr_short;
108
109                                 // Interpolated part
110                                 for (i = 0; i < interpolation_limit; i++)
111                                 {
112                                         srcsample = (samplefrac >> FRACTIONAL_BITS) * in_format->channels;
113                                         in_ptr_short = &((const short*)in_ptr)[srcsample];
114
115                                         for (j = 0; j < in_format->channels; j++)
116                                         {
117                                                 int a, b;
118
119                                                 a = *in_ptr_short;
120                                                 b = *(in_ptr_short + in_format->channels);
121                                                 *((short*)out_ptr) = (((b - a) * (samplefrac & FRACTIONAL_MASK)) >> FRACTIONAL_BITS) + a;
122
123                                                 in_ptr_short++;
124                                                 out_ptr += sizeof (short);
125                                         }
126
127                                         samplefrac += fracstep;
128                                 }
129
130                                 // Non-interpolated part
131                                 for (/* nothing */; i < tmpcount; i++)
132                                 {
133                                         srcsample = (samplefrac >> FRACTIONAL_BITS) * in_format->channels;
134                                         in_ptr_short = &((const short*)in_ptr)[srcsample];
135
136                                         for (j = 0; j < in_format->channels; j++)
137                                         {
138                                                 *((short*)out_ptr) = *in_ptr_short;
139
140                                                 in_ptr_short++;
141                                                 out_ptr += sizeof (short);
142                                         }
143
144                                         samplefrac += fracstep;
145                                 }
146                         }
147                         // 8 bit samples
148                         else  // if (in_format->width == 1)
149                         {
150                                 const unsigned char* in_ptr_byte;
151
152                                 // Convert up to 1 sec of sound
153                                 for (i = 0; i < interpolation_limit; i++)
154                                 {
155                                         srcsample = (samplefrac >> FRACTIONAL_BITS) * in_format->channels;
156                                         in_ptr_byte = &((const unsigned char*)in_ptr)[srcsample];
157
158                                         for (j = 0; j < in_format->channels; j++)
159                                         {
160                                                 int a, b;
161
162                                                 a = *in_ptr_byte - 128;
163                                                 b = *(in_ptr_byte + in_format->channels) - 128;
164                                                 *((signed char*)out_ptr) = (((b - a) * (samplefrac & FRACTIONAL_MASK)) >> FRACTIONAL_BITS) + a;
165
166                                                 in_ptr_byte++;
167                                                 out_ptr += sizeof (signed char);
168                                         }
169
170                                         samplefrac += fracstep;
171                                 }
172
173                                 // Non-interpolated part
174                                 for (/* nothing */; i < tmpcount; i++)
175                                 {
176                                         srcsample = (samplefrac >> FRACTIONAL_BITS) * in_format->channels;
177                                         in_ptr_byte = &((const unsigned char*)in_ptr)[srcsample];
178
179                                         for (j = 0; j < in_format->channels; j++)
180                                         {
181                                                 *((signed char*)out_ptr) = *in_ptr_byte - 128;
182
183                                                 in_ptr_byte++;
184                                                 out_ptr += sizeof (signed char);
185                                         }
186
187                                         samplefrac += fracstep;
188                                 }
189                         }
190
191                         // Update the counters and the buffer position
192                         remain_in -= in_format->speed * in_format->channels;
193                         in_ptr += in_format->speed * in_format->channels * in_format->width;
194                         total_out += tmpcount;
195                 }
196         }
197
198         return outcount;
199 }
200
201 //=============================================================================
202
203 /*
204 ==============
205 S_LoadSound
206 ==============
207 */
208 qboolean S_LoadSound (sfx_t *s, qboolean complain)
209 {
210         char namebuffer[MAX_QPATH + 16];
211         size_t len;
212
213         if (!shm || !shm->format.speed)
214                 return false;
215
216         // If we weren't able to load it previously, no need to retry
217         if (s->flags & SFXFLAG_FILEMISSING)
218                 return false;
219
220         // See if in memory
221         if (s->fetcher != NULL)
222         {
223                 if (s->format.speed != shm->format.speed)
224                         Con_Printf ("S_LoadSound: sound %s hasn't been resampled (%uHz instead of %uHz)\n", s->name);
225                 return true;
226         }
227
228         // LordHavoc: if the sound filename does not begin with sound/, try adding it
229         if (strncasecmp(s->name, "sound/", 6))
230         {
231                 len = dpsnprintf (namebuffer, sizeof(namebuffer), "sound/%s", s->name);
232                 if (len < 0)
233                 {
234                         // name too long
235                         Con_Printf("S_LoadSound: name \"%s\" is too long\n", s->name);
236                         return false;
237                 }
238                 if (S_LoadWavFile (namebuffer, s))
239                         return true;
240                 if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".wav"))
241                         strcpy (namebuffer + len - 3, "ogg");
242                 if (OGG_LoadVorbisFile (namebuffer, s))
243                         return true;
244         }
245
246         // LordHavoc: then try without the added sound/ as wav and ogg
247         len = dpsnprintf (namebuffer, sizeof(namebuffer), "%s", s->name);
248         if (len < 0)
249         {
250                 // name too long
251                 Con_Printf("S_LoadSound: name \"%s\" is too long\n", s->name);
252                 return false;
253         }
254         if (S_LoadWavFile (namebuffer, s))
255                 return true;
256         if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".wav"))
257                 strcpy (namebuffer + len - 3, "ogg");
258         if (OGG_LoadVorbisFile (namebuffer, s))
259                 return true;
260
261         // Can't load the sound!
262         s->flags |= SFXFLAG_FILEMISSING;
263         if (complain)
264                 Con_Printf("S_LoadSound: Couldn't load \"%s\"\n", s->name);
265         return false;
266 }