1 /* Copyright (C) 2007 Jean-Marc Valin
4 Arbitrary resampling code
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are
10 1. Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
17 3. The name of the author may not be used to endorse or promote products
18 derived from this software without specific prior written permission.
20 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
24 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 POSSIBILITY OF SUCH DAMAGE.
34 The design goals of this code are:
36 - SIMD-friendly algorithm
37 - Low memory requirement
38 - Good *perceptual* quality (and not best SNR)
40 The code is working, but it's in a very early stage, so it may have
41 artifacts, noise or subliminal messages from satan. Also, the API
42 isn't stable and I can actually promise that I *will* change the API
43 some time in the future.
46 - Variable calculation resolution depending on quality setting
47 - Single vs double in float mode
48 - 16-bit vs 32-bit (sinc only) in fixed-point mode
49 - Make sure the filter update works even when changing params
50 after only a few samples procesed
59 static void *speex_alloc (int size
) {return calloc(size
,1);}
60 static void *speex_realloc (void *ptr
, int size
) {return realloc(ptr
, size
);}
61 static void speex_free (void *ptr
) {free(ptr
);}
62 #include "speex_resampler.h"
64 #else /* OUTSIDE_SPEEX */
66 #include "speex/speex_resampler.h"
68 #endif /* OUTSIDE_SPEEX */
73 #define M_PI 3.14159263
77 #define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x)))
79 #define WORD2INT(x) ((x) < -32767.5f ? -32768 : ((x) > 32766.5f ? 32767 : floor(.5+(x))))
82 /*#define float double*/
83 #define FILTER_SIZE 64
86 #define IMAX(a,b) ((a) > (b) ? (a) : (b))
87 #define IMIN(a,b) ((a) < (b) ? (a) : (b))
93 typedef int (*resampler_basic_func
)(SpeexResamplerState
*, spx_uint32_t
, const spx_word16_t
*, spx_uint32_t
*, spx_word16_t
*, spx_uint32_t
*);
95 struct SpeexResamplerState_
{
97 spx_uint32_t out_rate
;
98 spx_uint32_t num_rate
;
99 spx_uint32_t den_rate
;
102 spx_uint32_t nb_channels
;
103 spx_uint32_t filt_len
;
104 spx_uint32_t mem_alloc_size
;
108 spx_uint32_t oversample
;
112 /* These are per-channel */
113 spx_int32_t
*last_sample
;
114 spx_uint32_t
*samp_frac_num
;
115 spx_uint32_t
*magic_samples
;
118 spx_word16_t
*sinc_table
;
119 spx_uint32_t sinc_table_length
;
120 resampler_basic_func resampler_ptr
;
126 static double kaiser12_table
[68] = {
127 0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076,
128 0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014,
129 0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601,
130 0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014,
131 0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490,
132 0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546,
133 0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178,
134 0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947,
135 0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058,
136 0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438,
137 0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734,
138 0.00001000, 0.00000000};
140 static double kaiser12_table[36] = {
141 0.99440475, 1.00000000, 0.99440475, 0.97779076, 0.95066529, 0.91384741,
142 0.86843014, 0.81573067, 0.75723148, 0.69451601, 0.62920216, 0.56287762,
143 0.49704014, 0.43304576, 0.37206735, 0.31506490, 0.26276832, 0.21567274,
144 0.17404546, 0.13794294, 0.10723616, 0.08164178, 0.06075685, 0.04409466,
145 0.03111947, 0.02127838, 0.01402878, 0.00886058, 0.00531256, 0.00298291,
146 0.00153438, 0.00069463, 0.00025272, 0.0000527734, 0.00000500, 0.00000000};
148 static double kaiser10_table
[36] = {
149 0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446,
150 0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347,
151 0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962,
152 0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451,
153 0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739,
154 0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000};
156 static double kaiser8_table
[36] = {
157 0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200,
158 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126,
159 0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272,
160 0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758,
161 0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490,
162 0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000};
164 static double kaiser6_table
[36] = {
165 0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003,
166 0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565,
167 0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561,
168 0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058,
169 0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600,
170 0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000};
177 static struct FuncDef _KAISER12
= {kaiser12_table
, 64};
178 #define KAISER12 (&_KAISER12)
179 /*static struct FuncDef _KAISER12 = {kaiser12_table, 32};
180 #define KAISER12 (&_KAISER12)*/
181 static struct FuncDef _KAISER10
= {kaiser10_table
, 32};
182 #define KAISER10 (&_KAISER10)
183 static struct FuncDef _KAISER8
= {kaiser8_table
, 32};
184 #define KAISER8 (&_KAISER8)
185 static struct FuncDef _KAISER6
= {kaiser6_table
, 32};
186 #define KAISER6 (&_KAISER6)
188 struct QualityMapping
{
191 float downsample_bandwidth
;
192 float upsample_bandwidth
;
193 struct FuncDef
*window_func
;
197 /* This table maps conversion quality to internal parameters. There are two
198 reasons that explain why the up-sampling bandwidth is larger than the
199 down-sampling bandwidth:
200 1) When up-sampling, we can assume that the spectrum is already attenuated
201 close to the Nyquist rate (from an A/D or a previous resampling filter)
202 2) Any aliasing that occurs very close to the Nyquist rate will be masked
203 by the sinusoids/noise just below the Nyquist rate (guaranteed only for
206 static const struct QualityMapping quality_map
[11] = {
207 { 8, 4, 0.830f
, 0.860f
, KAISER6
}, /* Q0 */
208 { 16, 4, 0.850f
, 0.880f
, KAISER6
}, /* Q1 */
209 { 32, 4, 0.882f
, 0.910f
, KAISER6
}, /* Q2 */ /* 82.3% cutoff ( ~60 dB stop) 6 */
210 { 48, 8, 0.895f
, 0.917f
, KAISER8
}, /* Q3 */ /* 84.9% cutoff ( ~80 dB stop) 8 */
211 { 64, 8, 0.921f
, 0.940f
, KAISER8
}, /* Q4 */ /* 88.7% cutoff ( ~80 dB stop) 8 */
212 { 80, 16, 0.922f
, 0.940f
, KAISER10
}, /* Q5 */ /* 89.1% cutoff (~100 dB stop) 10 */
213 { 96, 16, 0.940f
, 0.945f
, KAISER10
}, /* Q6 */ /* 91.5% cutoff (~100 dB stop) 10 */
214 {128, 16, 0.950f
, 0.950f
, KAISER10
}, /* Q7 */ /* 93.1% cutoff (~100 dB stop) 10 */
215 {160, 16, 0.960f
, 0.960f
, KAISER10
}, /* Q8 */ /* 94.5% cutoff (~100 dB stop) 10 */
216 {192, 32, 0.968f
, 0.968f
, KAISER12
}, /* Q9 */ /* 95.5% cutoff (~100 dB stop) 10 */
217 {256, 32, 0.975f
, 0.975f
, KAISER12
}, /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */
219 /*8,24,40,56,80,104,128,160,200,256,320*/
220 static double compute_func(float x
, struct FuncDef
*func
)
225 y
= x
*func
->oversample
;
228 /* CSE with handle the repeated powers */
229 interp
[3] = -0.1666666667*frac
+ 0.1666666667*(frac
*frac
*frac
);
230 interp
[2] = frac
+ 0.5*(frac
*frac
) - 0.5*(frac
*frac
*frac
);
231 /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
232 interp
[0] = -0.3333333333*frac
+ 0.5*(frac
*frac
) - 0.1666666667*(frac
*frac
*frac
);
233 /* Just to make sure we don't have rounding problems */
234 interp
[1] = 1.f
-interp
[3]-interp
[2]-interp
[0];
236 /*sum = frac*accum[1] + (1-frac)*accum[2];*/
237 return interp
[0]*func
->table
[ind
] + interp
[1]*func
->table
[ind
+1] + interp
[2]*func
->table
[ind
+2] + interp
[3]*func
->table
[ind
+3];
242 int main(int argc
, char **argv
)
247 printf ("%f\n", compute_func(i
/256., KAISER12
));
254 /* The slow way of computing a sinc for the table. Should improve that some day */
255 static spx_word16_t
sinc(float cutoff
, float x
, int N
, struct FuncDef
*window_func
)
257 /*fprintf (stderr, "%f ", x);*/
258 float xx
= x
* cutoff
;
260 return WORD2INT(32768.*cutoff
);
261 else if (fabs(x
) > .5f
*N
)
263 /*FIXME: Can it really be any slower than this? */
264 return WORD2INT(32768.*cutoff
*sin(M_PI
*xx
)/(M_PI
*xx
) * compute_func(fabs(2.*x
/N
), window_func
));
267 /* The slow way of computing a sinc for the table. Should improve that some day */
268 static spx_word16_t
sinc(float cutoff
, float x
, int N
, struct FuncDef
*window_func
)
270 /*fprintf (stderr, "%f ", x);*/
271 float xx
= x
* cutoff
;
274 else if (fabs(x
) > .5*N
)
276 /*FIXME: Can it really be any slower than this? */
277 return cutoff
*sin(M_PI
*xx
)/(M_PI
*xx
) * compute_func(fabs(2.*x
/N
), window_func
);
282 static void cubic_coef(spx_word16_t x
, spx_word16_t interp
[4])
284 /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation
285 but I know it's MMSE-optimal on a sinc */
287 x2
= MULT16_16_P15(x
, x
);
288 x3
= MULT16_16_P15(x
, x2
);
289 interp
[0] = PSHR32(MULT16_16(QCONST16(-0.16667f
, 15),x
) + MULT16_16(QCONST16(0.16667f
, 15),x3
),15);
290 interp
[1] = EXTRACT16(EXTEND32(x
) + SHR32(SUB32(EXTEND32(x2
),EXTEND32(x3
)),1));
291 interp
[3] = PSHR32(MULT16_16(QCONST16(-0.33333f
, 15),x
) + MULT16_16(QCONST16(.5f
,15),x2
) - MULT16_16(QCONST16(0.16667f
, 15),x3
),15);
292 /* Just to make sure we don't have rounding problems */
293 interp
[2] = Q15_ONE
-interp
[0]-interp
[1]-interp
[3];
298 static void cubic_coef(spx_word16_t frac
, spx_word16_t interp
[4])
300 /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation
301 but I know it's MMSE-optimal on a sinc */
302 interp
[0] = -0.16667f
*frac
+ 0.16667f
*frac
*frac
*frac
;
303 interp
[1] = frac
+ 0.5f
*frac
*frac
- 0.5f
*frac
*frac
*frac
;
304 /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
305 interp
[3] = -0.33333f
*frac
+ 0.5f
*frac
*frac
- 0.16667f
*frac
*frac
*frac
;
306 /* Just to make sure we don't have rounding problems */
307 interp
[2] = 1.-interp
[0]-interp
[1]-interp
[3];
311 static int resampler_basic_direct_single(SpeexResamplerState
*st
, spx_uint32_t channel_index
, const spx_word16_t
*in
, spx_uint32_t
*in_len
, spx_word16_t
*out
, spx_uint32_t
*out_len
)
313 int N
= st
->filt_len
;
316 int last_sample
= st
->last_sample
[channel_index
];
317 spx_uint32_t samp_frac_num
= st
->samp_frac_num
[channel_index
];
318 mem
= st
->mem
+ channel_index
* st
->mem_alloc_size
;
319 while (!(last_sample
>= (spx_int32_t
)*in_len
|| out_sample
>= (spx_int32_t
)*out_len
))
324 /* We already have all the filter coefficients pre-computed in the table */
325 const spx_word16_t
*ptr
;
326 /* Do the memory part */
327 for (j
=0;last_sample
-N
+1+j
< 0;j
++)
329 sum
+= MULT16_16(mem
[last_sample
+j
],st
->sinc_table
[samp_frac_num
*st
->filt_len
+j
]);
332 /* Do the new part */
333 ptr
= in
+st
->in_stride
*(last_sample
-N
+1+j
);
336 sum
+= MULT16_16(*ptr
,st
->sinc_table
[samp_frac_num
*st
->filt_len
+j
]);
337 ptr
+= st
->in_stride
;
340 *out
= PSHR32(sum
,15);
341 out
+= st
->out_stride
;
343 last_sample
+= st
->int_advance
;
344 samp_frac_num
+= st
->frac_advance
;
345 if (samp_frac_num
>= st
->den_rate
)
347 samp_frac_num
-= st
->den_rate
;
351 st
->last_sample
[channel_index
] = last_sample
;
352 st
->samp_frac_num
[channel_index
] = samp_frac_num
;
358 /* This is the same as the previous function, except with a double-precision accumulator */
359 static int resampler_basic_direct_double(SpeexResamplerState
*st
, spx_uint32_t channel_index
, const spx_word16_t
*in
, spx_uint32_t
*in_len
, spx_word16_t
*out
, spx_uint32_t
*out_len
)
361 int N
= st
->filt_len
;
364 int last_sample
= st
->last_sample
[channel_index
];
365 spx_uint32_t samp_frac_num
= st
->samp_frac_num
[channel_index
];
366 mem
= st
->mem
+ channel_index
* st
->mem_alloc_size
;
367 while (!(last_sample
>= (spx_int32_t
)*in_len
|| out_sample
>= (spx_int32_t
)*out_len
))
372 /* We already have all the filter coefficients pre-computed in the table */
373 const spx_word16_t
*ptr
;
374 /* Do the memory part */
375 for (j
=0;last_sample
-N
+1+j
< 0;j
++)
377 sum
+= MULT16_16(mem
[last_sample
+j
],(double)st
->sinc_table
[samp_frac_num
*st
->filt_len
+j
]);
380 /* Do the new part */
381 ptr
= in
+st
->in_stride
*(last_sample
-N
+1+j
);
384 sum
+= MULT16_16(*ptr
,(double)st
->sinc_table
[samp_frac_num
*st
->filt_len
+j
]);
385 ptr
+= st
->in_stride
;
389 out
+= st
->out_stride
;
391 last_sample
+= st
->int_advance
;
392 samp_frac_num
+= st
->frac_advance
;
393 if (samp_frac_num
>= st
->den_rate
)
395 samp_frac_num
-= st
->den_rate
;
399 st
->last_sample
[channel_index
] = last_sample
;
400 st
->samp_frac_num
[channel_index
] = samp_frac_num
;
405 static int resampler_basic_interpolate_single(SpeexResamplerState
*st
, spx_uint32_t channel_index
, const spx_word16_t
*in
, spx_uint32_t
*in_len
, spx_word16_t
*out
, spx_uint32_t
*out_len
)
407 int N
= st
->filt_len
;
410 int last_sample
= st
->last_sample
[channel_index
];
411 spx_uint32_t samp_frac_num
= st
->samp_frac_num
[channel_index
];
412 mem
= st
->mem
+ channel_index
* st
->mem_alloc_size
;
413 while (!(last_sample
>= (spx_int32_t
)*in_len
|| out_sample
>= (spx_int32_t
)*out_len
))
418 /* We need to interpolate the sinc filter */
419 spx_word32_t accum
[4] = {0.f
,0.f
, 0.f
, 0.f
};
420 spx_word16_t interp
[4];
421 const spx_word16_t
*ptr
;
424 offset
= samp_frac_num
*st
->oversample
/st
->den_rate
;
426 frac
= PDIV32(SHL32((samp_frac_num
*st
->oversample
) % st
->den_rate
,15),st
->den_rate
);
428 frac
= ((float)((samp_frac_num
*st
->oversample
) % st
->den_rate
))/st
->den_rate
;
430 /* This code is written like this to make it easy to optimise with SIMD.
431 For most DSPs, it would be best to split the loops in two because most DSPs
432 have only two accumulators */
433 for (j
=0;last_sample
-N
+1+j
< 0;j
++)
435 spx_word16_t curr_mem
= mem
[last_sample
+j
];
436 accum
[0] += MULT16_16(curr_mem
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
-2]);
437 accum
[1] += MULT16_16(curr_mem
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
-1]);
438 accum
[2] += MULT16_16(curr_mem
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
]);
439 accum
[3] += MULT16_16(curr_mem
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
+1]);
441 ptr
= in
+st
->in_stride
*(last_sample
-N
+1+j
);
442 /* Do the new part */
445 spx_word16_t curr_in
= *ptr
;
446 ptr
+= st
->in_stride
;
447 accum
[0] += MULT16_16(curr_in
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
-2]);
448 accum
[1] += MULT16_16(curr_in
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
-1]);
449 accum
[2] += MULT16_16(curr_in
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
]);
450 accum
[3] += MULT16_16(curr_in
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
+1]);
452 cubic_coef(frac
, interp
);
453 sum
= MULT16_32_Q15(interp
[0],accum
[0]) + MULT16_32_Q15(interp
[1],accum
[1]) + MULT16_32_Q15(interp
[2],accum
[2]) + MULT16_32_Q15(interp
[3],accum
[3]);
455 *out
= PSHR32(sum
,15);
456 out
+= st
->out_stride
;
458 last_sample
+= st
->int_advance
;
459 samp_frac_num
+= st
->frac_advance
;
460 if (samp_frac_num
>= st
->den_rate
)
462 samp_frac_num
-= st
->den_rate
;
466 st
->last_sample
[channel_index
] = last_sample
;
467 st
->samp_frac_num
[channel_index
] = samp_frac_num
;
473 /* This is the same as the previous function, except with a double-precision accumulator */
474 static int resampler_basic_interpolate_double(SpeexResamplerState
*st
, spx_uint32_t channel_index
, const spx_word16_t
*in
, spx_uint32_t
*in_len
, spx_word16_t
*out
, spx_uint32_t
*out_len
)
476 int N
= st
->filt_len
;
479 int last_sample
= st
->last_sample
[channel_index
];
480 spx_uint32_t samp_frac_num
= st
->samp_frac_num
[channel_index
];
481 mem
= st
->mem
+ channel_index
* st
->mem_alloc_size
;
482 while (!(last_sample
>= (spx_int32_t
)*in_len
|| out_sample
>= (spx_int32_t
)*out_len
))
487 /* We need to interpolate the sinc filter */
488 double accum
[4] = {0.f
,0.f
, 0.f
, 0.f
};
490 const spx_word16_t
*ptr
;
491 float alpha
= ((float)samp_frac_num
)/st
->den_rate
;
492 int offset
= samp_frac_num
*st
->oversample
/st
->den_rate
;
493 float frac
= alpha
*st
->oversample
- offset
;
494 /* This code is written like this to make it easy to optimise with SIMD.
495 For most DSPs, it would be best to split the loops in two because most DSPs
496 have only two accumulators */
497 for (j
=0;last_sample
-N
+1+j
< 0;j
++)
499 double curr_mem
= mem
[last_sample
+j
];
500 accum
[0] += MULT16_16(curr_mem
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
-2]);
501 accum
[1] += MULT16_16(curr_mem
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
-1]);
502 accum
[2] += MULT16_16(curr_mem
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
]);
503 accum
[3] += MULT16_16(curr_mem
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
+1]);
505 ptr
= in
+st
->in_stride
*(last_sample
-N
+1+j
);
506 /* Do the new part */
509 double curr_in
= *ptr
;
510 ptr
+= st
->in_stride
;
511 accum
[0] += MULT16_16(curr_in
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
-2]);
512 accum
[1] += MULT16_16(curr_in
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
-1]);
513 accum
[2] += MULT16_16(curr_in
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
]);
514 accum
[3] += MULT16_16(curr_in
,st
->sinc_table
[4+(j
+1)*st
->oversample
-offset
+1]);
516 cubic_coef(frac
, interp
);
517 sum
= interp
[0]*accum
[0] + interp
[1]*accum
[1] + interp
[2]*accum
[2] + interp
[3]*accum
[3];
519 *out
= PSHR32(sum
,15);
520 out
+= st
->out_stride
;
522 last_sample
+= st
->int_advance
;
523 samp_frac_num
+= st
->frac_advance
;
524 if (samp_frac_num
>= st
->den_rate
)
526 samp_frac_num
-= st
->den_rate
;
530 st
->last_sample
[channel_index
] = last_sample
;
531 st
->samp_frac_num
[channel_index
] = samp_frac_num
;
536 static void update_filter(SpeexResamplerState
*st
)
538 spx_uint32_t old_length
;
540 old_length
= st
->filt_len
;
541 st
->oversample
= quality_map
[st
->quality
].oversample
;
542 st
->filt_len
= quality_map
[st
->quality
].base_length
;
544 if (st
->num_rate
> st
->den_rate
)
547 st
->cutoff
= quality_map
[st
->quality
].downsample_bandwidth
* st
->den_rate
/ st
->num_rate
;
548 /* FIXME: divide the numerator and denominator by a certain amount if they're too large */
549 st
->filt_len
= st
->filt_len
*st
->num_rate
/ st
->den_rate
;
550 /* Round down to make sure we have a multiple of 4 */
551 st
->filt_len
&= (~0x3);
552 if (2*st
->den_rate
< st
->num_rate
)
553 st
->oversample
>>= 1;
554 if (4*st
->den_rate
< st
->num_rate
)
555 st
->oversample
>>= 1;
556 if (8*st
->den_rate
< st
->num_rate
)
557 st
->oversample
>>= 1;
558 if (16*st
->den_rate
< st
->num_rate
)
559 st
->oversample
>>= 1;
560 if (st
->oversample
< 1)
564 st
->cutoff
= quality_map
[st
->quality
].upsample_bandwidth
;
567 /* Choose the resampling type that requires the least amount of memory */
568 if (st
->den_rate
<= st
->oversample
)
572 st
->sinc_table
= (spx_word16_t
*)speex_alloc(st
->filt_len
*st
->den_rate
*sizeof(spx_word16_t
));
573 else if (st
->sinc_table_length
< st
->filt_len
*st
->den_rate
)
575 st
->sinc_table
= (spx_word16_t
*)speex_realloc(st
->sinc_table
,st
->filt_len
*st
->den_rate
*sizeof(spx_word16_t
));
576 st
->sinc_table_length
= st
->filt_len
*st
->den_rate
;
578 for (i
=0;i
<st
->den_rate
;i
++)
581 for (j
=0;j
<st
->filt_len
;j
++)
583 st
->sinc_table
[i
*st
->filt_len
+j
] = sinc(st
->cutoff
,((j
-(spx_int32_t
)st
->filt_len
/2+1)-((float)i
)/st
->den_rate
), st
->filt_len
, quality_map
[st
->quality
].window_func
);
587 st
->resampler_ptr
= resampler_basic_direct_single
;
590 st
->resampler_ptr
= resampler_basic_direct_double
;
592 st
->resampler_ptr
= resampler_basic_direct_single
;
594 /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/
598 st
->sinc_table
= (spx_word16_t
*)speex_alloc((st
->filt_len
*st
->oversample
+8)*sizeof(spx_word16_t
));
599 else if (st
->sinc_table_length
< st
->filt_len
*st
->oversample
+8)
601 st
->sinc_table
= (spx_word16_t
*)speex_realloc(st
->sinc_table
,(st
->filt_len
*st
->oversample
+8)*sizeof(spx_word16_t
));
602 st
->sinc_table_length
= st
->filt_len
*st
->oversample
+8;
604 for (i
=-4;i
<(spx_int32_t
)(st
->oversample
*st
->filt_len
+4);i
++)
605 st
->sinc_table
[i
+4] = sinc(st
->cutoff
,(i
/(float)st
->oversample
- st
->filt_len
/2), st
->filt_len
, quality_map
[st
->quality
].window_func
);
607 st
->resampler_ptr
= resampler_basic_interpolate_single
;
610 st
->resampler_ptr
= resampler_basic_interpolate_double
;
612 st
->resampler_ptr
= resampler_basic_interpolate_single
;
614 /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/
616 st
->int_advance
= st
->num_rate
/st
->den_rate
;
617 st
->frac_advance
= st
->num_rate
%st
->den_rate
;
620 /* Here's the place where we update the filter memory to take into account
621 the change in filter length. It's probably the messiest part of the code
622 due to handling of lots of corner cases. */
626 st
->mem
= (spx_word16_t
*)speex_alloc(st
->nb_channels
*(st
->filt_len
-1) * sizeof(spx_word16_t
));
627 for (i
=0;i
<st
->nb_channels
*(st
->filt_len
-1);i
++)
629 st
->mem_alloc_size
= st
->filt_len
-1;
630 /*speex_warning("init filter");*/
631 } else if (!st
->started
)
634 st
->mem
= (spx_word16_t
*)speex_realloc(st
->mem
, st
->nb_channels
*(st
->filt_len
-1) * sizeof(spx_word16_t
));
635 for (i
=0;i
<st
->nb_channels
*(st
->filt_len
-1);i
++)
637 st
->mem_alloc_size
= st
->filt_len
-1;
638 /*speex_warning("reinit filter");*/
639 } else if (st
->filt_len
> old_length
)
642 /* Increase the filter length */
643 /*speex_warning("increase filter size");*/
644 int old_alloc_size
= st
->mem_alloc_size
;
645 if (st
->filt_len
-1 > st
->mem_alloc_size
)
647 st
->mem
= (spx_word16_t
*)speex_realloc(st
->mem
, st
->nb_channels
*(st
->filt_len
-1) * sizeof(spx_word16_t
));
648 st
->mem_alloc_size
= st
->filt_len
-1;
650 for (i
=st
->nb_channels
-1;i
>=0;i
--)
653 spx_uint32_t olen
= old_length
;
654 /*if (st->magic_samples[i])*/
656 /* Try and remove the magic samples as if nothing had happened */
658 /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */
659 olen
= old_length
+ 2*st
->magic_samples
[i
];
660 for (j
=old_length
-2+st
->magic_samples
[i
];j
>=0;j
--)
661 st
->mem
[i
*st
->mem_alloc_size
+j
+st
->magic_samples
[i
]] = st
->mem
[i
*old_alloc_size
+j
];
662 for (j
=0;j
<st
->magic_samples
[i
];j
++)
663 st
->mem
[i
*st
->mem_alloc_size
+j
] = 0;
664 st
->magic_samples
[i
] = 0;
666 if (st
->filt_len
> olen
)
668 /* If the new filter length is still bigger than the "augmented" length */
669 /* Copy data going backward */
670 for (j
=0;j
<olen
-1;j
++)
671 st
->mem
[i
*st
->mem_alloc_size
+(st
->filt_len
-2-j
)] = st
->mem
[i
*st
->mem_alloc_size
+(olen
-2-j
)];
672 /* Then put zeros for lack of anything better */
673 for (;j
<st
->filt_len
-1;j
++)
674 st
->mem
[i
*st
->mem_alloc_size
+(st
->filt_len
-2-j
)] = 0;
675 /* Adjust last_sample */
676 st
->last_sample
[i
] += (st
->filt_len
- olen
)/2;
678 /* Put back some of the magic! */
679 st
->magic_samples
[i
] = (olen
- st
->filt_len
)/2;
680 for (j
=0;j
<st
->filt_len
-1+st
->magic_samples
[i
];j
++)
681 st
->mem
[i
*st
->mem_alloc_size
+j
] = st
->mem
[i
*st
->mem_alloc_size
+j
+st
->magic_samples
[i
]];
684 } else if (st
->filt_len
< old_length
)
687 /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic"
688 samples so they can be used directly as input the next time(s) */
689 for (i
=0;i
<st
->nb_channels
;i
++)
692 spx_uint32_t old_magic
= st
->magic_samples
[i
];
693 st
->magic_samples
[i
] = (old_length
- st
->filt_len
)/2;
694 /* We must copy some of the memory that's no longer used */
695 /* Copy data going backward */
696 for (j
=0;j
<st
->filt_len
-1+st
->magic_samples
[i
]+old_magic
;j
++)
697 st
->mem
[i
*st
->mem_alloc_size
+j
] = st
->mem
[i
*st
->mem_alloc_size
+j
+st
->magic_samples
[i
]];
698 st
->magic_samples
[i
] += old_magic
;
704 SpeexResamplerState
*speex_resampler_init(spx_uint32_t nb_channels
, spx_uint32_t in_rate
, spx_uint32_t out_rate
, int quality
, int *err
)
706 return speex_resampler_init_frac(nb_channels
, in_rate
, out_rate
, in_rate
, out_rate
, quality
, err
);
709 SpeexResamplerState
*speex_resampler_init_frac(spx_uint32_t nb_channels
, spx_uint32_t ratio_num
, spx_uint32_t ratio_den
, spx_uint32_t in_rate
, spx_uint32_t out_rate
, int quality
, int *err
)
712 SpeexResamplerState
*st
;
713 if (quality
> 10 || quality
< 0)
716 *err
= RESAMPLER_ERR_INVALID_ARG
;
719 st
= (SpeexResamplerState
*)speex_alloc(sizeof(SpeexResamplerState
));
727 st
->sinc_table_length
= 0;
728 st
->mem_alloc_size
= 0;
731 st
->resampler_ptr
= 0;
734 st
->nb_channels
= nb_channels
;
738 /* Per channel data */
739 st
->last_sample
= (spx_int32_t
*)speex_alloc(nb_channels
*sizeof(int));
740 st
->magic_samples
= (spx_uint32_t
*)speex_alloc(nb_channels
*sizeof(int));
741 st
->samp_frac_num
= (spx_uint32_t
*)speex_alloc(nb_channels
*sizeof(int));
742 for (i
=0;i
<nb_channels
;i
++)
744 st
->last_sample
[i
] = 0;
745 st
->magic_samples
[i
] = 0;
746 st
->samp_frac_num
[i
] = 0;
749 speex_resampler_set_quality(st
, quality
);
750 speex_resampler_set_rate_frac(st
, ratio_num
, ratio_den
, in_rate
, out_rate
);
757 *err
= RESAMPLER_ERR_SUCCESS
;
762 void speex_resampler_destroy(SpeexResamplerState
*st
)
765 speex_free(st
->sinc_table
);
766 speex_free(st
->last_sample
);
767 speex_free(st
->magic_samples
);
768 speex_free(st
->samp_frac_num
);
774 static int speex_resampler_process_native(SpeexResamplerState
*st
, spx_uint32_t channel_index
, const spx_word16_t
*in
, spx_uint32_t
*in_len
, spx_word16_t
*out
, spx_uint32_t
*out_len
)
777 int N
= st
->filt_len
;
780 spx_uint32_t tmp_out_len
= 0;
781 mem
= st
->mem
+ channel_index
* st
->mem_alloc_size
;
784 /* Handle the case where we have samples left from a reduction in filter length */
785 if (st
->magic_samples
[channel_index
])
788 spx_uint32_t tmp_in_len
;
789 spx_uint32_t tmp_magic
;
791 istride_save
= st
->in_stride
;
792 tmp_in_len
= st
->magic_samples
[channel_index
];
793 tmp_out_len
= *out_len
;
794 /* magic_samples needs to be set to zero to avoid infinite recursion */
795 tmp_magic
= st
->magic_samples
[channel_index
];
796 st
->magic_samples
[channel_index
] = 0;
798 speex_resampler_process_native(st
, channel_index
, mem
+N
-1, &tmp_in_len
, out
, &tmp_out_len
);
799 st
->in_stride
= istride_save
;
800 /*speex_warning_int("extra samples:", tmp_out_len);*/
801 /* If we couldn't process all "magic" input samples, save the rest for next time */
802 if (tmp_in_len
< tmp_magic
)
805 st
->magic_samples
[channel_index
] = tmp_magic
-tmp_in_len
;
806 for (i
=0;i
<st
->magic_samples
[channel_index
];i
++)
807 mem
[N
-1+i
]=mem
[N
-1+i
+tmp_in_len
];
809 out
+= tmp_out_len
*st
->out_stride
;
810 *out_len
-= tmp_out_len
;
813 /* Call the right resampler through the function ptr */
814 out_sample
= st
->resampler_ptr(st
, channel_index
, in
, in_len
, out
, out_len
);
816 if (st
->last_sample
[channel_index
] < (spx_int32_t
)*in_len
)
817 *in_len
= st
->last_sample
[channel_index
];
818 *out_len
= out_sample
+tmp_out_len
;
819 st
->last_sample
[channel_index
] -= *in_len
;
821 for (j
=0;j
<N
-1-(spx_int32_t
)*in_len
;j
++)
822 mem
[j
] = mem
[j
+*in_len
];
824 mem
[j
] = in
[st
->in_stride
*(j
+*in_len
-N
+1)];
826 return RESAMPLER_ERR_SUCCESS
;
829 #define FIXED_STACK_ALLOC 1024
832 int speex_resampler_process_float(SpeexResamplerState
*st
, spx_uint32_t channel_index
, const float *in
, spx_uint32_t
*in_len
, float *out
, spx_uint32_t
*out_len
)
835 int istride_save
, ostride_save
;
837 spx_word16_t x
[*in_len
];
838 spx_word16_t y
[*out_len
];
839 /*VARDECL(spx_word16_t *x);
840 VARDECL(spx_word16_t *y);
841 ALLOC(x, *in_len, spx_word16_t);
842 ALLOC(y, *out_len, spx_word16_t);*/
843 istride_save
= st
->in_stride
;
844 ostride_save
= st
->out_stride
;
845 for (i
=0;i
<*in_len
;i
++)
846 x
[i
] = WORD2INT(in
[i
*st
->in_stride
]);
847 st
->in_stride
= st
->out_stride
= 1;
848 speex_resampler_process_native(st
, channel_index
, x
, in_len
, y
, out_len
);
849 st
->in_stride
= istride_save
;
850 st
->out_stride
= ostride_save
;
851 for (i
=0;i
<*out_len
;i
++)
852 out
[i
*st
->out_stride
] = y
[i
];
854 spx_word16_t x
[FIXED_STACK_ALLOC
];
855 spx_word16_t y
[FIXED_STACK_ALLOC
];
856 spx_uint32_t ilen
=*in_len
, olen
=*out_len
;
857 istride_save
= st
->in_stride
;
858 ostride_save
= st
->out_stride
;
861 spx_uint32_t ichunk
, ochunk
;
864 if (ichunk
>FIXED_STACK_ALLOC
)
865 ichunk
=FIXED_STACK_ALLOC
;
866 if (ochunk
>FIXED_STACK_ALLOC
)
867 ochunk
=FIXED_STACK_ALLOC
;
868 for (i
=0;i
<ichunk
;i
++)
869 x
[i
] = WORD2INT(in
[i
*st
->in_stride
]);
870 st
->in_stride
= st
->out_stride
= 1;
871 speex_resampler_process_native(st
, channel_index
, x
, &ichunk
, y
, &ochunk
);
872 st
->in_stride
= istride_save
;
873 st
->out_stride
= ostride_save
;
874 for (i
=0;i
<ochunk
;i
++)
875 out
[i
*st
->out_stride
] = y
[i
];
884 return RESAMPLER_ERR_SUCCESS
;
886 int speex_resampler_process_int(SpeexResamplerState
*st
, spx_uint32_t channel_index
, const spx_int16_t
*in
, spx_uint32_t
*in_len
, spx_int16_t
*out
, spx_uint32_t
*out_len
)
888 return speex_resampler_process_native(st
, channel_index
, in
, in_len
, out
, out_len
);
891 int speex_resampler_process_float(SpeexResamplerState
*st
, spx_uint32_t channel_index
, const float *in
, spx_uint32_t
*in_len
, float *out
, spx_uint32_t
*out_len
)
893 return speex_resampler_process_native(st
, channel_index
, in
, in_len
, out
, out_len
);
895 int speex_resampler_process_int(SpeexResamplerState
*st
, spx_uint32_t channel_index
, const spx_int16_t
*in
, spx_uint32_t
*in_len
, spx_int16_t
*out
, spx_uint32_t
*out_len
)
898 int istride_save
, ostride_save
;
900 spx_word16_t x
[*in_len
];
901 spx_word16_t y
[*out_len
];
902 /*VARDECL(spx_word16_t *x);
903 VARDECL(spx_word16_t *y);
904 ALLOC(x, *in_len, spx_word16_t);
905 ALLOC(y, *out_len, spx_word16_t);*/
906 istride_save
= st
->in_stride
;
907 ostride_save
= st
->out_stride
;
908 for (i
=0;i
<*in_len
;i
++)
909 x
[i
] = in
[i
*st
->in_stride
];
910 st
->in_stride
= st
->out_stride
= 1;
911 speex_resampler_process_native(st
, channel_index
, x
, in_len
, y
, out_len
);
912 st
->in_stride
= istride_save
;
913 st
->out_stride
= ostride_save
;
914 for (i
=0;i
<*out_len
;i
++)
915 out
[i
*st
->out_stride
] = WORD2INT(y
[i
]);
917 spx_word16_t x
[FIXED_STACK_ALLOC
];
918 spx_word16_t y
[FIXED_STACK_ALLOC
];
919 spx_uint32_t ilen
=*in_len
, olen
=*out_len
;
920 istride_save
= st
->in_stride
;
921 ostride_save
= st
->out_stride
;
924 spx_uint32_t ichunk
, ochunk
;
927 if (ichunk
>FIXED_STACK_ALLOC
)
928 ichunk
=FIXED_STACK_ALLOC
;
929 if (ochunk
>FIXED_STACK_ALLOC
)
930 ochunk
=FIXED_STACK_ALLOC
;
931 for (i
=0;i
<ichunk
;i
++)
932 x
[i
] = in
[i
*st
->in_stride
];
933 st
->in_stride
= st
->out_stride
= 1;
934 speex_resampler_process_native(st
, channel_index
, x
, &ichunk
, y
, &ochunk
);
935 st
->in_stride
= istride_save
;
936 st
->out_stride
= ostride_save
;
937 for (i
=0;i
<ochunk
;i
++)
938 out
[i
*st
->out_stride
] = WORD2INT(y
[i
]);
947 return RESAMPLER_ERR_SUCCESS
;
951 int speex_resampler_process_interleaved_float(SpeexResamplerState
*st
, const float *in
, spx_uint32_t
*in_len
, float *out
, spx_uint32_t
*out_len
)
954 int istride_save
, ostride_save
;
955 spx_uint32_t bak_len
= *out_len
;
956 istride_save
= st
->in_stride
;
957 ostride_save
= st
->out_stride
;
958 st
->in_stride
= st
->out_stride
= st
->nb_channels
;
959 for (i
=0;i
<st
->nb_channels
;i
++)
962 speex_resampler_process_float(st
, i
, in
+i
, in_len
, out
+i
, out_len
);
964 st
->in_stride
= istride_save
;
965 st
->out_stride
= ostride_save
;
966 return RESAMPLER_ERR_SUCCESS
;
970 int speex_resampler_process_interleaved_int(SpeexResamplerState
*st
, const spx_int16_t
*in
, spx_uint32_t
*in_len
, spx_int16_t
*out
, spx_uint32_t
*out_len
)
973 int istride_save
, ostride_save
;
974 spx_uint32_t bak_len
= *out_len
;
975 istride_save
= st
->in_stride
;
976 ostride_save
= st
->out_stride
;
977 st
->in_stride
= st
->out_stride
= st
->nb_channels
;
978 for (i
=0;i
<st
->nb_channels
;i
++)
981 speex_resampler_process_int(st
, i
, in
+i
, in_len
, out
+i
, out_len
);
983 st
->in_stride
= istride_save
;
984 st
->out_stride
= ostride_save
;
985 return RESAMPLER_ERR_SUCCESS
;
988 int speex_resampler_set_rate(SpeexResamplerState
*st
, spx_uint32_t in_rate
, spx_uint32_t out_rate
)
990 return speex_resampler_set_rate_frac(st
, in_rate
, out_rate
, in_rate
, out_rate
);
993 void speex_resampler_get_rate(SpeexResamplerState
*st
, spx_uint32_t
*in_rate
, spx_uint32_t
*out_rate
)
995 *in_rate
= st
->in_rate
;
996 *out_rate
= st
->out_rate
;
999 int speex_resampler_set_rate_frac(SpeexResamplerState
*st
, spx_uint32_t ratio_num
, spx_uint32_t ratio_den
, spx_uint32_t in_rate
, spx_uint32_t out_rate
)
1002 spx_uint32_t old_den
;
1004 if (st
->in_rate
== in_rate
&& st
->out_rate
== out_rate
&& st
->num_rate
== ratio_num
&& st
->den_rate
== ratio_den
)
1005 return RESAMPLER_ERR_SUCCESS
;
1007 old_den
= st
->den_rate
;
1008 st
->in_rate
= in_rate
;
1009 st
->out_rate
= out_rate
;
1010 st
->num_rate
= ratio_num
;
1011 st
->den_rate
= ratio_den
;
1012 /* FIXME: This is terribly inefficient, but who cares (at least for now)? */
1013 for (fact
=2;fact
<=IMIN(st
->num_rate
, st
->den_rate
);fact
++)
1015 while ((st
->num_rate
% fact
== 0) && (st
->den_rate
% fact
== 0))
1017 st
->num_rate
/= fact
;
1018 st
->den_rate
/= fact
;
1024 for (i
=0;i
<st
->nb_channels
;i
++)
1026 st
->samp_frac_num
[i
]=st
->samp_frac_num
[i
]*st
->den_rate
/old_den
;
1028 if (st
->samp_frac_num
[i
] >= st
->den_rate
)
1029 st
->samp_frac_num
[i
] = st
->den_rate
-1;
1033 if (st
->initialised
)
1035 return RESAMPLER_ERR_SUCCESS
;
1038 void speex_resampler_get_ratio(SpeexResamplerState
*st
, spx_uint32_t
*ratio_num
, spx_uint32_t
*ratio_den
)
1040 *ratio_num
= st
->num_rate
;
1041 *ratio_den
= st
->den_rate
;
1044 int speex_resampler_set_quality(SpeexResamplerState
*st
, int quality
)
1046 if (quality
> 10 || quality
< 0)
1047 return RESAMPLER_ERR_INVALID_ARG
;
1048 if (st
->quality
== quality
)
1049 return RESAMPLER_ERR_SUCCESS
;
1050 st
->quality
= quality
;
1051 if (st
->initialised
)
1053 return RESAMPLER_ERR_SUCCESS
;
1056 void speex_resampler_get_quality(SpeexResamplerState
*st
, int *quality
)
1058 *quality
= st
->quality
;
1061 void speex_resampler_set_input_stride(SpeexResamplerState
*st
, spx_uint32_t stride
)
1063 st
->in_stride
= stride
;
1066 void speex_resampler_get_input_stride(SpeexResamplerState
*st
, spx_uint32_t
*stride
)
1068 *stride
= st
->in_stride
;
1071 void speex_resampler_set_output_stride(SpeexResamplerState
*st
, spx_uint32_t stride
)
1073 st
->out_stride
= stride
;
1076 void speex_resampler_get_output_stride(SpeexResamplerState
*st
, spx_uint32_t
*stride
)
1078 *stride
= st
->out_stride
;
1081 int speex_resampler_skip_zeros(SpeexResamplerState
*st
)
1084 for (i
=0;i
<st
->nb_channels
;i
++)
1085 st
->last_sample
[i
] = st
->filt_len
/2;
1086 return RESAMPLER_ERR_SUCCESS
;
1089 int speex_resampler_reset_mem(SpeexResamplerState
*st
)
1092 for (i
=0;i
<st
->nb_channels
*(st
->filt_len
-1);i
++)
1094 return RESAMPLER_ERR_SUCCESS
;
1097 const char *speex_resampler_strerror(int err
)
1101 case RESAMPLER_ERR_SUCCESS
:
1103 case RESAMPLER_ERR_ALLOC_FAILED
:
1104 return "Memory allocation failed.";
1105 case RESAMPLER_ERR_BAD_STATE
:
1106 return "Bad resampler state.";
1107 case RESAMPLER_ERR_INVALID_ARG
:
1108 return "Invalid argument.";
1109 case RESAMPLER_ERR_PTR_OVERLAP
:
1110 return "Input and output buffers overlap.";
1112 return "Unknown error. Bad error code or strange version mismatch.";