summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/SDL2/src/audio/SDL_audiocvt.c
blob: 3b47c4cbcc0c10e3f48c480a46ebcd905f863467 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888 } /* Comment */
.highlight .err { color: #A61717; background-color: #E3D2D2 } /* Error */
.highlight .k { color: #080; font-weight: bold } /* Keyword */
.highlight .ch { color: #888 } /* Comment.Hashbang */
.highlight .cm { color: #888 } /* Comment.Multiline */
.highlight .cp { color: #C00; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888 } /* Comment.Single */
.highlight .cs { color: #C00; font-weight: bold; background-color: #FFF0F0 } /* Comment.Special */
.highlight .gd { color: #000; background-color: #FDD } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #A00 } /* Generic.Error */
.highlight .gh { color: #333 } /* Generic.Heading */
.highlight .gi { color: #000; background-color: #DFD } /* Generic.Inserted */
.highlight .go { color: #888 } /* Generic.Output */
.highlight .gp { color: #555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666 } /* Generic.Subheading */
.highlight .gt { color: #A00 } /* Generic.Traceback */
.highlight .kc { color: #080; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #080; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #080; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #080 } /* Keyword.Pseudo */
.highlight .kr { color: #080; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #00D; font-weight: bold } /* Literal.Number */
.highlight .s { color: #D20; background-color: #FFF0F0 } /* Literal.String */
.highlight .na { color: #369 } /* Name.Attribute */
.highlight .nb { color: #038 } /* Name.Builtin */
.highlight .nc { color: #B06; font-weight: bold } /* Name.Class */
.highlight .no { color: #036; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555 } /* Name.Decorator */
.highlight .ne { color: #B06; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #06B; font-weight: bold } /* Name.Function */
.highlight .nl { color: #369; font-style: italic } /* Name.Label */
.highlight .nn { color: #B06; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #369; font-weight: bold } /* Name.Property */
.highlight .nt { color: #B06; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #369 } /* Name.Variable */
.highlight .ow { color: #080 } /* Operator.Word */
.highlight .w { color: #BBB } /* Text.Whitespace */
.highlight .mb { color: #00D; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #00D; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #00D; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #00D; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #00D; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #D20; background-color: #FFF0F0 } /* Literal.String.Affix */
.highlight .sb { color: #D20; background-color: #FFF0F0 } /* Literal.String.Backtick */
.highlight .sc { color: #D20; background-color: #FFF0F0 } /* Literal.String.Char */
.highlight .dl { color: #D20; background-color: #FFF0F0 } /* Literal.String.Delimiter */
.highlight .sd { color: #D20; background-color: #FFF0F0 } /* Literal.String.Doc */
.highlight .s2 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Double */
.highlight .se { color: #04D; background-color: #FFF0F0 } /* Literal.String.Escape */
.highlight .sh { color: #D20; background-color: #FFF0F0 } /* Literal.String.Heredoc */
.highlight .si { color: #33B; background-color: #FFF0F0 } /* Literal.String.Interpol */
.highlight .sx { color: #2B2; background-color: #F0FFF0 } /* Literal.String.Other */
.highlight .sr { color: #080; background-color: #FFF0FF } /* Literal.String.Regex */
.highlight .s1 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Single */
.highlight .ss { color: #A60; background-color: #FFF0F0 } /* Literal.String.Symbol */
.highlight .bp { color: #038 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #06B; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #369 } /* Name.Variable.Class */
.highlight .vg { color: #D70 } /* Name.Variable.Global */
.highlight .vi { color: #33B } /* Name.Variable.Instance */
.highlight .vm { color: #369 } /* Name.Variable.Magic */
.highlight .il { color: #00D; font-weight: bold } /* Literal.Number.Integer.Long */
// Tencent is pleased to support the open source community by making RapidJSON available.
// 
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed 
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
// specific language governing permissions and limitations under the License.

#include "unittest.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/encodedstream.h"
#include "rapidjson/stringbuffer.h"

using namespace rapidjson;

// Verification of encoders/decoders with Hoehrmann's UTF8 decoder

// http://www.unicode.org/Public/UNIDATA/Blocks.txt
static const unsigned kCodepointRanges[] = {
    0x0000,     0x007F,     // Basic Latin
    0x0080,     0x00FF,     // Latin-1 Supplement
    0x0100,     0x017F,     // Latin Extended-A
    0x0180,     0x024F,     // Latin Extended-B
    0x0250,     0x02AF,     // IPA Extensions
    0x02B0,     0x02FF,     // Spacing Modifier Letters
    0x0300,     0x036F,     // Combining Diacritical Marks
    0x0370,     0x03FF,     // Greek and Coptic
    0x0400,     0x04FF,     // Cyrillic
    0x0500,     0x052F,     // Cyrillic Supplement
    0x0530,     0x058F,     // Armenian
    0x0590,     0x05FF,     // Hebrew
    0x0600,     0x06FF,     // Arabic
    0x0700,     0x074F,     // Syriac
    0x0750,     0x077F,     // Arabic Supplement
    0x0780,     0x07BF,     // Thaana
    0x07C0,     0x07FF,     // NKo
    0x0800,     0x083F,     // Samaritan
    0x0840,     0x085F,     // Mandaic
    0x0900,     0x097F,     // Devanagari
    0x0980,     0x09FF,     // Bengali
    0x0A00,     0x0A7F,     // Gurmukhi
    0x0A80,     0x0AFF,     // Gujarati
    0x0B00,     0x0B7F,     // Oriya
    0x0B80,     0x0BFF,     // Tamil
    0x0C00,     0x0C7F,     // Telugu
    0x0C80,     0x0CFF,     // Kannada
    0x0D00,     0x0D7F,     // Malayalam
    0x0D80,     0x0DFF,     // Sinhala
    0x0E00,     0x0E7F,     // Thai
    0x0E80,     0x0EFF,     // Lao
    0x0F00,     0x0FFF,     // Tibetan
    0x1000,     0x109F,     // Myanmar
    0x10A0,     0x10FF,     // Georgian
    0x1100,     0x11FF,     // Hangul Jamo
    0x1200,     0x137F,     // Ethiopic
    0x1380,     0x139F,     // Ethiopic Supplement
    0x13A0,     0x13FF,     // Cherokee
    0x1400,     0x167F,     // Unified Canadian Aboriginal Syllabics
    0x1680,     0x169F,     // Ogham
    0x16A0,     0x16FF,     // Runic
    0x1700,     0x171F,     // Tagalog
    0x1720,     0x173F,     // Hanunoo
    0x1740,     0x175F,     // Buhid
    0x1760,     0x177F,     // Tagbanwa
    0x1780,     0x17FF,     // Khmer
    0x1800,     0x18AF,     // Mongolian
    0x18B0,     0x18FF,     // Unified Canadian Aboriginal Syllabics Extended
    0x1900,     0x194F,     // Limbu
    0x1950,     0x197F,     // Tai Le
    0x1980,     0x19DF,     // New Tai Lue
    0x19E0,     0x19FF,     // Khmer Symbols
    0x1A00,     0x1A1F,     // Buginese
    0x1A20,     0x1AAF,     // Tai Tham
    0x1B00,     0x1B7F,     // Balinese
    0x1B80,     0x1BBF,     // Sundanese
    0x1BC0,     0x1BFF,     // Batak
    0x1C00,     0x1C4F,     // Lepcha
    0x1C50,     0x1C7F,     // Ol Chiki
    0x1CD0,     0x1CFF,     // Vedic Extensions
    0x1D00,     0x1D7F,     // Phonetic Extensions
    0x1D80,     0x1DBF,     // Phonetic Extensions Supplement
    0x1DC0,     0x1DFF,     // Combining Diacritical Marks Supplement
    0x1E00,     0x1EFF,     // Latin Extended Additional
    0x1F00,     0x1FFF,     // Greek Extended
    0x2000,     0x206F,     // General Punctuation
    0x2070,     0x209F,     // Superscripts and Subscripts
    0x20A0,     0x20CF,     // Currency Symbols
    0x20D0,     0x20FF,     // Combining Diacritical Marks for Symbols
    0x2100,     0x214F,     // Letterlike Symbols
    0x2150,     0x218F,     // Number Forms
    0x2190,     0x21FF,     // Arrowspre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888 } /* Comment */
.highlight .err { color: #A61717; background-color: #E3D2D2 } /* Error */
.highlight .k { color: #080; font-weight: bold } /* Keyword */
.highlight .ch { color: #888 } /* Comment.Hashbang */
.highlight .cm { color: #888 } /* Comment.Multiline */
.highlight .cp { color: #C00; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888 } /* Comment.Single */
.highlight .cs { color: #C00; font-weight: bold; background-color: #FFF0F0 } /* Comment.Special */
.highlight .gd { color: #000; background-color: #FDD } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #A00 } /* Generic.Error */
.highlight .gh { color: #333 } /* Generic.Heading */
.highlight .gi { color: #000; background-color: #DFD } /* Generic.Inserted */
.highlight .go { color: #888 } /* Generic.Output */
.highlight .gp { color: #555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666 } /* Generic.Subheading */
.highlight .gt { color: #A00 } /* Generic.Traceback */
.highlight .kc { color: #080; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #080; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #080; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #080 } /* Keyword.Pseudo */
.highlight .kr { color: #080; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #00D; font-weight: bold } /* Literal.Number */
.highlight .s { color: #D20; background-color: #FFF0F0 } /* Literal.String */
.highlight .na { color: #369 } /* Name.Attribute */
.highlight .nb { color: #038 } /* Name.Builtin */
.highlight .nc { color: #B06; font-weight: bold } /* Name.Class */
.highlight .no { color: #036; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555 } /* Name.Decorator */
.highlight .ne { color: #B06; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #06B; font-weight: bold } /* Name.Function */
.highlight .nl { color: #369; font-style: italic } /* Name.Label */
.highlight .nn { color: #B06; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #369; font-weight: bold } /* Name.Property */
.highlight .nt { color: #B06; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #369 } /* Name.Variable */
.highlight .ow { color: #080 } /* Operator.Word */
.highlight .w { color: #BBB } /* Text.Whitespace */
.highlight .mb { color: #00D; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #00D; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #00D; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #00D; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #00D; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #D20; background-color: #FFF0F0 } /* Literal.String.Affix */
.highlight .sb { color: #D20; background-color: #FFF0F0 } /* Literal.String.Backtick */
.highlight .sc { color: #D20; background-color: #FFF0F0 } /* Literal.String.Char */
.highlight .dl { color: #D20; background-color: #FFF0F0 } /* Literal.String.Delimiter */
.highlight .sd { color: #D20; background-color: #FFF0F0 } /* Literal.String.Doc */
.highlight .s2 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Double */
.highlight .se { color: #04D; background-color: #FFF0F0 } /* Literal.String.Escape */
.highlight .sh { color: #D20; background-color: #FFF0F0 } /* Literal.String.Heredoc */
.highlight .si { color: #33B; background-color: #FFF0F0 } /* Literal.String.Interpol */
.highlight .sx { color: #2B2; background-color: #F0FFF0 } /* Literal.String.Other */
.highlight .sr { color: #080; background-color: #FFF0FF } /* Literal.String.Regex */
.highlight .s1 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Single */
.highlight .ss { color: #A60; background-color: #FFF0F0 } /* Literal.String.Symbol */
.highlight .bp { color: #038 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #06B; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #369 } /* Name.Variable.Class */
.highlight .vg { color: #D70 } /* Name.Variable.Global */
.highlight .vi { color: #33B } /* Name.Variable.Instance */
.highlight .vm { color: #369 } /* Name.Variable.Magic */
.highlight .il { color: #00D; font-weight: bold } /* Literal.Number.Integer.Long */
/*
  Simple DirectMedia Layer
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>

  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  arising from the use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
*/
#include "../SDL_internal.h"

/* Functions for audio drivers to perform runtime conversion of audio format */

#include "SDL_audio.h"
#include "SDL_audio_c.h"

#include "SDL_assert.h"

/* #define DEBUG_CONVERT */

/* Effectively mix right and left channels into a single channel */
static void SDLCALL
SDL_ConvertMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
{
    int i;
    Sint32 sample;

#ifdef DEBUG_CONVERT
    fprintf(stderr, "Converting to mono\n");
#endif
	switch (format & (SDL_AUDIO_MASK_SIGNED |
                      SDL_AUDIO_MASK_BITSIZE |
                      SDL_AUDIO_MASK_DATATYPE)) {
    case AUDIO_U8:
        {
            Uint8 *src, *dst;

            src = cvt->buf;
            dst = cvt->buf;
            for (i = cvt->len_cvt / 2; i; --i) {
                sample = src[0] + src[1];
                *dst = (Uint8) (sample / 2);
                src += 2;
                dst += 1;
            }
        }
        break;

    case AUDIO_S8:
        {
            Sint8 *src, *dst;

            src = (Sint8 *) cvt->buf;
            dst = (Sint8 *) cvt->buf;
            for (i = cvt->len_cvt / 2; i; --i) {
                sample = src[0] + src[1];
                *dst = (Sint8) (sample / 2);
                src += 2;
                dst += 1;
            }
        }
        break;

    case AUDIO_U16:
        {
            Uint8 *src, *dst;

            src = cvt->buf;
            dst = cvt->buf;
            if (SDL_AUDIO_ISBIGENDIAN(format)) {
                for (i = cvt->len_cvt / 4; i; --i) {
                    sample = (Uint16) ((src[0] << 8) | src[1]) +
                        (Uint16) ((src[2] << 8) | src[3]);
                    sample /= 2;
                    dst[1] = (sample & 0xFF);
                    sample >>= 8;
                    dst[0] = (sample & 0xFF);
                    src += 4;
                    dst += 2;
                }
            } else {
                for (i = cvt->len_cvt / 4; i; --i) {
                    sample = (Uint16) ((src[1] << 8) | src[0]) +
                        (Uint16) ((src[3] << 8) | src[2]);
                    sample /= 2;
                    dst[0] = (sample & 0xFF);
                    sample >>= 8;
                    dst[1] = (sample & 0xFF);
                    src += 4;
                    dst += 2;
                }
            }
        }
        break;

    case AUDIO_S16:
        {
            Uint8 *src, *dst;

            src = cvt->buf;
            dst = cvt->buf;
            if (SDL_AUDIO_ISBIGENDIAN(format)) {
                for (i = cvt->len_cvt / 4; i; --i) {
                    sample = (Sint16) ((src[0] << 8) | src[1]) +
                        (Sint16) ((src[2] << 8) | src[3]);
                    sample /= 2;
                    dst[1] = (sample & 0xFF);
                    sample >>= 8;
                    dst[0] = (sample & 0xFF);
                    src += 4;
                    dst += 2;
                }
            } else {
                for (i = cvt->len_cvt / 4; i; --i) {
                    sample = (Sint16) ((src[1] << 8) | src[0]) +
                        (Sint16) ((src[3] << 8) | src[2]);
                    sample /= 2;
                    dst[0] = (sample & 0xFF);
                    sample >>= 8;
                    dst[1] = (sample & 0xFF);
                    src += 4;
                    dst += 2;
                }
            }
        }
        break;

    case AUDIO_S32:
        {
            const Uint32 *src = (const Uint32 *) cvt->buf;
            Uint32 *dst = (Uint32 *) cvt->buf;
            if (SDL_AUDIO_ISBIGENDIAN(format)) {
                for (i = cvt->len_cvt / 8; i; --i, src += 2) {
                    const Sint64 added =
                        (((Sint64) (Sint32) SDL_SwapBE32(src[0])) +
                         ((Sint64) (Sint32) SDL_SwapBE32(src[1])));
                    *(dst++) = SDL_SwapBE32((Uint32) ((Sint32) (added / 2)));
                }
            } else {
                for (i = cvt->len_cvt / 8; i; --i, src += 2) {
                    const Sint64 added =
                        (((Sint64) (Sint32) SDL_SwapLE32(src[0])) +
                         ((Sint64) (Sint32) SDL_SwapLE32(src[1])));
                    *(dst++) = SDL_SwapLE32((Uint32) ((Sint32) (added / 2)));
                }
            }
        }
        break;

    case AUDIO_F32:
        {
            const float *src = (const float *) cvt->buf;
            float *dst = (float *) cvt->buf;
            if (SDL_AUDIO_ISBIGENDIAN(format)) {
                for (i = cvt->len_cvt / 8; i; --i, src += 2) {
                    const float src1 = SDL_SwapFloatBE(src[0]);
                    const float src2 = SDL_SwapFloatBE(src[1]);
                    const double added = ((double) src1) + ((double) src2);
                    const float halved = (float) (added * 0.5);
                    *(dst++) = SDL_SwapFloatBE(halved);
                }
            } else {
                for (i = cvt->len_cvt / 8; i; --i, src += 2) {
                    const float src1 = SDL_SwapFloatLE(src[0]);
                    const float src2 = SDL_SwapFloatLE(src[1]);
                    const double added = ((double) src1) + ((double) src2);
                    const float halved = (float) (added * 0.5);
                    *(dst++) = SDL_SwapFloatLE(halved);
                }
            }
        }
        break;
    }

    cvt->len_cvt /= 2;
    if (cvt->filters[++cvt->filter_index]) {
        cvt->filters[cvt->filter_index] (cvt, format);
    }
}


/* Discard top 4 channels */
static void SDLCALL
SDL_ConvertStrip(SDL_AudioCVT * cvt, SDL_AudioFormat format)
{
    int i;

#ifdef DEBUG_CONVERT
    fprintf(stderr, "Converting down from 6 channels to stereo\n");
#endif

#define strip_chans_6_to_2(type) \
    { \
        const type *src = (const type *) cvt->buf; \
        type *dst = (type *) cvt->buf; \
        for (i = cvt->len_cvt / (sizeof (type) * 6); i; --i) { \
            dst[0] = src[0]; \
            dst[1] = src[1]; \
            src += 6; \
            dst += 2; \
        } \
    }

    /* this function only cares about typesize, and data as a block of bits. */
    switch (SDL_AUDIO_BITSIZE(format)) {
    case 8:
        strip_chans_6_to_2(Uint8);
        break;
    case 16:
        strip_chans_6_to_2(Uint16);
        break;
    case 32:
        strip_chans_6_to_2(Uint32);
        break;
    }

#undef strip_chans_6_to_2

    cvt->len_cvt /= 3;
    if (cvt->filters[++cvt->filter_index]) {
        cvt->filters[cvt->filter_index] (cvt, format);
    }
}


/* Discard top 2 channels of 6 */
static void SDLCALL
SDL_ConvertStrip_2(SDL_AudioCVT * cvt, SDL_AudioFormat format)
{
    int i;

#ifdef DEBUG_CONVERT
    fprintf(stderr, "Converting 6 down to quad\n");
#endif

#define strip_chans_6_to_4(type) \
    { \
        const type *src = (const type *) cvt->buf; \
        type *dst = (type *) cvt->buf; \
        for (i = cvt->len_cvt / (sizeof (type) * 6); i; --i) { \
            dst[0] = src[0]; \
            dst[1] = src[1]; \
            dst[2] = src[2]; \
            dst[3] = src[3]; \
            src += 6; \
            dst += 4; \
        } \
    }

    /* this function only cares about typesize, and data as a block of bits. */
    switch (SDL_AUDIO_BITSIZE(format)) {
    case 8:
        strip_chans_6_to_4(Uint8);
        break;
    case 16:
        strip_chans_6_to_4(Uint16);
        break;
    case 32:
        strip_chans_6_to_4(Uint32);
        break;
    }

#undef strip_chans_6_to_4

    cvt->len_cvt /= 6;
    cvt->len_cvt *= 4;
    if (cvt->filters[++cvt->filter_index]) {
        cvt->filters[cvt->filter_index] (cvt, format);
    }
}

/* Duplicate a mono channel to both stereo channels */
static void SDLCALL
SDL_ConvertStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
{
    int i;

#ifdef DEBUG_CONVERT
    fprintf(stderr, "Converting to stereo\n");
#endif

#define dup_chans_1_to_2(type) \
    { \
        const type *src = (const type *) (cvt->buf + cvt->len_cvt); \
        type *dst = (type *) (cvt->buf + cvt->len_cvt * 2); \
        for (i = cvt->len_cvt / sizeof(type); i; --i) { \
            src -= 1; \
            dst -= 2; \
            dst[0] = dst[1] = *src; \
        } \
    }

    /* this function only cares about typesize, and data as a block of bits. */
    switch (SDL_AUDIO_BITSIZE(format)) {
    case 8:
        dup_chans_1_to_2(Uint8);
        break;
    case 16:
        dup_chans_1_to_2(Uint16);
        break;
    case 32:
        dup_chans_1_to_2(Uint32);
        break;
    }

#undef dup_chans_1_to_2

    cvt->len_cvt *= 2;
    if (cvt->filters[++cvt->filter_index]) {
        cvt->filters[cvt->filter_index] (cvt, format);
    }
}


/* Duplicate a stereo channel to a pseudo-5.1 stream */
static void SDLCALL
SDL_ConvertSurround(SDL_AudioCVT * cvt, SDL_AudioFormat format)
{
    int i;

#ifdef DEBUG_CONVERT
    fprintf(stderr, "Converting stereo to surround\n");
#endif

    switch (format & (SDL_AUDIO_MASK_SIGNED  |
                      SDL_AUDIO_MASK_BITSIZE |
                      SDL_AUDIO_MASK_DATATYPE)) {
    case AUDIO_U8:
        {
            Uint8 *src, *dst, lf, rf, ce;

            src = (Uint8 *) (cvt->buf + cvt->len_cvt);
            dst = (Uint8 *) (cvt->buf + cvt->len_cvt * 3);
            for (i = cvt->len_cvt; i; --i) {
                dst -= 6;
                src -= 2;
                lf = src[0];
                rf = src[1];
                ce = (lf / 2) + (rf / 2);
                dst[0] = lf;
                dst[1] = rf;
                dst[2] = lf - ce;
                dst[3] = rf - ce;
                dst[4] = ce;
                dst[5] = ce;
            }
        }
        break;

    case AUDIO_S8:
        {
            Sint8 *src, *dst, lf, rf, ce;

            src = (Sint8 *) cvt->buf + cvt->len_cvt;
            dst = (Sint8 *) cvt->buf + cvt->len_cvt * 3;
            for (i = cvt->len_cvt; i; --i) {
                dst -= 6;
                src -= 2;
                lf = src[0];
                rf = src[1];
                ce = (lf / 2) + (rf / 2);
                dst[0] = lf;
                dst[1] = rf;
                dst[2] = lf - ce;
                dst[3] = rf - ce;
                dst[4] = ce;
                dst[5] = ce;
            }
        }
        break;

    case AUDIO_U16:
        {
            Uint8 *src, *dst;
            Uint16 lf, rf, ce, lr, rr;

            src = cvt->buf + cvt->len_cvt;
            dst = cvt->buf + cvt->len_cvt * 3;

            if (SDL_AUDIO_ISBIGENDIAN(format)) {
                for (i = cvt->len_cvt / 4; i; --i) {
                    dst -= 12;
                    src -= 4;
                    lf = (Uint16) ((src[0] << 8) | src[1]);
                    rf = (Uint16) ((src[2] << 8) | src[3]);
                    ce = (lf / 2) + (rf / 2);
                    rr = lf - ce;
                    lr = rf - ce;
                    dst[1] = (lf & 0xFF);
                    dst[0] = ((lf >> 8) & 0xFF);
                    dst[3] = (rf & 0xFF);
                    dst[2] = ((rf >> 8) & 0xFF);

                    dst[1 + 4] = (lr & 0xFF);
                    dst[0 + 4] = ((lr >> 8) & 0xFF);
                    dst[3 + 4] = (rr & 0xFF);
                    dst[2 + 4] = ((rr >> 8) & 0xFF);

                    dst[1 + 8] = (ce & 0xFF);
                    dst[0 + 8] = ((ce >> 8) & 0xFF);
                    dst[3 + 8] = (ce & 0xFF);
                    dst[2 + 8] = ((ce >> 8) & 0xFF);
                }
            } else {
                for (i = cvt->len_cvt / 4; i; --i) {
                    dst -= 12;
                    src -= 4;
                    lf = (Uint16) ((src[1] << 8) | src[0]);
                    rf = (Uint16) ((src[3] << 8) | src[2]);
                    ce = (lf / 2) + (rf / 2);
                    rr = lf - ce;
                    lr = rf - ce;
                    dst[0] = (lf & 0xFF);
                    dst[1] = ((lf >> 8) & 0xFF);
                    dst[2] = (rf & 0xFF);
                    dst[3] = ((rf >> 8) & 0xFF);

                    dst[0 + 4] = (lr & 0xFF);
                    dst[1 + 4] = ((lr >> 8) & 0xFF);
                    dst[2 + 4] = (rr & 0xFF);
                    dst[3 + 4] = ((rr >> 8) & 0xFF);

                    dst[0 + 8] = (ce & 0xFF);
                    dst[1 + 8] = ((ce >> 8) & 0xFF);
                    dst[2 + 8] = (ce & 0xFF);
                    dst[3 + 8] = ((ce >> 8) & 0xFF);
                }
            }
        }
        break;

    case AUDIO_S16:
        {
            Uint8 *src, *dst;
            Sint16 lf, rf, ce, lr, rr;

            src = cvt->buf + cvt->len_cvt;
            dst = cvt->buf + cvt->len_cvt * 3;

            if (SDL_AUDIO_ISBIGENDIAN(format)) {
                for (i = cvt->len_cvt / 4; i; --i) {
                    dst -= 12;
                    src -= 4;
                    lf = (Sint16) ((src[0] << 8) | src[1]);
                    rf = (Sint16) ((src[2] << 8) | src[3]);
                    ce = (lf / 2) + (rf / 2);
                    rr = lf - ce;
                    lr = rf - ce;
                    dst[1] = (lf & 0xFF);
                    dst[0] = ((lf >> 8) & 0xFF);
                    dst[3] = (rf & 0xFF);
                    dst[2] = ((rf >> 8) & 0xFF);

                    dst[1 + 4] = (lr & 0xFF);
                    dst[0 + 4] = ((lr >> 8) & 0xFF);
                    dst[3 + 4] = (rr & 0xFF);
                    dst[2 + 4] = ((rr >> 8) & 0xFF);

                    dst[1 + 8] = (ce & 0xFF);
                    dst[0 + 8] = ((ce >> 8) & 0xFF);
                    dst[3 + 8] = (ce & 0xFF);
                    dst[2 + 8] = ((ce >> 8) & 0xFF);
                }
            } else {
                for (i = cvt->len_cvt / 4; i; --i) {
                    dst -= 12;
                    src -= 4;
                    lf = (Sint16) ((src[1] << 8) | src[0]);
                    rf = (Sint16) ((src[3] << 8) | src[2]);
                    ce = (lf / 2) + (rf / 2);
                    rr = lf - ce;
                    lr = rf - ce;
                    dst[0] = (lf & 0xFF);
                    dst[1] = ((lf >> 8) & 0xFF);
                    dst[2] = (rf & 0xFF);
                    dst[3] = ((rf >> 8) & 0xFF);

                    dst[0 + 4] = (lr & 0xFF);
                    dst[1 + 4] = ((lr >> 8) & 0xFF);
                    dst[2 + 4] = (rr & 0xFF);
                    dst[3 + 4] = ((rr >> 8) & 0xFF);

                    dst[0 + 8] = (ce & 0xFF);
                    dst[1 + 8] = ((ce >> 8) & 0xFF);
                    dst[2 + 8] = (ce & 0xFF);
                    dst[3 + 8] = ((ce >> 8) & 0xFF);
                }
            }
        }
        break;

    case AUDIO_S32:
        {
            Sint32 lf, rf, ce;
            const Uint32 *src = (const Uint32 *) (cvt->buf + cvt->len_cvt);
            Uint32 *dst = (Uint32 *) (cvt->buf + cvt->len_cvt * 3);

            if (SDL_AUDIO_ISBIGENDIAN(format)) {
                for (i = cvt->len_cvt / 8; i; --i) {
                    dst -= 6;
                    src -= 2;
                    lf = (Sint32) SDL_SwapBE32(src[0]);
                    rf = (Sint32) SDL_SwapBE32(src[1]);
                    ce = (lf / 2) + (rf / 2);
                    dst[0] = SDL_SwapBE32((Uint32) lf);
                    dst[1] = SDL_SwapBE32((Uint32) rf);
                    dst[2] = SDL_SwapBE32((Uint32) (lf - ce));
                    dst[3] = SDL_SwapBE32((Uint32) (rf - ce));
                    dst[4] = SDL_SwapBE32((Uint32) ce);
                    dst[5] = SDL_SwapBE32((Uint32) ce);
                }
            } else {
                for (i = cvt->len_cvt / 8; i; --i) {
                    dst -= 6;
                    src -= 2;
                    lf = (Sint32) SDL_SwapLE32(src[0]);
                    rf = (Sint32) SDL_SwapLE32(src[1]);
                    ce = (lf / 2) + (rf / 2);
                    dst[0] = src[0];
                    dst[1] = src[1];
                    dst[2] = SDL_SwapLE32((Uint32) (lf - ce));
                    dst[3] = SDL_SwapLE32((Uint32) (rf - ce));
                    dst[4] = SDL_SwapLE32((Uint32) ce);
                    dst[5] = SDL_SwapLE32((Uint32) ce);
                }
            }
        }
        break;

    case AUDIO_F32:
        {
            float lf, rf, ce;
            const float *src = (const float *) (cvt->buf + cvt->len_cvt);
            float *dst = (float *) (cvt->buf + cvt->len_cvt * 3);

            if (SDL_AUDIO_ISBIGENDIAN(format)) {
                for (i = cvt->len_cvt / 8; i; --i) {
                    dst -= 6;
                    src -= 2;
                    lf = SDL_SwapFloatBE(src[0]);
                    rf = SDL_SwapFloatBE(src[1]);
                    ce = (lf * 0.5f) + (rf * 0.5f);
                    dst[0] = src[0];
                    dst[1] = src[1];
                    dst[2] = SDL_SwapFloatBE(lf - ce);
                    dst[3] = SDL_SwapFloatBE(rf - ce);
                    dst[4] = dst[5] = SDL_SwapFloatBE(ce);
                }
            } else {
                for (i = cvt->len_cvt / 8; i; --i) {
                    dst -= 6;
                    src -= 2;
                    lf = SDL_SwapFloatLE(src[0]);
                    rf = SDL_SwapFloatLE(src[1]);
                    ce = (lf * 0.5f) + (rf * 0.5f);
                    dst[0] = src[0];
                    dst[1] = src[1];
                    dst[2] = SDL_SwapFloatLE(lf - ce);
                    dst[3] = SDL_SwapFloatLE(rf - ce);
                    dst[4] = dst[5] = SDL_SwapFloatLE(ce);
                }
            }
        }
        break;

    }
    cvt->len_cvt *= 3;
    if (cvt->filters[++cvt->filter_index]) {
        cvt->filters[cvt->filter_index] (cvt, format);
    }
}


/* Duplicate a stereo channel to a pseudo-4.0 stream */
static void SDLCALL
SDL_ConvertSurround_4(SDL_AudioCVT * cvt, SDL_AudioFormat format)
{
    int i;

#ifdef DEBUG_CONVERT
    fprintf(stderr, "Converting stereo to quad\n");
#endif

    switch (format & (SDL_AUDIO_MASK_SIGNED |
                      SDL_AUDIO_MASK_BITSIZE |
                      SDL_AUDIO_MASK_DATATYPE)) {
    case AUDIO_U8:
        {
            Uint8 *src, *dst, lf, rf, ce;

            src = (Uint8 *) (cvt->buf + cvt->len_cvt);
            dst = (Uint8 *) (cvt->buf + cvt->len_cvt * 2);
            for (i = cvt->len_cvt; i; --i) {
                dst -= 4;
                src -= 2;
                lf = src[0];
                rf = src[1];
                ce = (lf / 2) + (rf / 2);
                dst[0] = lf;
                dst[1] = rf;
                dst[2] = lf - ce;
                dst[3] = rf - ce;
            }
        }
        break;

    case AUDIO_S8:
        {
            Sint8 *src, *dst, lf, rf, ce;

            src = (Sint8 *) cvt->buf + cvt->len_cvt;
            dst = (Sint8 *) cvt->buf + cvt->len_cvt * 2;
            for (i = cvt->len_cvt; i; --i) {
                dst -= 4;
                src -= 2;
                lf = src[0];
                rf = src[1];
                ce = (lf / 2) + (rf / 2);
                dst[0] = lf;
                dst[1] = rf;
                dst[2] = lf - ce;
                dst[3] = rf - ce;
            }
        }
        break;

    case AUDIO_U16:
        {
            Uint8 *src, *dst;
            Uint16 lf, rf, ce, lr, rr;

            src = cvt->buf + cvt->len_cvt;
            dst = cvt->buf + cvt->len_cvt * 2;

            if (SDL_AUDIO_ISBIGENDIAN(format)) {
                for (i = cvt->len_cvt / 4; i; --i) {
                    dst -= 8;
                    src -= 4;
                    lf = (Uint16) ((src[0] << 8) | src[1]);
                    rf = (Uint16) ((src[2] << 8) | src[3]);
                    ce = (lf / 2) + (rf / 2);
                    rr = lf - ce;
                    lr = rf - ce;
                    dst[1] = (lf & 0xFF);
                    dst[0] = ((lf >> 8) & 0xFF);
                    dst[3] = (rf & 0xFF);
                    dst[2] = ((rf >> 8) & 0xFF);

                    dst[1 + 4] = (lr & 0xFF);
                    dst[0 + 4] = ((lr >> 8) & 0xFF);
                    dst[3 + 4] = (rr & 0xFF);
                    dst[2 + 4] = ((rr >> 8) & 0xFF);
                }
            } else {
                for (i = cvt->len_cvt / 4; i; --i) {
                    dst -= 8;
                    src -= 4;
                    lf = (Uint16) ((src[1] << 8) | src[0]);
                    rf = (Uint16) ((src[3] << 8) | src[2]);
                    ce = (lf / 2) + (rf / 2);
                    rr = lf - ce;
                    lr = rf - ce;
                    dst[0] = (lf & 0xFF);
                    dst[1] = ((lf >> 8) & 0xFF);
                    dst[2] = (rf & 0xFF);
                    dst[3] = ((rf >> 8) & 0xFF);

                    dst[0 + 4] = (lr & 0xFF);
                    dst[1 + 4] = ((lr >> 8) & 0xFF);
                    dst[2 + 4] = (rr & 0xFF);
                    dst[3 + 4] = ((rr >> 8) & 0xFF);
                }
            }
        }
        break;

    case AUDIO_S16:
        {
            Uint8 *src, *dst;
            Sint16 lf, rf, ce, lr, rr;

            src = cvt->buf + cvt->len_cvt;
            dst = cvt->buf + cvt->len_cvt * 2;

            if (SDL_AUDIO_ISBIGENDIAN(format)) {
                for (i = cvt->len_cvt / 4; i; --i) {
                    dst -= 8;
                    src -= 4;
                    lf = (Sint16) ((src[0] << 8) | src[1]);
                    rf = (Sint16) ((src[2] << 8) | src[3]);
                    ce = (lf / 2) + (rf / 2);
                    rr = lf - ce;
                    lr = rf - ce;
                    dst[1] = (lf & 0xFF);
                    dst[0] = ((lf >> 8) & 0xFF);
                    dst[3] = (rf & 0xFF);
                    dst[2] = ((rf >> 8) & 0xFF);

                    dst[1 + 4] = (lr & 0xFF);
                    dst[0 + 4] = ((lr >> 8) & 0xFF);
                    dst[3 + 4] = (rr & 0xFF);
                    dst[2 + 4] = ((rr >> 8) & 0xFF);
                }
            } else {
                for (i = cvt->len_cvt / 4; i; --i) {
                    dst -= 8;
                    src -= 4;
                    lf = (Sint16) ((src[1] << 8) | src[0]);
                    rf = (Sint16) ((src[3] << 8) | src[2]);
                    ce = (lf / 2) + (rf / 2);
                    rr = lf - ce;
                    lr = rf - ce;
                    dst[0] = (lf & 0xFF);
                    dst[1] = ((lf >> 8) & 0xFF);
                    dst[2] = (rf & 0xFF);
                    dst[3] = ((rf >> 8) & 0xFF);

                    dst[0 + 4] = (lr & 0xFF);
                    dst[1 + 4] = ((lr >> 8) & 0xFF);
                    dst[2 + 4] = (rr & 0xFF);
                    dst[3 + 4] = ((rr >> 8) & 0xFF);
                }
            }
        }
        break;

    case AUDIO_S32:
        {
            const Uint32 *src = (const Uint32 *) (cvt->buf + cvt->len_cvt);
            Uint32 *dst = (Uint32 *) (cvt->buf + cvt->len_cvt * 2);
            Sint32 lf, rf, ce;

            if (SDL_AUDIO_ISBIGENDIAN(format)) {
                for (i = cvt->len_cvt / 8; i; --i) {
                    dst -= 4;
                    src -= 2;
                    lf = (Sint32) SDL_SwapBE32(src[0]);
                    rf = (Sint32) SDL_SwapBE32(src[1]);
                    ce = (lf / 2) + (rf / 2);
                    dst[0] = src[0];
                    dst[1] = src[1];
                    dst[2] = SDL_SwapBE32((Uint32) (lf - ce));
                    dst[3] = SDL_SwapBE32((Uint32) (rf - ce));
                }
            } else {
                for (i = cvt->len_cvt / 8; i; --i) {
                    dst -= 4;
                    src -= 2;
                    lf = (Sint32) SDL_SwapLE32(src[0]);
                    rf = (Sint32) SDL_SwapLE32(src[1]);
                    ce = (lf / 2) + (rf / 2);
                    dst[0] = src[0];
                    dst[1] = src[1];
                    dst[2] = SDL_SwapLE32((Uint32) (lf - ce));
                    dst[3] = SDL_SwapLE32((Uint32) (rf - ce));
                }
            }
        }
        break;

    case AUDIO_F32:
        {
            const float *src = (const float *) (cvt->buf + cvt->len_cvt);
            float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
            float lf, rf, ce;

            if (SDL_AUDIO_ISBIGENDIAN(format)) {
                for (i = cvt->len_cvt / 8; i; --i) {
                    dst -= 4;
                    src -= 2;
                    lf = SDL_SwapFloatBE(src[0]);
                    rf = SDL_SwapFloatBE(src[1]);
                    ce = (lf / 2) + (rf / 2);
                    dst[0] = src[0];
                    dst[1] = src[1];
                    dst[2] = SDL_SwapFloatBE(lf - ce);
                    dst[3] = SDL_SwapFloatBE(rf - ce);
                }
            } else {
                for (i = cvt->len_cvt / 8; i; --i) {
                    dst -= 4;
                    src -= 2;
                    lf = SDL_SwapFloatLE(src[0]);
                    rf = SDL_SwapFloatLE(src[1]);
                    ce = (lf / 2) + (rf / 2);
                    dst[0] = src[0];
                    dst[1] = src[1];
                    dst[2] = SDL_SwapFloatLE(lf - ce);
                    dst[3] = SDL_SwapFloatLE(rf - ce);
                }
            }
        }
        break;
    }
    cvt->len_cvt *= 2;
    if (cvt->filters[++cvt->filter_index]) {
        cvt->filters[cvt->filter_index] (cvt, format);
    }
}


int
SDL_ConvertAudio(SDL_AudioCVT * cvt)
{
    /* !!! FIXME: (cvt) should be const; stack-copy it here. */
    /* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */

    /* Make sure there's data to convert */
    if (cvt->buf == NULL) {
        SDL_SetError("No buffer allocated for conversion");
        return (-1);
    }
    /* Return okay if no conversion is necessary */
    cvt->len_cvt = cvt->len;
    if (cvt->filters[0] == NULL) {
        return (0);
    }

    /* Set up the conversion and go! */
    cvt->filter_index = 0;
    cvt->filters[0] (cvt, cvt->src_format);
    return (0);
}


static SDL_AudioFilter
SDL_HandTunedTypeCVT(SDL_AudioFormat src_fmt, SDL_AudioFormat dst_fmt)
{
    /*
     * Fill in any future conversions that are specialized to a
     *  processor, platform, compiler, or library here.
     */

    return NULL;                /* no specialized converter code available. */
}


/*
 * Find a converter between two data types. We try to select a hand-tuned
 *  asm/vectorized/optimized function first, and then fallback to an
 *  autogenerated function that is customized to convert between two
 *  specific data types.
 */
static int
SDL_BuildAudioTypeCVT(SDL_AudioCVT * cvt,
                      SDL_AudioFormat src_fmt, SDL_AudioFormat dst_fmt)
{
    if (src_fmt != dst_fmt) {
        const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt);
        const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt);
        SDL_AudioFilter filter = SDL_HandTunedTypeCVT(src_fmt, dst_fmt);

        /* No hand-tuned converter? Try the autogenerated ones. */
        if (filter == NULL) {
            int i;
            for (i = 0; sdl_audio_type_filters[i].filter != NULL; i++) {
                const SDL_AudioTypeFilters *filt = &sdl_audio_type_filters[i];
                if ((filt->src_fmt == src_fmt) && (filt->dst_fmt == dst_fmt)) {
                    filter = filt->filter;
                    break;
                }
            }

            if (filter == NULL) {
                SDL_SetError("No conversion available for these formats");
                return -1;
            }
        }

        /* Update (cvt) with filter details... */
        cvt->filters[cvt->filter_index++] = filter;
        if (src_bitsize < dst_bitsize) {
            const int mult = (dst_bitsize / src_bitsize);
            cvt->len_mult *= mult;
            cvt->len_ratio *= mult;
        } else if (src_bitsize > dst_bitsize) {
            cvt->len_ratio /= (src_bitsize / dst_bitsize);
        }

        return 1;               /* added a converter. */
    }

    return 0;                   /* no conversion necessary. */
}


static SDL_AudioFilter
SDL_HandTunedResampleCVT(SDL_AudioCVT * cvt, int dst_channels,
                         int src_rate, int dst_rate)
{
    /*
     * Fill in any future conversions that are specialized to a
     *  processor, platform, compiler, or library here.
     */

    return NULL;                /* no specialized converter code available. */
}

static int
SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate)
{
    int retval = 0;

    /* If we only built with the arbitrary resamplers, ignore multiples. */
#if !LESS_RESAMPLERS
    int lo, hi;
    int div;

    SDL_assert(src_rate != 0);
    SDL_assert(dst_rate != 0);
    SDL_assert(src_rate != dst_rate);

    if (src_rate < dst_rate) {
        lo = src_rate;
        hi = dst_rate;
    } else {
        lo = dst_rate;
        hi = src_rate;
    }

    /* zero means "not a supported multiple" ... we only do 2x and 4x. */
    if ((hi % lo) != 0)
        return 0;               /* not a multiple. */

    div = hi / lo;
    retval = ((div == 2) || (div == 4)) ? div : 0;
#endif

    return retval;
}

static int
SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, int dst_channels,
                          int src_rate, int dst_rate)
{
    if (src_rate != dst_rate) {
        SDL_AudioFilter filter = SDL_HandTunedResampleCVT(cvt, dst_channels,
                                                          src_rate, dst_rate);

        /* No hand-tuned converter? Try the autogenerated ones. */
        if (filter == NULL) {
            int i;
            const int upsample = (src_rate < dst_rate) ? 1 : 0;
            const int multiple =
                SDL_FindFrequencyMultiple(src_rate, dst_rate);

            for (i = 0; sdl_audio_rate_filters[i].filter != NULL; i++) {
                const SDL_AudioRateFilters *filt = &sdl_audio_rate_filters[i];
                if ((filt->fmt == cvt->dst_format) &&
                    (filt->channels == dst_channels) &&
                    (filt->upsample == upsample) &&
                    (filt->multiple == multiple)) {
                    filter = filt->filter;
                    break;
                }
            }

            if (filter == NULL) {
                SDL_SetError("No conversion available for these rates");
                return -1;
            }
        }

        /* Update (cvt) with filter details... */
        cvt->filters[cvt->filter_index++] = filter;
        if (src_rate < dst_rate) {
            const double mult = ((double) dst_rate) / ((double) src_rate);
            cvt->len_mult *= (int) SDL_ceil(mult);
            cvt->len_ratio *= mult;
        } else {
            cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
        }

        return 1;               /* added a converter. */
    }

    return 0;                   /* no conversion necessary. */
}


/* Creates a set of audio filters to convert from one format to another.
   Returns -1 if the format conversion is not supported, 0 if there's
   no conversion needed, or 1 if the audio filter is set up.
*/

int
SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
                  SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate,
                  SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate)
{
    /*
     * !!! FIXME: reorder filters based on which grow/shrink the buffer.
     * !!! FIXME: ideally, we should do everything that shrinks the buffer
     * !!! FIXME: first, so we don't have to process as many bytes in a given
     * !!! FIXME: filter and abuse the CPU cache less. This might not be as
     * !!! FIXME: good in practice as it sounds in theory, though.
     */

    /* Sanity check target pointer */
    if (cvt == NULL) {
        return SDL_InvalidParamError("cvt");
    }

    /* there are no unsigned types over 16 bits, so catch this up front. */
    if ((SDL_AUDIO_BITSIZE(src_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(src_fmt))) {
        return SDL_SetError("Invalid source format");
    }
    if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) {
        return SDL_SetError("Invalid destination format");
    }

    /* prevent possible divisions by zero, etc. */
    if ((src_channels == 0) || (dst_channels == 0)) {
        return SDL_SetError("Source or destination channels is zero");
    }
    if ((src_rate == 0) || (dst_rate == 0)) {
        return SDL_SetError("Source or destination rate is zero");
    }
#ifdef DEBUG_CONVERT
    printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n",
           src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate);
#endif

    /* Start off with no conversion necessary */
    SDL_zerop(cvt);
    cvt->src_format = src_fmt;
    cvt->dst_format = dst_fmt;
    cvt->needed = 0;
    cvt->filter_index = 0;
    cvt->filters[0] = NULL;
    cvt->len_mult = 1;
    cvt->len_ratio = 1.0;
    cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);

    /* Convert data types, if necessary. Updates (cvt). */
    if (SDL_BuildAudioTypeCVT(cvt, src_fmt, dst_fmt) == -1) {
        return -1;              /* shouldn't happen, but just in case... */
    }

    /* Channel conversion */
    if (src_channels != dst_channels) {
        if ((src_channels == 1) && (dst_channels > 1)) {
            cvt->filters[cvt->filter_index++] = SDL_ConvertStereo;
            cvt->len_mult *= 2;
            src_channels = 2;
            cvt->len_ratio *= 2;
        }
        if ((src_channels == 2) && (dst_channels == 6)) {
            cvt->filters[cvt->filter_index++] = SDL_ConvertSurround;
            src_channels = 6;
            cvt->len_mult *= 3;
            cvt->len_ratio *= 3;
        }
        if ((src_channels == 2) && (dst_channels == 4)) {
            cvt->filters[cvt->filter_index++] = SDL_ConvertSurround_4;
            src_channels = 4;
            cvt->len_mult *= 2;
            cvt->len_ratio *= 2;
        }
        while ((src_channels * 2) <= dst_channels) {
            cvt->filters[cvt->filter_index++] = SDL_ConvertStereo;
            cvt->len_mult *= 2;
            src_channels *= 2;
            cvt->len_ratio *= 2;
        }
        if ((src_channels == 6) && (dst_channels <= 2)) {
            cvt->filters[cvt->filter_index++] = SDL_ConvertStrip;
            src_channels = 2;
            cvt->len_ratio /= 3;
        }
        if ((src_channels == 6) && (dst_channels == 4)) {
            cvt->filters[cvt->filter_index++] = SDL_ConvertStrip_2;
            src_channels = 4;
            cvt->len_ratio /= 2;
        }
        /* This assumes that 4 channel audio is in the format:
           Left {front/back} + Right {front/back}
           so converting to L/R stereo works properly.
         */
        while (((src_channels % 2) == 0) &&
               ((src_channels / 2) >= dst_channels)) {
            cvt->filters[cvt->filter_index++] = SDL_ConvertMono;
            src_channels /= 2;
            cvt->len_ratio /= 2;
        }
        if (src_channels != dst_channels) {
            /* Uh oh.. */ ;
        }
    }

    /* Do rate conversion, if necessary. Updates (cvt). */
    if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) ==
        -1) {
        return -1;              /* shouldn't happen, but just in case... */
    }

    /* Set up the filter information */
    if (cvt->filter_index != 0) {
        cvt->needed = 1;
        cvt->src_format = src_fmt;
        cvt->dst_format = dst_fmt;
        cvt->len = 0;
        cvt->buf = NULL;
        cvt->filters[cvt->filter_index] = NULL;
    }
    return (cvt->needed);
}


/* vi: set ts=4 sw=4 expandtab: */