src/audio/SDL_audiocvt.c
author Ryan C. Gordon <icculus@icculus.org>
Sat, 05 Nov 2016 02:34:38 -0400
changeset 10575 bb99dede0675
parent 9998 f67cf37e9cd4
child 10576 4f6ab0d2cc3b
permissions -rw-r--r--
Reworked audio converter code.

This no longer uses a script to generate code for every possible type
conversion or resampler. This caused a bloat in binary size and and compile
times. Now we use a handful of more generic functions and assume staying in
the CPU cache is the most important thing anyhow.

This shrinks the size of the final build (in this case: macOS X amd64, -Os to
optimize for size) by 15%. When compiling on a single core, build times drop
by about 15% too (although the previous cost was largely hidden by multicore
builds).
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
{
icculus@10575
   214
    #if DEBUG_CONVERT
icculus@10575
   215
    fprintf(stderr, "Converting byte order\n");
icculus@10575
   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@10575
   261
        SDL_AudioFilter filter = NULL;
icculus@10575
   262
        switch (src_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   263
            case AUDIO_S8: filter = SDL_Convert_S8_to_F32; break;
icculus@10575
   264
            case AUDIO_U8: filter = SDL_Convert_U8_to_F32; break;
icculus@10575
   265
            case AUDIO_S16: filter = SDL_Convert_S16_to_F32; break;
icculus@10575
   266
            case AUDIO_S32: filter = SDL_Convert_S32_to_F32; break;
icculus@10575
   267
            default: SDL_assert(!"Unexpected audio format!"); break;
icculus@1982
   268
        }
icculus@1982
   269
icculus@10575
   270
        if (!filter) {
icculus@10575
   271
            return SDL_SetError("No conversion available for these formats");
icculus@10575
   272
        }
icculus@10575
   273
icculus@10575
   274
        const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt);
icculus@10575
   275
        const Uint16 dst_bitsize = 32;
icculus@1982
   276
        cvt->filters[cvt->filter_index++] = filter;
icculus@1982
   277
        if (src_bitsize < dst_bitsize) {
icculus@1982
   278
            const int mult = (dst_bitsize / src_bitsize);
icculus@1982
   279
            cvt->len_mult *= mult;
icculus@1982
   280
            cvt->len_ratio *= mult;
icculus@1982
   281
        } else if (src_bitsize > dst_bitsize) {
icculus@1982
   282
            cvt->len_ratio /= (src_bitsize / dst_bitsize);
icculus@1982
   283
        }
icculus@10575
   284
        retval = 1;  /* added a converter. */
icculus@1982
   285
    }
icculus@1982
   286
icculus@10575
   287
    return retval;
icculus@1982
   288
}
icculus@1982
   289
icculus@10575
   290
static int
icculus@10575
   291
SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
icculus@10575
   292
{
icculus@10575
   293
    int retval = 0;  /* 0 == no conversion necessary. */
icculus@3021
   294
icculus@10575
   295
    if (!SDL_AUDIO_ISFLOAT(dst_fmt)) {
icculus@10575
   296
        SDL_AudioFilter filter = NULL;
icculus@10575
   297
        switch (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   298
            case AUDIO_S8: filter = SDL_Convert_F32_to_S8; break;
icculus@10575
   299
            case AUDIO_U8: filter = SDL_Convert_F32_to_U8; break;
icculus@10575
   300
            case AUDIO_S16: filter = SDL_Convert_F32_to_S16; break;
icculus@10575
   301
            case AUDIO_S32: filter = SDL_Convert_F32_to_S32; break;
icculus@10575
   302
            default: SDL_assert(!"Unexpected audio format!"); break;
icculus@10575
   303
        }
slouken@2716
   304
icculus@10575
   305
        if (!filter) {
icculus@10575
   306
            return SDL_SetError("No conversion available for these formats");
icculus@10575
   307
        }
icculus@10575
   308
icculus@10575
   309
        const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt);
icculus@10575
   310
        const Uint16 src_bitsize = 32;
icculus@10575
   311
        cvt->filters[cvt->filter_index++] = filter;
icculus@10575
   312
        if (src_bitsize < dst_bitsize) {
icculus@10575
   313
            const int mult = (dst_bitsize / src_bitsize);
icculus@10575
   314
            cvt->len_mult *= mult;
icculus@10575
   315
            cvt->len_ratio *= mult;
icculus@10575
   316
        } else if (src_bitsize > dst_bitsize) {
icculus@10575
   317
            cvt->len_ratio /= (src_bitsize / dst_bitsize);
icculus@10575
   318
        }
icculus@10575
   319
        retval = 1;  /* added a converter. */
icculus@10575
   320
    }
icculus@10575
   321
icculus@10575
   322
    if ((SDL_AUDIO_ISBIGENDIAN(dst_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
icculus@10575
   323
        cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
icculus@10575
   324
        retval = 1;  /* added a converter. */
icculus@10575
   325
    }
icculus@10575
   326
icculus@10575
   327
    return retval;
icculus@3021
   328
}
slouken@2716
   329
icculus@3021
   330
static int
icculus@3021
   331
SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate)
icculus@3021
   332
{
icculus@3021
   333
    int retval = 0;
slouken@2716
   334
icculus@3021
   335
    /* If we only built with the arbitrary resamplers, ignore multiples. */
icculus@3021
   336
    int lo, hi;
icculus@3021
   337
    int div;
slouken@2716
   338
icculus@6281
   339
    SDL_assert(src_rate != 0);
icculus@6281
   340
    SDL_assert(dst_rate != 0);
icculus@6281
   341
    SDL_assert(src_rate != dst_rate);
slouken@2716
   342
icculus@3021
   343
    if (src_rate < dst_rate) {
icculus@3021
   344
        lo = src_rate;
icculus@3021
   345
        hi = dst_rate;
icculus@3021
   346
    } else {
icculus@3021
   347
        lo = dst_rate;
icculus@3021
   348
        hi = src_rate;
icculus@3021
   349
    }
slouken@2716
   350
icculus@3021
   351
    /* zero means "not a supported multiple" ... we only do 2x and 4x. */
icculus@3021
   352
    if ((hi % lo) != 0)
slouken@3040
   353
        return 0;               /* not a multiple. */
slouken@2716
   354
icculus@3021
   355
    div = hi / lo;
icculus@3021
   356
    retval = ((div == 2) || (div == 4)) ? div : 0;
icculus@3021
   357
icculus@3021
   358
    return retval;
icculus@3021
   359
}
icculus@3021
   360
icculus@10575
   361
#define RESAMPLER_FUNCS(chans) \
icculus@10575
   362
    static void SDLCALL \
icculus@10575
   363
    SDL_Upsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10575
   364
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10575
   365
        SDL_Upsample_Arbitrary(cvt, chans); \
icculus@10575
   366
    }\
icculus@10575
   367
    static void SDLCALL \
icculus@10575
   368
    SDL_Downsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10575
   369
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10575
   370
        SDL_Downsample_Arbitrary(cvt, chans); \
icculus@10575
   371
    } \
icculus@10575
   372
    static void SDLCALL \
icculus@10575
   373
    SDL_Upsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10575
   374
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10575
   375
        SDL_Upsample_x2(cvt, chans); \
icculus@10575
   376
    } \
icculus@10575
   377
    static void SDLCALL \
icculus@10575
   378
    SDL_Downsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10575
   379
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10575
   380
        SDL_Downsample_Multiple(cvt, 2, chans); \
icculus@10575
   381
    } \
icculus@10575
   382
    static void SDLCALL \
icculus@10575
   383
    SDL_Upsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10575
   384
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10575
   385
        SDL_Upsample_x4(cvt, chans); \
icculus@10575
   386
    } \
icculus@10575
   387
    static void SDLCALL \
icculus@10575
   388
    SDL_Downsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10575
   389
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10575
   390
        SDL_Downsample_Multiple(cvt, 4, chans); \
icculus@10575
   391
    }
icculus@10575
   392
RESAMPLER_FUNCS(1)
icculus@10575
   393
RESAMPLER_FUNCS(2)
icculus@10575
   394
RESAMPLER_FUNCS(4)
icculus@10575
   395
RESAMPLER_FUNCS(6)
icculus@10575
   396
RESAMPLER_FUNCS(8)
icculus@10575
   397
#undef RESAMPLER_FUNCS
icculus@10575
   398
icculus@3021
   399
static int
icculus@3021
   400
SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, int dst_channels,
icculus@3021
   401
                          int src_rate, int dst_rate)
icculus@3021
   402
{
icculus@3021
   403
    if (src_rate != dst_rate) {
icculus@10575
   404
        const int upsample = (src_rate < dst_rate) ? 1 : 0;
icculus@10575
   405
        const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate);
icculus@10575
   406
        SDL_AudioFilter filter = NULL;
icculus@3021
   407
icculus@10575
   408
        #define PICK_CHANNEL_FILTER(upordown, resampler) switch (dst_channels) { \
icculus@10575
   409
            case 1: filter = SDL_##upordown##_##resampler##_c1; break; \
icculus@10575
   410
            case 2: filter = SDL_##upordown##_##resampler##_c2; break; \
icculus@10575
   411
            case 4: filter = SDL_##upordown##_##resampler##_c4; break; \
icculus@10575
   412
            case 6: filter = SDL_##upordown##_##resampler##_c6; break; \
icculus@10575
   413
            case 8: filter = SDL_##upordown##_##resampler##_c8; break; \
icculus@10575
   414
            default: break; \
icculus@10575
   415
        }
icculus@10575
   416
icculus@10575
   417
        if (upsample) {
icculus@10575
   418
            if (multiple == 0) {
icculus@10575
   419
                PICK_CHANNEL_FILTER(Upsample, Arbitrary);
icculus@10575
   420
            } else if (multiple == 2) {
icculus@10575
   421
                PICK_CHANNEL_FILTER(Upsample, x2);
icculus@10575
   422
            } else if (multiple == 4) {
icculus@10575
   423
                PICK_CHANNEL_FILTER(Upsample, x4);
icculus@10575
   424
            }
icculus@10575
   425
        } else {
icculus@10575
   426
            if (multiple == 0) {
icculus@10575
   427
                PICK_CHANNEL_FILTER(Downsample, Arbitrary);
icculus@10575
   428
            } else if (multiple == 2) {
icculus@10575
   429
                PICK_CHANNEL_FILTER(Downsample, x2);
icculus@10575
   430
            } else if (multiple == 4) {
icculus@10575
   431
                PICK_CHANNEL_FILTER(Downsample, x4);
icculus@10575
   432
            }
icculus@10575
   433
        }
icculus@10575
   434
icculus@10575
   435
        #undef PICK_CHANNEL_FILTER
icculus@10575
   436
icculus@3021
   437
        if (filter == NULL) {
icculus@10575
   438
            return SDL_SetError("No conversion available for these rates");
slouken@2716
   439
        }
slouken@2716
   440
icculus@3021
   441
        /* Update (cvt) with filter details... */
icculus@3021
   442
        cvt->filters[cvt->filter_index++] = filter;
icculus@3021
   443
        if (src_rate < dst_rate) {
icculus@3021
   444
            const double mult = ((double) dst_rate) / ((double) src_rate);
slouken@3032
   445
            cvt->len_mult *= (int) SDL_ceil(mult);
icculus@3021
   446
            cvt->len_ratio *= mult;
icculus@3021
   447
        } else {
icculus@3021
   448
            cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
slouken@2716
   449
        }
icculus@3021
   450
icculus@3021
   451
        return 1;               /* added a converter. */
slouken@2716
   452
    }
slouken@2716
   453
icculus@3021
   454
    return 0;                   /* no conversion necessary. */
slouken@2716
   455
}
icculus@1982
   456
icculus@1982
   457
icculus@1982
   458
/* Creates a set of audio filters to convert from one format to another.
icculus@1982
   459
   Returns -1 if the format conversion is not supported, 0 if there's
icculus@1982
   460
   no conversion needed, or 1 if the audio filter is set up.
slouken@0
   461
*/
slouken@1895
   462
slouken@1895
   463
int
slouken@1895
   464
SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
icculus@1982
   465
                  SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate,
icculus@1982
   466
                  SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate)
slouken@0
   467
{
aschiffler@6819
   468
    /* Sanity check target pointer */
aschiffler@6819
   469
    if (cvt == NULL) {
icculus@7037
   470
        return SDL_InvalidParamError("cvt");
aschiffler@6819
   471
    }
slouken@7191
   472
slouken@3491
   473
    /* there are no unsigned types over 16 bits, so catch this up front. */
icculus@1982
   474
    if ((SDL_AUDIO_BITSIZE(src_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(src_fmt))) {
icculus@7037
   475
        return SDL_SetError("Invalid source format");
icculus@1982
   476
    }
icculus@1982
   477
    if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) {
icculus@7037
   478
        return SDL_SetError("Invalid destination format");
icculus@1982
   479
    }
icculus@3021
   480
icculus@3021
   481
    /* prevent possible divisions by zero, etc. */
aschiffler@6819
   482
    if ((src_channels == 0) || (dst_channels == 0)) {
icculus@7037
   483
        return SDL_SetError("Source or destination channels is zero");
aschiffler@6819
   484
    }
icculus@3021
   485
    if ((src_rate == 0) || (dst_rate == 0)) {
icculus@7037
   486
        return SDL_SetError("Source or destination rate is zero");
icculus@3021
   487
    }
slouken@1985
   488
#ifdef DEBUG_CONVERT
icculus@1982
   489
    printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n",
slouken@1985
   490
           src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate);
slouken@1985
   491
#endif
icculus@1982
   492
slouken@1895
   493
    /* Start off with no conversion necessary */
icculus@2879
   494
    SDL_zerop(cvt);
icculus@1982
   495
    cvt->src_format = src_fmt;
icculus@1982
   496
    cvt->dst_format = dst_fmt;
slouken@1895
   497
    cvt->needed = 0;
slouken@1895
   498
    cvt->filter_index = 0;
slouken@1895
   499
    cvt->filters[0] = NULL;
slouken@1895
   500
    cvt->len_mult = 1;
slouken@1895
   501
    cvt->len_ratio = 1.0;
icculus@3021
   502
    cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);
slouken@0
   503
icculus@10575
   504
    /* Type conversion goes like this now:
icculus@10575
   505
        - byteswap to CPU native format first if necessary.
icculus@10575
   506
        - convert to native Float32 if necessary.
icculus@10575
   507
        - resample and change channel count if necessary.
icculus@10575
   508
        - convert back to native format.
icculus@10575
   509
        - byteswap back to foreign format if necessary.
icculus@10575
   510
icculus@10575
   511
       The expectation is we can process data faster in float32
icculus@10575
   512
       (possibly with SIMD), and making several passes over the same
icculus@10575
   513
       buffer in is likely to be CPU cache-friendly, avoiding the
icculus@10575
   514
       biggest performance hit in modern times. Previously we had
icculus@10575
   515
       (script-generated) custom converters for every data type and
icculus@10575
   516
       it was a bloat on SDL compile times and final library size. */
icculus@10575
   517
icculus@10575
   518
    /* see if we can skip float conversion entirely (just a byteswap needed). */
icculus@10575
   519
    if ((src_rate == dst_rate) && (src_channels == dst_channels) &&
icculus@10575
   520
        ((src_fmt != dst_fmt) &&
icculus@10575
   521
         ((src_fmt & ~SDL_AUDIO_MASK_ENDIAN) == (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN)))) {
icculus@10575
   522
        cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
icculus@10575
   523
        cvt->needed = 1;
icculus@10575
   524
        return 1;
icculus@10575
   525
    }
icculus@10575
   526
icculus@1982
   527
    /* Convert data types, if necessary. Updates (cvt). */
icculus@10575
   528
    if (SDL_BuildAudioTypeCVTToFloat(cvt, src_fmt) == -1) {
slouken@1985
   529
        return -1;              /* shouldn't happen, but just in case... */
icculus@3021
   530
    }
slouken@0
   531
icculus@1982
   532
    /* Channel conversion */
slouken@1895
   533
    if (src_channels != dst_channels) {
slouken@1895
   534
        if ((src_channels == 1) && (dst_channels > 1)) {
slouken@1895
   535
            cvt->filters[cvt->filter_index++] = SDL_ConvertStereo;
slouken@1895
   536
            cvt->len_mult *= 2;
slouken@1895
   537
            src_channels = 2;
slouken@1895
   538
            cvt->len_ratio *= 2;
slouken@1895
   539
        }
slouken@1895
   540
        if ((src_channels == 2) && (dst_channels == 6)) {
slouken@1895
   541
            cvt->filters[cvt->filter_index++] = SDL_ConvertSurround;
slouken@1895
   542
            src_channels = 6;
slouken@1895
   543
            cvt->len_mult *= 3;
slouken@1895
   544
            cvt->len_ratio *= 3;
slouken@1895
   545
        }
slouken@1895
   546
        if ((src_channels == 2) && (dst_channels == 4)) {
slouken@1895
   547
            cvt->filters[cvt->filter_index++] = SDL_ConvertSurround_4;
slouken@1895
   548
            src_channels = 4;
slouken@1895
   549
            cvt->len_mult *= 2;
slouken@1895
   550
            cvt->len_ratio *= 2;
slouken@1895
   551
        }
slouken@1895
   552
        while ((src_channels * 2) <= dst_channels) {
slouken@1895
   553
            cvt->filters[cvt->filter_index++] = SDL_ConvertStereo;
slouken@1895
   554
            cvt->len_mult *= 2;
slouken@1895
   555
            src_channels *= 2;
slouken@1895
   556
            cvt->len_ratio *= 2;
slouken@1895
   557
        }
slouken@1895
   558
        if ((src_channels == 6) && (dst_channels <= 2)) {
slouken@1895
   559
            cvt->filters[cvt->filter_index++] = SDL_ConvertStrip;
slouken@1895
   560
            src_channels = 2;
slouken@1895
   561
            cvt->len_ratio /= 3;
slouken@1895
   562
        }
slouken@1895
   563
        if ((src_channels == 6) && (dst_channels == 4)) {
slouken@1895
   564
            cvt->filters[cvt->filter_index++] = SDL_ConvertStrip_2;
slouken@1895
   565
            src_channels = 4;
slouken@1895
   566
            cvt->len_ratio /= 2;
slouken@1895
   567
        }
slouken@1895
   568
        /* This assumes that 4 channel audio is in the format:
slouken@1895
   569
           Left {front/back} + Right {front/back}
slouken@1895
   570
           so converting to L/R stereo works properly.
slouken@1895
   571
         */
slouken@1895
   572
        while (((src_channels % 2) == 0) &&
slouken@1895
   573
               ((src_channels / 2) >= dst_channels)) {
slouken@1895
   574
            cvt->filters[cvt->filter_index++] = SDL_ConvertMono;
slouken@1895
   575
            src_channels /= 2;
slouken@1895
   576
            cvt->len_ratio /= 2;
slouken@1895
   577
        }
slouken@1895
   578
        if (src_channels != dst_channels) {
slouken@1895
   579
            /* Uh oh.. */ ;
slouken@1895
   580
        }
slouken@1895
   581
    }
slouken@0
   582
icculus@3021
   583
    /* Do rate conversion, if necessary. Updates (cvt). */
slouken@3040
   584
    if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) ==
slouken@3040
   585
        -1) {
icculus@3021
   586
        return -1;              /* shouldn't happen, but just in case... */
slouken@2716
   587
    }
slouken@2716
   588
icculus@10575
   589
    if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) == -1) {
icculus@10575
   590
        return -1;              /* shouldn't happen, but just in case... */
slouken@1895
   591
    }
icculus@10575
   592
icculus@10575
   593
    cvt->needed = (cvt->filter_index != 0);
slouken@1895
   594
    return (cvt->needed);
slouken@0
   595
}
slouken@1895
   596
icculus@10575
   597
/* vi: set ts=4 sw=4 expandtab: */
slouken@2716
   598