src/audio/SDL_audiocvt.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Mon, 07 Nov 2016 21:10:01 +0100
changeset 10591 9717b0804467
parent 10579 03531deafb03
child 10737 3406a0f8b041
permissions -rw-r--r--
Fixed audio conversion for unsigned 16 bit data.
slouken@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@9998
     3
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
slouken@0
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@0
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@0
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    20
*/
icculus@8093
    21
#include "../SDL_internal.h"
slouken@2728
    22
slouken@0
    23
/* Functions for audio drivers to perform runtime conversion of audio format */
slouken@0
    24
slouken@0
    25
#include "SDL_audio.h"
icculus@1982
    26
#include "SDL_audio_c.h"
slouken@0
    27
icculus@6281
    28
#include "SDL_assert.h"
icculus@6281
    29
slouken@2716
    30
slouken@0
    31
/* Effectively mix right and left channels into a single channel */
icculus@1982
    32
static void SDLCALL
icculus@1982
    33
SDL_ConvertMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@0
    34
{
icculus@10575
    35
    float *dst = (float *) cvt->buf;
icculus@10575
    36
    const float *src = dst;
slouken@1895
    37
    int i;
slouken@0
    38
icculus@10575
    39
    LOG_DEBUG_CONVERT("stereo", "mono");
icculus@10575
    40
    SDL_assert(format == AUDIO_F32SYS);
slouken@0
    41
icculus@10575
    42
    for (i = cvt->len_cvt / 8; i; --i, src += 2) {
icculus@10575
    43
        *(dst++) = (float) ((((double) src[0]) + ((double) src[1])) * 0.5);
slouken@1895
    44
    }
icculus@1982
    45
slouken@1895
    46
    cvt->len_cvt /= 2;
slouken@1895
    47
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
    48
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
    49
    }
slouken@0
    50
}
slouken@0
    51
icculus@1982
    52
slouken@942
    53
/* Discard top 4 channels */
icculus@1982
    54
static void SDLCALL
icculus@1982
    55
SDL_ConvertStrip(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
    56
{
icculus@10575
    57
    float *dst = (float *) cvt->buf;
icculus@10575
    58
    const float *src = dst;
slouken@1895
    59
    int i;
slouken@942
    60
icculus@10575
    61
    LOG_DEBUG_CONVERT("6 channels", "stereo");
icculus@10575
    62
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
    63
icculus@10575
    64
    for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 2) {
icculus@10575
    65
        dst[0] = src[0];
icculus@10575
    66
        dst[1] = src[1];
icculus@1982
    67
    }
slouken@942
    68
slouken@1895
    69
    cvt->len_cvt /= 3;
slouken@1895
    70
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
    71
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
    72
    }
slouken@942
    73
}
slouken@942
    74
slouken@942
    75
slouken@942
    76
/* Discard top 2 channels of 6 */
icculus@1982
    77
static void SDLCALL
icculus@1982
    78
SDL_ConvertStrip_2(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
    79
{
icculus@10575
    80
    float *dst = (float *) cvt->buf;
icculus@10575
    81
    const float *src = dst;
slouken@1895
    82
    int i;
slouken@942
    83
icculus@10575
    84
    LOG_DEBUG_CONVERT("6 channels", "quad");
icculus@10575
    85
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
    86
icculus@10575
    87
    for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 4) {
icculus@10575
    88
        dst[0] = src[0];
icculus@10575
    89
        dst[1] = src[1];
icculus@10575
    90
        dst[2] = src[2];
icculus@10575
    91
        dst[3] = src[3];
icculus@1982
    92
    }
slouken@942
    93
icculus@1982
    94
    cvt->len_cvt /= 6;
icculus@1982
    95
    cvt->len_cvt *= 4;
slouken@1895
    96
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
    97
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
    98
    }
slouken@942
    99
}
slouken@0
   100
slouken@0
   101
/* Duplicate a mono channel to both stereo channels */
icculus@1982
   102
static void SDLCALL
icculus@1982
   103
SDL_ConvertStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@0
   104
{
icculus@10575
   105
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@10575
   106
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
slouken@1895
   107
    int i;
slouken@0
   108
icculus@10575
   109
    LOG_DEBUG_CONVERT("mono", "stereo");
icculus@10575
   110
    SDL_assert(format == AUDIO_F32SYS);
slouken@0
   111
icculus@10575
   112
    for (i = cvt->len_cvt / sizeof (float); i; --i) {
icculus@10575
   113
        src--;
icculus@10575
   114
        dst -= 2;
icculus@10575
   115
        dst[0] = dst[1] = *src;
icculus@1982
   116
    }
slouken@0
   117
slouken@1895
   118
    cvt->len_cvt *= 2;
slouken@1895
   119
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   120
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   121
    }
slouken@0
   122
}
slouken@0
   123
slouken@942
   124
slouken@942
   125
/* Duplicate a stereo channel to a pseudo-5.1 stream */
icculus@1982
   126
static void SDLCALL
icculus@1982
   127
SDL_ConvertSurround(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
   128
{
slouken@1895
   129
    int i;
icculus@10575
   130
    float lf, rf, ce;
icculus@10575
   131
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@10575
   132
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 3);
slouken@942
   133
icculus@10575
   134
    LOG_DEBUG_CONVERT("stereo", "5.1");
icculus@10575
   135
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
   136
icculus@10575
   137
    for (i = cvt->len_cvt / 8; i; --i) {
icculus@10575
   138
        dst -= 6;
icculus@10575
   139
        src -= 2;
icculus@10575
   140
        lf = src[0];
icculus@10575
   141
        rf = src[1];
icculus@10575
   142
        ce = (lf * 0.5f) + (rf * 0.5f);
icculus@10575
   143
        dst[0] = src[0];
icculus@10575
   144
        dst[1] = src[1];
icculus@10575
   145
        dst[2] = lf - ce;
icculus@10575
   146
        dst[3] = rf - ce;
icculus@10575
   147
        dst[4] = dst[5] = ce;
icculus@10575
   148
    }
slouken@942
   149
slouken@1895
   150
    cvt->len_cvt *= 3;
slouken@1895
   151
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   152
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   153
    }
slouken@942
   154
}
slouken@942
   155
slouken@942
   156
slouken@942
   157
/* Duplicate a stereo channel to a pseudo-4.0 stream */
icculus@1982
   158
static void SDLCALL
icculus@1982
   159
SDL_ConvertSurround_4(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
   160
{
icculus@10575
   161
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@10575
   162
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
icculus@10575
   163
    float lf, rf, ce;
slouken@1895
   164
    int i;
slouken@942
   165
icculus@10575
   166
    LOG_DEBUG_CONVERT("stereo", "quad");
icculus@10575
   167
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
   168
icculus@10575
   169
    for (i = cvt->len_cvt / 8; i; --i) {
icculus@10575
   170
        dst -= 4;
icculus@10575
   171
        src -= 2;
icculus@10575
   172
        lf = src[0];
icculus@10575
   173
        rf = src[1];
icculus@10575
   174
        ce = (lf / 2) + (rf / 2);
icculus@10575
   175
        dst[0] = src[0];
icculus@10575
   176
        dst[1] = src[1];
icculus@10575
   177
        dst[2] = lf - ce;
icculus@10575
   178
        dst[3] = rf - ce;
icculus@10575
   179
    }
slouken@942
   180
slouken@1895
   181
    cvt->len_cvt *= 2;
slouken@1895
   182
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   183
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   184
    }
slouken@0
   185
}
slouken@0
   186
slouken@0
   187
slouken@1895
   188
int
slouken@1895
   189
SDL_ConvertAudio(SDL_AudioCVT * cvt)
slouken@0
   190
{
icculus@3021
   191
    /* !!! FIXME: (cvt) should be const; stack-copy it here. */
icculus@3021
   192
    /* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */
icculus@3021
   193
slouken@1895
   194
    /* Make sure there's data to convert */
slouken@1895
   195
    if (cvt->buf == NULL) {
icculus@10575
   196
        return SDL_SetError("No buffer allocated for conversion");
slouken@1895
   197
    }
icculus@10575
   198
slouken@1895
   199
    /* Return okay if no conversion is necessary */
slouken@1895
   200
    cvt->len_cvt = cvt->len;
slouken@1895
   201
    if (cvt->filters[0] == NULL) {
icculus@10575
   202
        return 0;
slouken@1895
   203
    }
slouken@0
   204
slouken@1895
   205
    /* Set up the conversion and go! */
slouken@1895
   206
    cvt->filter_index = 0;
slouken@1895
   207
    cvt->filters[0] (cvt, cvt->src_format);
icculus@10575
   208
    return 0;
slouken@0
   209
}
slouken@0
   210
icculus@10575
   211
static void SDLCALL
icculus@10575
   212
SDL_Convert_Byteswap(SDL_AudioCVT *cvt, SDL_AudioFormat format)
icculus@10575
   213
{
slouken@10579
   214
#if DEBUG_CONVERT
slouken@10579
   215
    printf("Converting byte order\n");
slouken@10579
   216
#endif
icculus@1982
   217
icculus@10575
   218
    switch (SDL_AUDIO_BITSIZE(format)) {
icculus@10575
   219
        #define CASESWAP(b) \
icculus@10575
   220
            case b: { \
icculus@10575
   221
                Uint##b *ptr = (Uint##b *) cvt->buf; \
icculus@10575
   222
                int i; \
icculus@10575
   223
                for (i = cvt->len_cvt / sizeof (*ptr); i; --i, ++ptr) { \
icculus@10575
   224
                    *ptr = SDL_Swap##b(*ptr); \
icculus@10575
   225
                } \
icculus@10575
   226
                break; \
icculus@10575
   227
            }
icculus@1982
   228
icculus@10575
   229
        CASESWAP(16);
icculus@10575
   230
        CASESWAP(32);
icculus@10575
   231
        CASESWAP(64);
icculus@10575
   232
icculus@10575
   233
        #undef CASESWAP
icculus@10575
   234
icculus@10575
   235
        default: SDL_assert(!"unhandled byteswap datatype!"); break;
icculus@10575
   236
    }
icculus@10575
   237
icculus@10575
   238
    if (cvt->filters[++cvt->filter_index]) {
icculus@10575
   239
        /* flip endian flag for data. */
icculus@10575
   240
        if (format & SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   241
            format &= ~SDL_AUDIO_MASK_ENDIAN;
icculus@10575
   242
        } else {
icculus@10575
   243
            format |= SDL_AUDIO_MASK_ENDIAN;
icculus@10575
   244
        }
icculus@10575
   245
        cvt->filters[cvt->filter_index](cvt, format);
icculus@10575
   246
    }
icculus@1982
   247
}
icculus@1982
   248
icculus@1982
   249
icculus@1982
   250
static int
icculus@10575
   251
SDL_BuildAudioTypeCVTToFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat src_fmt)
icculus@1982
   252
{
icculus@10575
   253
    int retval = 0;  /* 0 == no conversion necessary. */
icculus@1982
   254
icculus@10575
   255
    if ((SDL_AUDIO_ISBIGENDIAN(src_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
icculus@10575
   256
        cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
icculus@10575
   257
        retval = 1;  /* added a converter. */
icculus@10575
   258
    }
icculus@1982
   259
icculus@10575
   260
    if (!SDL_AUDIO_ISFLOAT(src_fmt)) {
icculus@10576
   261
        const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt);
icculus@10576
   262
        const Uint16 dst_bitsize = 32;
icculus@10575
   263
        SDL_AudioFilter filter = NULL;
icculus@10576
   264
icculus@10575
   265
        switch (src_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   266
            case AUDIO_S8: filter = SDL_Convert_S8_to_F32; break;
icculus@10575
   267
            case AUDIO_U8: filter = SDL_Convert_U8_to_F32; break;
icculus@10575
   268
            case AUDIO_S16: filter = SDL_Convert_S16_to_F32; break;
philipp@10591
   269
            case AUDIO_U16: filter = SDL_Convert_U16_to_F32; break;
icculus@10575
   270
            case AUDIO_S32: filter = SDL_Convert_S32_to_F32; break;
icculus@10575
   271
            default: SDL_assert(!"Unexpected audio format!"); break;
icculus@1982
   272
        }
icculus@1982
   273
icculus@10575
   274
        if (!filter) {
icculus@10575
   275
            return SDL_SetError("No conversion available for these formats");
icculus@10575
   276
        }
icculus@10575
   277
icculus@1982
   278
        cvt->filters[cvt->filter_index++] = filter;
icculus@1982
   279
        if (src_bitsize < dst_bitsize) {
icculus@1982
   280
            const int mult = (dst_bitsize / src_bitsize);
icculus@1982
   281
            cvt->len_mult *= mult;
icculus@1982
   282
            cvt->len_ratio *= mult;
icculus@1982
   283
        } else if (src_bitsize > dst_bitsize) {
icculus@1982
   284
            cvt->len_ratio /= (src_bitsize / dst_bitsize);
icculus@1982
   285
        }
icculus@10576
   286
icculus@10575
   287
        retval = 1;  /* added a converter. */
icculus@1982
   288
    }
icculus@1982
   289
icculus@10575
   290
    return retval;
icculus@1982
   291
}
icculus@1982
   292
icculus@10575
   293
static int
icculus@10575
   294
SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
icculus@10575
   295
{
icculus@10575
   296
    int retval = 0;  /* 0 == no conversion necessary. */
icculus@3021
   297
icculus@10575
   298
    if (!SDL_AUDIO_ISFLOAT(dst_fmt)) {
icculus@10577
   299
        const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt);
icculus@10577
   300
        const Uint16 src_bitsize = 32;
icculus@10575
   301
        SDL_AudioFilter filter = NULL;
icculus@10575
   302
        switch (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   303
            case AUDIO_S8: filter = SDL_Convert_F32_to_S8; break;
icculus@10575
   304
            case AUDIO_U8: filter = SDL_Convert_F32_to_U8; break;
icculus@10575
   305
            case AUDIO_S16: filter = SDL_Convert_F32_to_S16; break;
philipp@10591
   306
            case AUDIO_U16: filter = SDL_Convert_F32_to_U16; break;
icculus@10575
   307
            case AUDIO_S32: filter = SDL_Convert_F32_to_S32; break;
icculus@10575
   308
            default: SDL_assert(!"Unexpected audio format!"); break;
icculus@10575
   309
        }
slouken@2716
   310
icculus@10575
   311
        if (!filter) {
icculus@10575
   312
            return SDL_SetError("No conversion available for these formats");
icculus@10575
   313
        }
icculus@10575
   314
icculus@10575
   315
        cvt->filters[cvt->filter_index++] = filter;
icculus@10575
   316
        if (src_bitsize < dst_bitsize) {
icculus@10575
   317
            const int mult = (dst_bitsize / src_bitsize);
icculus@10575
   318
            cvt->len_mult *= mult;
icculus@10575
   319
            cvt->len_ratio *= mult;
icculus@10575
   320
        } else if (src_bitsize > dst_bitsize) {
icculus@10575
   321
            cvt->len_ratio /= (src_bitsize / dst_bitsize);
icculus@10575
   322
        }
icculus@10575
   323
        retval = 1;  /* added a converter. */
icculus@10575
   324
    }
icculus@10575
   325
icculus@10575
   326
    if ((SDL_AUDIO_ISBIGENDIAN(dst_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
icculus@10575
   327
        cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
icculus@10575
   328
        retval = 1;  /* added a converter. */
icculus@10575
   329
    }
icculus@10575
   330
icculus@10575
   331
    return retval;
icculus@3021
   332
}
slouken@2716
   333
icculus@3021
   334
static int
icculus@3021
   335
SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate)
icculus@3021
   336
{
icculus@3021
   337
    int retval = 0;
slouken@2716
   338
icculus@3021
   339
    /* If we only built with the arbitrary resamplers, ignore multiples. */
icculus@3021
   340
    int lo, hi;
icculus@3021
   341
    int div;
slouken@2716
   342
icculus@6281
   343
    SDL_assert(src_rate != 0);
icculus@6281
   344
    SDL_assert(dst_rate != 0);
icculus@6281
   345
    SDL_assert(src_rate != dst_rate);
slouken@2716
   346
icculus@3021
   347
    if (src_rate < dst_rate) {
icculus@3021
   348
        lo = src_rate;
icculus@3021
   349
        hi = dst_rate;
icculus@3021
   350
    } else {
icculus@3021
   351
        lo = dst_rate;
icculus@3021
   352
        hi = src_rate;
icculus@3021
   353
    }
slouken@2716
   354
icculus@3021
   355
    /* zero means "not a supported multiple" ... we only do 2x and 4x. */
icculus@3021
   356
    if ((hi % lo) != 0)
slouken@3040
   357
        return 0;               /* not a multiple. */
slouken@2716
   358
icculus@3021
   359
    div = hi / lo;
icculus@3021
   360
    retval = ((div == 2) || (div == 4)) ? div : 0;
icculus@3021
   361
icculus@3021
   362
    return retval;
icculus@3021
   363
}
icculus@3021
   364
icculus@10575
   365
#define RESAMPLER_FUNCS(chans) \
icculus@10575
   366
    static void SDLCALL \
icculus@10575
   367
    SDL_Upsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10575
   368
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10575
   369
        SDL_Upsample_Arbitrary(cvt, chans); \
icculus@10575
   370
    }\
icculus@10575
   371
    static void SDLCALL \
icculus@10575
   372
    SDL_Downsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10575
   373
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10575
   374
        SDL_Downsample_Arbitrary(cvt, chans); \
icculus@10575
   375
    } \
icculus@10575
   376
    static void SDLCALL \
icculus@10575
   377
    SDL_Upsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10575
   378
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10575
   379
        SDL_Upsample_x2(cvt, chans); \
icculus@10575
   380
    } \
icculus@10575
   381
    static void SDLCALL \
icculus@10575
   382
    SDL_Downsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10575
   383
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10575
   384
        SDL_Downsample_Multiple(cvt, 2, chans); \
icculus@10575
   385
    } \
icculus@10575
   386
    static void SDLCALL \
icculus@10575
   387
    SDL_Upsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10575
   388
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10575
   389
        SDL_Upsample_x4(cvt, chans); \
icculus@10575
   390
    } \
icculus@10575
   391
    static void SDLCALL \
icculus@10575
   392
    SDL_Downsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10575
   393
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10575
   394
        SDL_Downsample_Multiple(cvt, 4, chans); \
icculus@10575
   395
    }
icculus@10575
   396
RESAMPLER_FUNCS(1)
icculus@10575
   397
RESAMPLER_FUNCS(2)
icculus@10575
   398
RESAMPLER_FUNCS(4)
icculus@10575
   399
RESAMPLER_FUNCS(6)
icculus@10575
   400
RESAMPLER_FUNCS(8)
icculus@10575
   401
#undef RESAMPLER_FUNCS
icculus@10575
   402
icculus@3021
   403
static int
icculus@3021
   404
SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, int dst_channels,
icculus@3021
   405
                          int src_rate, int dst_rate)
icculus@3021
   406
{
icculus@3021
   407
    if (src_rate != dst_rate) {
icculus@10575
   408
        const int upsample = (src_rate < dst_rate) ? 1 : 0;
icculus@10575
   409
        const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate);
icculus@10575
   410
        SDL_AudioFilter filter = NULL;
icculus@3021
   411
icculus@10575
   412
        #define PICK_CHANNEL_FILTER(upordown, resampler) switch (dst_channels) { \
icculus@10575
   413
            case 1: filter = SDL_##upordown##_##resampler##_c1; break; \
icculus@10575
   414
            case 2: filter = SDL_##upordown##_##resampler##_c2; break; \
icculus@10575
   415
            case 4: filter = SDL_##upordown##_##resampler##_c4; break; \
icculus@10575
   416
            case 6: filter = SDL_##upordown##_##resampler##_c6; break; \
icculus@10575
   417
            case 8: filter = SDL_##upordown##_##resampler##_c8; break; \
icculus@10575
   418
            default: break; \
icculus@10575
   419
        }
icculus@10575
   420
icculus@10575
   421
        if (upsample) {
icculus@10575
   422
            if (multiple == 0) {
icculus@10575
   423
                PICK_CHANNEL_FILTER(Upsample, Arbitrary);
icculus@10575
   424
            } else if (multiple == 2) {
icculus@10575
   425
                PICK_CHANNEL_FILTER(Upsample, x2);
icculus@10575
   426
            } else if (multiple == 4) {
icculus@10575
   427
                PICK_CHANNEL_FILTER(Upsample, x4);
icculus@10575
   428
            }
icculus@10575
   429
        } else {
icculus@10575
   430
            if (multiple == 0) {
icculus@10575
   431
                PICK_CHANNEL_FILTER(Downsample, Arbitrary);
icculus@10575
   432
            } else if (multiple == 2) {
icculus@10575
   433
                PICK_CHANNEL_FILTER(Downsample, x2);
icculus@10575
   434
            } else if (multiple == 4) {
icculus@10575
   435
                PICK_CHANNEL_FILTER(Downsample, x4);
icculus@10575
   436
            }
icculus@10575
   437
        }
icculus@10575
   438
icculus@10575
   439
        #undef PICK_CHANNEL_FILTER
icculus@10575
   440
icculus@3021
   441
        if (filter == NULL) {
icculus@10575
   442
            return SDL_SetError("No conversion available for these rates");
slouken@2716
   443
        }
slouken@2716
   444
icculus@3021
   445
        /* Update (cvt) with filter details... */
icculus@3021
   446
        cvt->filters[cvt->filter_index++] = filter;
icculus@3021
   447
        if (src_rate < dst_rate) {
icculus@3021
   448
            const double mult = ((double) dst_rate) / ((double) src_rate);
slouken@3032
   449
            cvt->len_mult *= (int) SDL_ceil(mult);
icculus@3021
   450
            cvt->len_ratio *= mult;
icculus@3021
   451
        } else {
icculus@3021
   452
            cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
slouken@2716
   453
        }
icculus@3021
   454
icculus@3021
   455
        return 1;               /* added a converter. */
slouken@2716
   456
    }
slouken@2716
   457
icculus@3021
   458
    return 0;                   /* no conversion necessary. */
slouken@2716
   459
}
icculus@1982
   460
icculus@1982
   461
icculus@1982
   462
/* Creates a set of audio filters to convert from one format to another.
icculus@1982
   463
   Returns -1 if the format conversion is not supported, 0 if there's
icculus@1982
   464
   no conversion needed, or 1 if the audio filter is set up.
slouken@0
   465
*/
slouken@1895
   466
slouken@1895
   467
int
slouken@1895
   468
SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
icculus@1982
   469
                  SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate,
icculus@1982
   470
                  SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate)
slouken@0
   471
{
aschiffler@6819
   472
    /* Sanity check target pointer */
aschiffler@6819
   473
    if (cvt == NULL) {
icculus@7037
   474
        return SDL_InvalidParamError("cvt");
aschiffler@6819
   475
    }
slouken@7191
   476
slouken@3491
   477
    /* there are no unsigned types over 16 bits, so catch this up front. */
icculus@1982
   478
    if ((SDL_AUDIO_BITSIZE(src_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(src_fmt))) {
icculus@7037
   479
        return SDL_SetError("Invalid source format");
icculus@1982
   480
    }
icculus@1982
   481
    if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) {
icculus@7037
   482
        return SDL_SetError("Invalid destination format");
icculus@1982
   483
    }
icculus@3021
   484
icculus@3021
   485
    /* prevent possible divisions by zero, etc. */
aschiffler@6819
   486
    if ((src_channels == 0) || (dst_channels == 0)) {
icculus@7037
   487
        return SDL_SetError("Source or destination channels is zero");
aschiffler@6819
   488
    }
icculus@3021
   489
    if ((src_rate == 0) || (dst_rate == 0)) {
icculus@7037
   490
        return SDL_SetError("Source or destination rate is zero");
icculus@3021
   491
    }
slouken@10579
   492
#if DEBUG_CONVERT
icculus@1982
   493
    printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n",
slouken@1985
   494
           src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate);
slouken@1985
   495
#endif
icculus@1982
   496
slouken@1895
   497
    /* Start off with no conversion necessary */
icculus@2879
   498
    SDL_zerop(cvt);
icculus@1982
   499
    cvt->src_format = src_fmt;
icculus@1982
   500
    cvt->dst_format = dst_fmt;
slouken@1895
   501
    cvt->needed = 0;
slouken@1895
   502
    cvt->filter_index = 0;
slouken@1895
   503
    cvt->filters[0] = NULL;
slouken@1895
   504
    cvt->len_mult = 1;
slouken@1895
   505
    cvt->len_ratio = 1.0;
icculus@3021
   506
    cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);
slouken@0
   507
icculus@10575
   508
    /* Type conversion goes like this now:
icculus@10575
   509
        - byteswap to CPU native format first if necessary.
icculus@10575
   510
        - convert to native Float32 if necessary.
icculus@10575
   511
        - resample and change channel count if necessary.
icculus@10575
   512
        - convert back to native format.
icculus@10575
   513
        - byteswap back to foreign format if necessary.
icculus@10575
   514
icculus@10575
   515
       The expectation is we can process data faster in float32
icculus@10575
   516
       (possibly with SIMD), and making several passes over the same
icculus@10575
   517
       buffer in is likely to be CPU cache-friendly, avoiding the
icculus@10575
   518
       biggest performance hit in modern times. Previously we had
icculus@10575
   519
       (script-generated) custom converters for every data type and
icculus@10575
   520
       it was a bloat on SDL compile times and final library size. */
icculus@10575
   521
icculus@10575
   522
    /* see if we can skip float conversion entirely (just a byteswap needed). */
icculus@10575
   523
    if ((src_rate == dst_rate) && (src_channels == dst_channels) &&
icculus@10575
   524
        ((src_fmt != dst_fmt) &&
icculus@10575
   525
         ((src_fmt & ~SDL_AUDIO_MASK_ENDIAN) == (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN)))) {
icculus@10575
   526
        cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
icculus@10575
   527
        cvt->needed = 1;
icculus@10575
   528
        return 1;
icculus@10575
   529
    }
icculus@10575
   530
icculus@1982
   531
    /* Convert data types, if necessary. Updates (cvt). */
icculus@10575
   532
    if (SDL_BuildAudioTypeCVTToFloat(cvt, src_fmt) == -1) {
slouken@1985
   533
        return -1;              /* shouldn't happen, but just in case... */
icculus@3021
   534
    }
slouken@0
   535
icculus@1982
   536
    /* Channel conversion */
slouken@1895
   537
    if (src_channels != dst_channels) {
slouken@1895
   538
        if ((src_channels == 1) && (dst_channels > 1)) {
slouken@1895
   539
            cvt->filters[cvt->filter_index++] = SDL_ConvertStereo;
slouken@1895
   540
            cvt->len_mult *= 2;
slouken@1895
   541
            src_channels = 2;
slouken@1895
   542
            cvt->len_ratio *= 2;
slouken@1895
   543
        }
slouken@1895
   544
        if ((src_channels == 2) && (dst_channels == 6)) {
slouken@1895
   545
            cvt->filters[cvt->filter_index++] = SDL_ConvertSurround;
slouken@1895
   546
            src_channels = 6;
slouken@1895
   547
            cvt->len_mult *= 3;
slouken@1895
   548
            cvt->len_ratio *= 3;
slouken@1895
   549
        }
slouken@1895
   550
        if ((src_channels == 2) && (dst_channels == 4)) {
slouken@1895
   551
            cvt->filters[cvt->filter_index++] = SDL_ConvertSurround_4;
slouken@1895
   552
            src_channels = 4;
slouken@1895
   553
            cvt->len_mult *= 2;
slouken@1895
   554
            cvt->len_ratio *= 2;
slouken@1895
   555
        }
slouken@1895
   556
        while ((src_channels * 2) <= dst_channels) {
slouken@1895
   557
            cvt->filters[cvt->filter_index++] = SDL_ConvertStereo;
slouken@1895
   558
            cvt->len_mult *= 2;
slouken@1895
   559
            src_channels *= 2;
slouken@1895
   560
            cvt->len_ratio *= 2;
slouken@1895
   561
        }
slouken@1895
   562
        if ((src_channels == 6) && (dst_channels <= 2)) {
slouken@1895
   563
            cvt->filters[cvt->filter_index++] = SDL_ConvertStrip;
slouken@1895
   564
            src_channels = 2;
slouken@1895
   565
            cvt->len_ratio /= 3;
slouken@1895
   566
        }
slouken@1895
   567
        if ((src_channels == 6) && (dst_channels == 4)) {
slouken@1895
   568
            cvt->filters[cvt->filter_index++] = SDL_ConvertStrip_2;
slouken@1895
   569
            src_channels = 4;
slouken@1895
   570
            cvt->len_ratio /= 2;
slouken@1895
   571
        }
slouken@1895
   572
        /* This assumes that 4 channel audio is in the format:
slouken@1895
   573
           Left {front/back} + Right {front/back}
slouken@1895
   574
           so converting to L/R stereo works properly.
slouken@1895
   575
         */
slouken@1895
   576
        while (((src_channels % 2) == 0) &&
slouken@1895
   577
               ((src_channels / 2) >= dst_channels)) {
slouken@1895
   578
            cvt->filters[cvt->filter_index++] = SDL_ConvertMono;
slouken@1895
   579
            src_channels /= 2;
slouken@1895
   580
            cvt->len_ratio /= 2;
slouken@1895
   581
        }
slouken@1895
   582
        if (src_channels != dst_channels) {
slouken@1895
   583
            /* Uh oh.. */ ;
slouken@1895
   584
        }
slouken@1895
   585
    }
slouken@0
   586
icculus@3021
   587
    /* Do rate conversion, if necessary. Updates (cvt). */
slouken@3040
   588
    if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) ==
slouken@3040
   589
        -1) {
icculus@3021
   590
        return -1;              /* shouldn't happen, but just in case... */
slouken@2716
   591
    }
slouken@2716
   592
icculus@10575
   593
    if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) == -1) {
icculus@10575
   594
        return -1;              /* shouldn't happen, but just in case... */
slouken@1895
   595
    }
icculus@10575
   596
icculus@10575
   597
    cvt->needed = (cvt->filter_index != 0);
slouken@1895
   598
    return (cvt->needed);
slouken@0
   599
}
slouken@1895
   600
icculus@10575
   601
/* vi: set ts=4 sw=4 expandtab: */
slouken@2716
   602