src/audio/SDL_audiocvt.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 08 Jan 2017 14:23:15 -0500
changeset 10791 64c46e09df9d
parent 10790 92726dd20acd
child 10793 7e657de3758d
permissions -rw-r--r--
audio: Patched to compile with libsamplerate support.
slouken@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@10737
     3
  Copyright (C) 1997-2017 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
slouken@10773
    28
#include "SDL_loadso.h"
icculus@6281
    29
#include "SDL_assert.h"
icculus@10757
    30
#include "../SDL_dataqueue.h"
icculus@6281
    31
slouken@0
    32
/* Effectively mix right and left channels into a single channel */
icculus@1982
    33
static void SDLCALL
icculus@1982
    34
SDL_ConvertMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@0
    35
{
icculus@10575
    36
    float *dst = (float *) cvt->buf;
icculus@10575
    37
    const float *src = dst;
slouken@1895
    38
    int i;
slouken@0
    39
icculus@10575
    40
    LOG_DEBUG_CONVERT("stereo", "mono");
icculus@10575
    41
    SDL_assert(format == AUDIO_F32SYS);
slouken@0
    42
icculus@10575
    43
    for (i = cvt->len_cvt / 8; i; --i, src += 2) {
icculus@10575
    44
        *(dst++) = (float) ((((double) src[0]) + ((double) src[1])) * 0.5);
slouken@1895
    45
    }
icculus@1982
    46
slouken@1895
    47
    cvt->len_cvt /= 2;
slouken@1895
    48
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
    49
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
    50
    }
slouken@0
    51
}
slouken@0
    52
icculus@1982
    53
slouken@942
    54
/* Discard top 4 channels */
icculus@1982
    55
static void SDLCALL
icculus@1982
    56
SDL_ConvertStrip(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
    57
{
icculus@10575
    58
    float *dst = (float *) cvt->buf;
icculus@10575
    59
    const float *src = dst;
slouken@1895
    60
    int i;
slouken@942
    61
icculus@10575
    62
    LOG_DEBUG_CONVERT("6 channels", "stereo");
icculus@10575
    63
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
    64
icculus@10575
    65
    for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 2) {
icculus@10575
    66
        dst[0] = src[0];
icculus@10575
    67
        dst[1] = src[1];
icculus@1982
    68
    }
slouken@942
    69
slouken@1895
    70
    cvt->len_cvt /= 3;
slouken@1895
    71
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
    72
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
    73
    }
slouken@942
    74
}
slouken@942
    75
slouken@942
    76
slouken@942
    77
/* Discard top 2 channels of 6 */
icculus@1982
    78
static void SDLCALL
icculus@1982
    79
SDL_ConvertStrip_2(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
    80
{
icculus@10575
    81
    float *dst = (float *) cvt->buf;
icculus@10575
    82
    const float *src = dst;
slouken@1895
    83
    int i;
slouken@942
    84
icculus@10575
    85
    LOG_DEBUG_CONVERT("6 channels", "quad");
icculus@10575
    86
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
    87
icculus@10575
    88
    for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 4) {
icculus@10575
    89
        dst[0] = src[0];
icculus@10575
    90
        dst[1] = src[1];
icculus@10575
    91
        dst[2] = src[2];
icculus@10575
    92
        dst[3] = src[3];
icculus@1982
    93
    }
slouken@942
    94
icculus@1982
    95
    cvt->len_cvt /= 6;
icculus@1982
    96
    cvt->len_cvt *= 4;
slouken@1895
    97
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
    98
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
    99
    }
slouken@942
   100
}
slouken@0
   101
slouken@0
   102
/* Duplicate a mono channel to both stereo channels */
icculus@1982
   103
static void SDLCALL
icculus@1982
   104
SDL_ConvertStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@0
   105
{
icculus@10575
   106
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@10575
   107
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
slouken@1895
   108
    int i;
slouken@0
   109
icculus@10575
   110
    LOG_DEBUG_CONVERT("mono", "stereo");
icculus@10575
   111
    SDL_assert(format == AUDIO_F32SYS);
slouken@0
   112
icculus@10575
   113
    for (i = cvt->len_cvt / sizeof (float); i; --i) {
icculus@10575
   114
        src--;
icculus@10575
   115
        dst -= 2;
icculus@10575
   116
        dst[0] = dst[1] = *src;
icculus@1982
   117
    }
slouken@0
   118
slouken@1895
   119
    cvt->len_cvt *= 2;
slouken@1895
   120
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   121
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   122
    }
slouken@0
   123
}
slouken@0
   124
slouken@942
   125
slouken@942
   126
/* Duplicate a stereo channel to a pseudo-5.1 stream */
icculus@1982
   127
static void SDLCALL
icculus@1982
   128
SDL_ConvertSurround(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
   129
{
slouken@1895
   130
    int i;
icculus@10575
   131
    float lf, rf, ce;
icculus@10575
   132
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@10575
   133
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 3);
slouken@942
   134
icculus@10575
   135
    LOG_DEBUG_CONVERT("stereo", "5.1");
icculus@10575
   136
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
   137
icculus@10575
   138
    for (i = cvt->len_cvt / 8; i; --i) {
icculus@10575
   139
        dst -= 6;
icculus@10575
   140
        src -= 2;
icculus@10575
   141
        lf = src[0];
icculus@10575
   142
        rf = src[1];
icculus@10575
   143
        ce = (lf * 0.5f) + (rf * 0.5f);
icculus@10575
   144
        dst[0] = src[0];
icculus@10575
   145
        dst[1] = src[1];
icculus@10575
   146
        dst[2] = lf - ce;
icculus@10575
   147
        dst[3] = rf - ce;
icculus@10575
   148
        dst[4] = dst[5] = ce;
icculus@10575
   149
    }
slouken@942
   150
slouken@1895
   151
    cvt->len_cvt *= 3;
slouken@1895
   152
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   153
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   154
    }
slouken@942
   155
}
slouken@942
   156
slouken@942
   157
slouken@942
   158
/* Duplicate a stereo channel to a pseudo-4.0 stream */
icculus@1982
   159
static void SDLCALL
icculus@1982
   160
SDL_ConvertSurround_4(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
   161
{
icculus@10575
   162
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@10575
   163
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
icculus@10575
   164
    float lf, rf, ce;
slouken@1895
   165
    int i;
slouken@942
   166
icculus@10575
   167
    LOG_DEBUG_CONVERT("stereo", "quad");
icculus@10575
   168
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
   169
icculus@10575
   170
    for (i = cvt->len_cvt / 8; i; --i) {
icculus@10575
   171
        dst -= 4;
icculus@10575
   172
        src -= 2;
icculus@10575
   173
        lf = src[0];
icculus@10575
   174
        rf = src[1];
icculus@10575
   175
        ce = (lf / 2) + (rf / 2);
icculus@10575
   176
        dst[0] = src[0];
icculus@10575
   177
        dst[1] = src[1];
icculus@10575
   178
        dst[2] = lf - ce;
icculus@10575
   179
        dst[3] = rf - ce;
icculus@10575
   180
    }
slouken@942
   181
slouken@1895
   182
    cvt->len_cvt *= 2;
slouken@1895
   183
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   184
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   185
    }
slouken@0
   186
}
slouken@0
   187
slouken@0
   188
slouken@1895
   189
int
slouken@1895
   190
SDL_ConvertAudio(SDL_AudioCVT * cvt)
slouken@0
   191
{
icculus@3021
   192
    /* !!! FIXME: (cvt) should be const; stack-copy it here. */
icculus@3021
   193
    /* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */
icculus@3021
   194
slouken@1895
   195
    /* Make sure there's data to convert */
slouken@1895
   196
    if (cvt->buf == NULL) {
icculus@10575
   197
        return SDL_SetError("No buffer allocated for conversion");
slouken@1895
   198
    }
icculus@10575
   199
slouken@1895
   200
    /* Return okay if no conversion is necessary */
slouken@1895
   201
    cvt->len_cvt = cvt->len;
slouken@1895
   202
    if (cvt->filters[0] == NULL) {
icculus@10575
   203
        return 0;
slouken@1895
   204
    }
slouken@0
   205
slouken@1895
   206
    /* Set up the conversion and go! */
slouken@1895
   207
    cvt->filter_index = 0;
slouken@1895
   208
    cvt->filters[0] (cvt, cvt->src_format);
icculus@10575
   209
    return 0;
slouken@0
   210
}
slouken@0
   211
icculus@10575
   212
static void SDLCALL
icculus@10575
   213
SDL_Convert_Byteswap(SDL_AudioCVT *cvt, SDL_AudioFormat format)
icculus@10575
   214
{
slouken@10579
   215
#if DEBUG_CONVERT
slouken@10579
   216
    printf("Converting byte order\n");
slouken@10579
   217
#endif
icculus@1982
   218
icculus@10575
   219
    switch (SDL_AUDIO_BITSIZE(format)) {
icculus@10575
   220
        #define CASESWAP(b) \
icculus@10575
   221
            case b: { \
icculus@10575
   222
                Uint##b *ptr = (Uint##b *) cvt->buf; \
icculus@10575
   223
                int i; \
icculus@10575
   224
                for (i = cvt->len_cvt / sizeof (*ptr); i; --i, ++ptr) { \
icculus@10575
   225
                    *ptr = SDL_Swap##b(*ptr); \
icculus@10575
   226
                } \
icculus@10575
   227
                break; \
icculus@10575
   228
            }
icculus@1982
   229
icculus@10575
   230
        CASESWAP(16);
icculus@10575
   231
        CASESWAP(32);
icculus@10575
   232
        CASESWAP(64);
icculus@10575
   233
icculus@10575
   234
        #undef CASESWAP
icculus@10575
   235
icculus@10575
   236
        default: SDL_assert(!"unhandled byteswap datatype!"); break;
icculus@10575
   237
    }
icculus@10575
   238
icculus@10575
   239
    if (cvt->filters[++cvt->filter_index]) {
icculus@10575
   240
        /* flip endian flag for data. */
icculus@10575
   241
        if (format & SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   242
            format &= ~SDL_AUDIO_MASK_ENDIAN;
icculus@10575
   243
        } else {
icculus@10575
   244
            format |= SDL_AUDIO_MASK_ENDIAN;
icculus@10575
   245
        }
icculus@10575
   246
        cvt->filters[cvt->filter_index](cvt, format);
icculus@10575
   247
    }
icculus@1982
   248
}
icculus@1982
   249
icculus@1982
   250
icculus@1982
   251
static int
icculus@10575
   252
SDL_BuildAudioTypeCVTToFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat src_fmt)
icculus@1982
   253
{
icculus@10575
   254
    int retval = 0;  /* 0 == no conversion necessary. */
icculus@1982
   255
icculus@10575
   256
    if ((SDL_AUDIO_ISBIGENDIAN(src_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
icculus@10575
   257
        cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
icculus@10575
   258
        retval = 1;  /* added a converter. */
icculus@10575
   259
    }
icculus@1982
   260
icculus@10575
   261
    if (!SDL_AUDIO_ISFLOAT(src_fmt)) {
icculus@10576
   262
        const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt);
icculus@10576
   263
        const Uint16 dst_bitsize = 32;
icculus@10575
   264
        SDL_AudioFilter filter = NULL;
icculus@10576
   265
icculus@10575
   266
        switch (src_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   267
            case AUDIO_S8: filter = SDL_Convert_S8_to_F32; break;
icculus@10575
   268
            case AUDIO_U8: filter = SDL_Convert_U8_to_F32; break;
icculus@10575
   269
            case AUDIO_S16: filter = SDL_Convert_S16_to_F32; break;
philipp@10591
   270
            case AUDIO_U16: filter = SDL_Convert_U16_to_F32; break;
icculus@10575
   271
            case AUDIO_S32: filter = SDL_Convert_S32_to_F32; break;
icculus@10575
   272
            default: SDL_assert(!"Unexpected audio format!"); break;
icculus@1982
   273
        }
icculus@1982
   274
icculus@10575
   275
        if (!filter) {
icculus@10575
   276
            return SDL_SetError("No conversion available for these formats");
icculus@10575
   277
        }
icculus@10575
   278
icculus@1982
   279
        cvt->filters[cvt->filter_index++] = filter;
icculus@1982
   280
        if (src_bitsize < dst_bitsize) {
icculus@1982
   281
            const int mult = (dst_bitsize / src_bitsize);
icculus@1982
   282
            cvt->len_mult *= mult;
icculus@1982
   283
            cvt->len_ratio *= mult;
icculus@1982
   284
        } else if (src_bitsize > dst_bitsize) {
icculus@1982
   285
            cvt->len_ratio /= (src_bitsize / dst_bitsize);
icculus@1982
   286
        }
icculus@10576
   287
icculus@10575
   288
        retval = 1;  /* added a converter. */
icculus@1982
   289
    }
icculus@1982
   290
icculus@10575
   291
    return retval;
icculus@1982
   292
}
icculus@1982
   293
icculus@10575
   294
static int
icculus@10575
   295
SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
icculus@10575
   296
{
icculus@10575
   297
    int retval = 0;  /* 0 == no conversion necessary. */
icculus@3021
   298
icculus@10575
   299
    if (!SDL_AUDIO_ISFLOAT(dst_fmt)) {
icculus@10577
   300
        const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt);
icculus@10577
   301
        const Uint16 src_bitsize = 32;
icculus@10575
   302
        SDL_AudioFilter filter = NULL;
icculus@10575
   303
        switch (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   304
            case AUDIO_S8: filter = SDL_Convert_F32_to_S8; break;
icculus@10575
   305
            case AUDIO_U8: filter = SDL_Convert_F32_to_U8; break;
icculus@10575
   306
            case AUDIO_S16: filter = SDL_Convert_F32_to_S16; break;
philipp@10591
   307
            case AUDIO_U16: filter = SDL_Convert_F32_to_U16; break;
icculus@10575
   308
            case AUDIO_S32: filter = SDL_Convert_F32_to_S32; break;
icculus@10575
   309
            default: SDL_assert(!"Unexpected audio format!"); break;
icculus@10575
   310
        }
slouken@2716
   311
icculus@10575
   312
        if (!filter) {
icculus@10575
   313
            return SDL_SetError("No conversion available for these formats");
icculus@10575
   314
        }
icculus@10575
   315
icculus@10575
   316
        cvt->filters[cvt->filter_index++] = filter;
icculus@10575
   317
        if (src_bitsize < dst_bitsize) {
icculus@10575
   318
            const int mult = (dst_bitsize / src_bitsize);
icculus@10575
   319
            cvt->len_mult *= mult;
icculus@10575
   320
            cvt->len_ratio *= mult;
icculus@10575
   321
        } else if (src_bitsize > dst_bitsize) {
icculus@10575
   322
            cvt->len_ratio /= (src_bitsize / dst_bitsize);
icculus@10575
   323
        }
icculus@10575
   324
        retval = 1;  /* added a converter. */
icculus@10575
   325
    }
icculus@10575
   326
icculus@10575
   327
    if ((SDL_AUDIO_ISBIGENDIAN(dst_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
icculus@10575
   328
        cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
icculus@10575
   329
        retval = 1;  /* added a converter. */
icculus@10575
   330
    }
icculus@10575
   331
icculus@10575
   332
    return retval;
icculus@3021
   333
}
slouken@2716
   334
icculus@10756
   335
icculus@10756
   336
/* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't store
icculus@10756
   337
   !!! FIXME:  channel info or integer sample rates, so we have to have
icculus@10756
   338
   !!! FIXME:  function entry points for each supported channel count and
icculus@10756
   339
   !!! FIXME:  multiple vs arbitrary. When we rev the ABI, remove this. */
icculus@10756
   340
#define RESAMPLER_FUNCS(chans) \
icculus@10756
   341
    static void SDLCALL \
icculus@10756
   342
    SDL_Upsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10756
   343
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10756
   344
        SDL_Upsample_Multiple(cvt, chans); \
icculus@10756
   345
    } \
icculus@10756
   346
    static void SDLCALL \
icculus@10756
   347
    SDL_Upsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10756
   348
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10756
   349
        SDL_Upsample_Arbitrary(cvt, chans); \
icculus@10756
   350
    }\
icculus@10756
   351
    static void SDLCALL \
icculus@10756
   352
    SDL_Downsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10756
   353
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10756
   354
        SDL_Downsample_Multiple(cvt, chans); \
icculus@10756
   355
    } \
icculus@10756
   356
    static void SDLCALL \
icculus@10756
   357
    SDL_Downsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10756
   358
        SDL_assert(format == AUDIO_F32SYS); \
icculus@10756
   359
        SDL_Downsample_Arbitrary(cvt, chans); \
icculus@10756
   360
    }
icculus@10756
   361
RESAMPLER_FUNCS(1)
icculus@10756
   362
RESAMPLER_FUNCS(2)
icculus@10756
   363
RESAMPLER_FUNCS(4)
icculus@10756
   364
RESAMPLER_FUNCS(6)
icculus@10756
   365
RESAMPLER_FUNCS(8)
icculus@10756
   366
#undef RESAMPLER_FUNCS
icculus@10756
   367
icculus@3021
   368
static int
icculus@3021
   369
SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate)
icculus@3021
   370
{
icculus@3021
   371
    int lo, hi;
slouken@2716
   372
icculus@6281
   373
    SDL_assert(src_rate != 0);
icculus@6281
   374
    SDL_assert(dst_rate != 0);
icculus@6281
   375
    SDL_assert(src_rate != dst_rate);
slouken@2716
   376
icculus@3021
   377
    if (src_rate < dst_rate) {
icculus@3021
   378
        lo = src_rate;
icculus@3021
   379
        hi = dst_rate;
icculus@3021
   380
    } else {
icculus@3021
   381
        lo = dst_rate;
icculus@3021
   382
        hi = src_rate;
icculus@3021
   383
    }
slouken@2716
   384
icculus@3021
   385
    if ((hi % lo) != 0)
slouken@3040
   386
        return 0;               /* not a multiple. */
slouken@2716
   387
icculus@10756
   388
    return hi / lo;
icculus@3021
   389
}
icculus@3021
   390
icculus@10756
   391
static SDL_AudioFilter
icculus@10756
   392
ChooseResampler(const int dst_channels, const int src_rate, const int dst_rate)
icculus@10756
   393
{
icculus@10756
   394
    const int upsample = (src_rate < dst_rate) ? 1 : 0;
icculus@10756
   395
    const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate);
icculus@10756
   396
    SDL_AudioFilter filter = NULL;
icculus@10756
   397
icculus@10756
   398
    #define PICK_CHANNEL_FILTER(upordown, resampler) switch (dst_channels) { \
icculus@10756
   399
        case 1: filter = SDL_##upordown##_##resampler##_c1; break; \
icculus@10756
   400
        case 2: filter = SDL_##upordown##_##resampler##_c2; break; \
icculus@10756
   401
        case 4: filter = SDL_##upordown##_##resampler##_c4; break; \
icculus@10756
   402
        case 6: filter = SDL_##upordown##_##resampler##_c6; break; \
icculus@10756
   403
        case 8: filter = SDL_##upordown##_##resampler##_c8; break; \
icculus@10756
   404
        default: break; \
icculus@10575
   405
    }
icculus@10756
   406
icculus@10756
   407
    if (upsample) {
icculus@10756
   408
        if (multiple) {
icculus@10756
   409
            PICK_CHANNEL_FILTER(Upsample, Multiple);
icculus@10756
   410
        } else {
icculus@10756
   411
            PICK_CHANNEL_FILTER(Upsample, Arbitrary);
icculus@10756
   412
        }
icculus@10756
   413
    } else {
icculus@10756
   414
        if (multiple) {
icculus@10756
   415
            PICK_CHANNEL_FILTER(Downsample, Multiple);
icculus@10756
   416
        } else {
icculus@10756
   417
            PICK_CHANNEL_FILTER(Downsample, Arbitrary);
icculus@10756
   418
        }
icculus@10756
   419
    }
icculus@10756
   420
icculus@10756
   421
    #undef PICK_CHANNEL_FILTER
icculus@10756
   422
icculus@10756
   423
    return filter;
icculus@10756
   424
}
icculus@10575
   425
icculus@3021
   426
static int
icculus@10756
   427
SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels,
icculus@10756
   428
                          const int src_rate, const int dst_rate)
icculus@3021
   429
{
icculus@10756
   430
    SDL_AudioFilter filter;
icculus@3021
   431
icculus@10756
   432
    if (src_rate == dst_rate) {
icculus@10756
   433
        return 0;  /* no conversion necessary. */
slouken@2716
   434
    }
slouken@2716
   435
icculus@10756
   436
    filter = ChooseResampler(dst_channels, src_rate, dst_rate);
icculus@10756
   437
    if (filter == NULL) {
icculus@10756
   438
        return SDL_SetError("No conversion available for these rates");
icculus@10756
   439
    }
icculus@10756
   440
icculus@10756
   441
    /* Update (cvt) with filter details... */
icculus@10756
   442
    cvt->filters[cvt->filter_index++] = filter;
icculus@10756
   443
    if (src_rate < dst_rate) {
icculus@10756
   444
        const double mult = ((double) dst_rate) / ((double) src_rate);
icculus@10756
   445
        cvt->len_mult *= (int) SDL_ceil(mult);
icculus@10756
   446
        cvt->len_ratio *= mult;
icculus@10756
   447
    } else {
icculus@10756
   448
        cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
icculus@10756
   449
    }
icculus@10756
   450
icculus@10756
   451
    return 1;               /* added a converter. */
slouken@2716
   452
}
icculus@1982
   453
icculus@1982
   454
icculus@1982
   455
/* Creates a set of audio filters to convert from one format to another.
icculus@1982
   456
   Returns -1 if the format conversion is not supported, 0 if there's
icculus@1982
   457
   no conversion needed, or 1 if the audio filter is set up.
slouken@0
   458
*/
slouken@1895
   459
slouken@1895
   460
int
slouken@1895
   461
SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
icculus@1982
   462
                  SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate,
icculus@1982
   463
                  SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate)
slouken@0
   464
{
aschiffler@6819
   465
    /* Sanity check target pointer */
aschiffler@6819
   466
    if (cvt == NULL) {
icculus@7037
   467
        return SDL_InvalidParamError("cvt");
aschiffler@6819
   468
    }
slouken@7191
   469
slouken@10767
   470
    /* Make sure we zero out the audio conversion before error checking */
slouken@10767
   471
    SDL_zerop(cvt);
slouken@10767
   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@10579
   488
#if 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@1982
   494
    cvt->src_format = src_fmt;
icculus@1982
   495
    cvt->dst_format = dst_fmt;
slouken@1895
   496
    cvt->needed = 0;
slouken@1895
   497
    cvt->filter_index = 0;
slouken@1895
   498
    cvt->filters[0] = NULL;
slouken@1895
   499
    cvt->len_mult = 1;
slouken@1895
   500
    cvt->len_ratio = 1.0;
icculus@3021
   501
    cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);
slouken@0
   502
icculus@10575
   503
    /* Type conversion goes like this now:
icculus@10575
   504
        - byteswap to CPU native format first if necessary.
icculus@10575
   505
        - convert to native Float32 if necessary.
icculus@10575
   506
        - resample and change channel count if necessary.
icculus@10575
   507
        - convert back to native format.
icculus@10575
   508
        - byteswap back to foreign format if necessary.
icculus@10575
   509
icculus@10575
   510
       The expectation is we can process data faster in float32
icculus@10575
   511
       (possibly with SIMD), and making several passes over the same
icculus@10756
   512
       buffer is likely to be CPU cache-friendly, avoiding the
icculus@10575
   513
       biggest performance hit in modern times. Previously we had
icculus@10575
   514
       (script-generated) custom converters for every data type and
icculus@10575
   515
       it was a bloat on SDL compile times and final library size. */
icculus@10575
   516
slouken@10767
   517
    /* see if we can skip float conversion entirely. */
slouken@10767
   518
    if (src_rate == dst_rate && src_channels == dst_channels) {
slouken@10767
   519
        if (src_fmt == dst_fmt) {
slouken@10767
   520
            return 0;
slouken@10767
   521
        }
slouken@10767
   522
slouken@10767
   523
        /* just a byteswap needed? */
slouken@10767
   524
        if ((src_fmt & ~SDL_AUDIO_MASK_ENDIAN) == (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN)) {
slouken@10767
   525
            cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
slouken@10767
   526
            cvt->needed = 1;
slouken@10767
   527
            return 1;
slouken@10767
   528
        }
icculus@10575
   529
    }
icculus@10575
   530
icculus@1982
   531
    /* Convert data types, if necessary. Updates (cvt). */
slouken@10767
   532
    if (SDL_BuildAudioTypeCVTToFloat(cvt, src_fmt) < 0) {
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@10767
   588
    if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) < 0) {
icculus@3021
   589
        return -1;              /* shouldn't happen, but just in case... */
slouken@2716
   590
    }
slouken@2716
   591
icculus@10756
   592
    /* Move to final data type. */
slouken@10767
   593
    if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) < 0) {
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
slouken@10773
   601
typedef int (*SDL_ResampleAudioStreamFunc)(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen);
slouken@10773
   602
typedef void (*SDL_ResetAudioStreamResamplerFunc)(SDL_AudioStream *stream);
slouken@10773
   603
typedef void (*SDL_CleanupAudioStreamResamplerFunc)(SDL_AudioStream *stream);
icculus@10757
   604
icculus@10757
   605
struct SDL_AudioStream
icculus@10757
   606
{
icculus@10757
   607
    SDL_AudioCVT cvt_before_resampling;
icculus@10757
   608
    SDL_AudioCVT cvt_after_resampling;
icculus@10757
   609
    SDL_DataQueue *queue;
icculus@10757
   610
    Uint8 *work_buffer;
icculus@10757
   611
    int work_buffer_len;
icculus@10757
   612
    Uint8 *resample_buffer;
icculus@10757
   613
    int resample_buffer_len;
icculus@10757
   614
    int src_sample_frame_size;
icculus@10757
   615
    SDL_AudioFormat src_format;
icculus@10757
   616
    Uint8 src_channels;
icculus@10757
   617
    int src_rate;
icculus@10757
   618
    int dst_sample_frame_size;
icculus@10757
   619
    SDL_AudioFormat dst_format;
icculus@10757
   620
    Uint8 dst_channels;
icculus@10757
   621
    int dst_rate;
icculus@10757
   622
    double rate_incr;
icculus@10757
   623
    Uint8 pre_resample_channels;
slouken@10773
   624
    int packetlen;
slouken@10773
   625
    void *resampler_state;
slouken@10773
   626
    SDL_ResampleAudioStreamFunc resampler_func;
slouken@10773
   627
    SDL_ResetAudioStreamResamplerFunc reset_resampler_func;
slouken@10773
   628
    SDL_CleanupAudioStreamResamplerFunc cleanup_resampler_func;
slouken@10773
   629
};
slouken@10773
   630
slouken@10777
   631
#ifdef HAVE_LIBSAMPLERATE_H
slouken@10773
   632
static int
slouken@10773
   633
SDL_ResampleAudioStream_SRC(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
slouken@10773
   634
{
icculus@10790
   635
    SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
slouken@10773
   636
    SRC_DATA data;
slouken@10773
   637
    int result;
slouken@10773
   638
slouken@10777
   639
    data.data_in = (float *)inbuf; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */
slouken@10773
   640
    data.input_frames = inbuflen / ( sizeof(float) * stream->pre_resample_channels );
slouken@10773
   641
    data.input_frames_used = 0;
slouken@10773
   642
slouken@10773
   643
    data.data_out = outbuf;
slouken@10773
   644
    data.output_frames = outbuflen / (sizeof(float) * stream->pre_resample_channels);
slouken@10773
   645
slouken@10773
   646
    data.end_of_input = 0;
slouken@10773
   647
    data.src_ratio = stream->rate_incr;
slouken@10773
   648
icculus@10790
   649
    result = SRC_src_process(state, &data);
slouken@10773
   650
    if (result != 0) {
icculus@10790
   651
        SDL_SetError("src_process() failed: %s", SRC_src_strerror(result));
slouken@10773
   652
        return 0;
slouken@10773
   653
    }
slouken@10773
   654
slouken@10773
   655
    /* If this fails, we need to store them off somewhere */
slouken@10773
   656
    SDL_assert(data.input_frames_used == data.input_frames);
slouken@10773
   657
slouken@10773
   658
    return data.output_frames_gen * (sizeof(float) * stream->pre_resample_channels);
slouken@10773
   659
}
slouken@10773
   660
slouken@10773
   661
static void
slouken@10773
   662
SDL_ResetAudioStreamResampler_SRC(SDL_AudioStream *stream)
slouken@10773
   663
{
icculus@10790
   664
    SRC_src_reset((SRC_STATE *)stream->resampler_state);
slouken@10773
   665
}
slouken@10773
   666
slouken@10773
   667
static void
slouken@10773
   668
SDL_CleanupAudioStreamResampler_SRC(SDL_AudioStream *stream)
slouken@10773
   669
{
icculus@10790
   670
    SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
slouken@10773
   671
    if (state) {
icculus@10790
   672
        SRC_src_delete(state);
slouken@10773
   673
    }
slouken@10773
   674
slouken@10773
   675
    stream->resampler_state = NULL;
slouken@10773
   676
    stream->resampler_func = NULL;
slouken@10773
   677
    stream->reset_resampler_func = NULL;
slouken@10773
   678
    stream->cleanup_resampler_func = NULL;
slouken@10773
   679
}
slouken@10773
   680
slouken@10773
   681
static SDL_bool
slouken@10773
   682
SetupLibSampleRateResampling(SDL_AudioStream *stream)
slouken@10773
   683
{
icculus@10790
   684
    int result = 0;
icculus@10790
   685
    SRC_STATE *state = NULL;
slouken@10773
   686
icculus@10790
   687
    if (SRC_available) {
icculus@10790
   688
        state = SRC_src_new(SRC_SINC_FASTEST, stream->pre_resample_channels, &result);
icculus@10790
   689
        if (!state) {
icculus@10790
   690
            SDL_SetError("src_new() failed: %s", SRC_src_strerror(result));
icculus@10790
   691
        }
slouken@10773
   692
    }
slouken@10773
   693
icculus@10790
   694
    if (!state) {
icculus@10790
   695
        SDL_CleanupAudioStreamResampler_SRC(stream);
slouken@10773
   696
        return SDL_FALSE;
slouken@10773
   697
    }
slouken@10773
   698
slouken@10773
   699
    stream->resampler_state = state;
slouken@10773
   700
    stream->resampler_func = SDL_ResampleAudioStream_SRC;
slouken@10773
   701
    stream->reset_resampler_func = SDL_ResetAudioStreamResampler_SRC;
slouken@10773
   702
    stream->cleanup_resampler_func = SDL_CleanupAudioStreamResampler_SRC;
slouken@10773
   703
slouken@10773
   704
    return SDL_TRUE;
slouken@10773
   705
}
icculus@10790
   706
#endif /* HAVE_LIBSAMPLERATE_H */
slouken@10773
   707
slouken@10773
   708
slouken@10773
   709
typedef struct
slouken@10773
   710
{
icculus@10757
   711
    SDL_bool resampler_seeded;
icculus@10757
   712
    float resampler_state[8];
slouken@10773
   713
} SDL_AudioStreamResamplerState;
slouken@10773
   714
slouken@10773
   715
static int
slouken@10773
   716
SDL_ResampleAudioStream(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
slouken@10773
   717
{
slouken@10773
   718
    /* !!! FIXME: this resampler sucks, but not much worse than our usual resampler.  :)  */  /* ... :( */
slouken@10773
   719
    SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
slouken@10773
   720
    const int chans = (int)stream->pre_resample_channels;
slouken@10773
   721
    const int framelen = chans * sizeof(float);
slouken@10773
   722
    const int total = (inbuflen / framelen);
slouken@10773
   723
    const int finalpos = total - chans;
slouken@10773
   724
    const double src_incr = 1.0 / stream->rate_incr;
slouken@10773
   725
    double idx = 0.0;
slouken@10773
   726
    float *dst = outbuf;
slouken@10773
   727
    float last_sample[SDL_arraysize(state->resampler_state)];
slouken@10773
   728
    int consumed = 0;
slouken@10773
   729
    int i;
slouken@10773
   730
slouken@10773
   731
    SDL_assert(chans <= SDL_arraysize(last_sample));
slouken@10773
   732
    SDL_assert((inbuflen % framelen) == 0);
slouken@10773
   733
slouken@10773
   734
    if (!state->resampler_seeded) {
slouken@10773
   735
        for (i = 0; i < chans; i++) {
slouken@10773
   736
            state->resampler_state[i] = inbuf[i];
slouken@10773
   737
        }
slouken@10773
   738
        state->resampler_seeded = SDL_TRUE;
slouken@10773
   739
    }
slouken@10773
   740
slouken@10773
   741
    for (i = 0; i < chans; i++) {
slouken@10773
   742
        last_sample[i] = state->resampler_state[i];
slouken@10773
   743
    }
slouken@10773
   744
slouken@10773
   745
    while (consumed < total) {
slouken@10773
   746
        const int pos = ((int)idx) * chans;
slouken@10773
   747
        const float *src = &inbuf[(pos >= finalpos) ? finalpos : pos];
slouken@10773
   748
        SDL_assert(dst < (outbuf + (outbuflen / framelen)));
slouken@10773
   749
        for (i = 0; i < chans; i++) {
slouken@10773
   750
            const float val = *(src++);
slouken@10773
   751
            *(dst++) = (val + last_sample[i]) * 0.5f;
slouken@10773
   752
            last_sample[i] = val;
slouken@10773
   753
        }
slouken@10773
   754
        consumed = pos + chans;
slouken@10773
   755
        idx += src_incr;
slouken@10773
   756
    }
slouken@10773
   757
slouken@10773
   758
    for (i = 0; i < chans; i++) {
slouken@10773
   759
        state->resampler_state[i] = last_sample[i];
slouken@10773
   760
    }
slouken@10773
   761
slouken@10773
   762
    return (int)((dst - outbuf) * sizeof(float));
slouken@10773
   763
}
slouken@10773
   764
slouken@10773
   765
static void
slouken@10773
   766
SDL_ResetAudioStreamResampler(SDL_AudioStream *stream)
slouken@10773
   767
{
slouken@10773
   768
    SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
slouken@10773
   769
    state->resampler_seeded = SDL_FALSE;
slouken@10773
   770
}
slouken@10773
   771
slouken@10773
   772
static void
slouken@10773
   773
SDL_CleanupAudioStreamResampler(SDL_AudioStream *stream)
slouken@10773
   774
{
slouken@10773
   775
    SDL_free(stream->resampler_state);
slouken@10773
   776
}
icculus@10757
   777
icculus@10789
   778
SDL_AudioStream *
icculus@10789
   779
SDL_NewAudioStream(const SDL_AudioFormat src_format,
icculus@10789
   780
                   const Uint8 src_channels,
icculus@10789
   781
                   const int src_rate,
icculus@10789
   782
                   const SDL_AudioFormat dst_format,
icculus@10789
   783
                   const Uint8 dst_channels,
icculus@10789
   784
                   const int dst_rate)
icculus@10757
   785
{
icculus@10757
   786
    const int packetlen = 4096;  /* !!! FIXME: good enough for now. */
icculus@10757
   787
    Uint8 pre_resample_channels;
icculus@10757
   788
    SDL_AudioStream *retval;
icculus@10757
   789
icculus@10757
   790
    retval = (SDL_AudioStream *) SDL_calloc(1, sizeof (SDL_AudioStream));
icculus@10757
   791
    if (!retval) {
icculus@10757
   792
        return NULL;
icculus@10757
   793
    }
icculus@10757
   794
icculus@10757
   795
    /* If increasing channels, do it after resampling, since we'd just
icculus@10757
   796
       do more work to resample duplicate channels. If we're decreasing, do
icculus@10757
   797
       it first so we resample the interpolated data instead of interpolating
icculus@10757
   798
       the resampled data (!!! FIXME: decide if that works in practice, though!). */
icculus@10757
   799
    pre_resample_channels = SDL_min(src_channels, dst_channels);
icculus@10757
   800
icculus@10757
   801
    retval->src_sample_frame_size = SDL_AUDIO_BITSIZE(src_format) * src_channels;
icculus@10757
   802
    retval->src_format = src_format;
icculus@10757
   803
    retval->src_channels = src_channels;
icculus@10757
   804
    retval->src_rate = src_rate;
icculus@10757
   805
    retval->dst_sample_frame_size = SDL_AUDIO_BITSIZE(dst_format) * dst_channels;
icculus@10757
   806
    retval->dst_format = dst_format;
icculus@10757
   807
    retval->dst_channels = dst_channels;
icculus@10757
   808
    retval->dst_rate = dst_rate;
icculus@10757
   809
    retval->pre_resample_channels = pre_resample_channels;
icculus@10757
   810
    retval->packetlen = packetlen;
icculus@10757
   811
    retval->rate_incr = ((double) dst_rate) / ((double) src_rate);
icculus@10757
   812
icculus@10757
   813
    /* Not resampling? It's an easy conversion (and maybe not even that!). */
icculus@10757
   814
    if (src_rate == dst_rate) {
icculus@10757
   815
        retval->cvt_before_resampling.needed = SDL_FALSE;
icculus@10757
   816
        retval->cvt_before_resampling.len_mult = 1;
slouken@10773
   817
        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
slouken@10773
   818
            SDL_FreeAudioStream(retval);
icculus@10757
   819
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
   820
        }
icculus@10757
   821
    } else {
icculus@10757
   822
        /* Don't resample at first. Just get us to Float32 format. */
icculus@10757
   823
        /* !!! FIXME: convert to int32 on devices without hardware float. */
slouken@10773
   824
        if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) < 0) {
slouken@10773
   825
            SDL_FreeAudioStream(retval);
icculus@10757
   826
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
   827
        }
icculus@10757
   828
slouken@10777
   829
#ifdef HAVE_LIBSAMPLERATE_H
slouken@10773
   830
        SetupLibSampleRateResampling(retval);
slouken@10773
   831
#endif
slouken@10773
   832
slouken@10773
   833
        if (!retval->resampler_func) {
slouken@10773
   834
            retval->resampler_state = SDL_calloc(1, sizeof(SDL_AudioStreamResamplerState));
slouken@10773
   835
            if (!retval->resampler_state) {
slouken@10773
   836
                SDL_FreeAudioStream(retval);
slouken@10773
   837
                SDL_OutOfMemory();
slouken@10773
   838
                return NULL;
slouken@10773
   839
            }
slouken@10773
   840
            retval->resampler_func = SDL_ResampleAudioStream;
slouken@10773
   841
            retval->reset_resampler_func = SDL_ResetAudioStreamResampler;
slouken@10773
   842
            retval->cleanup_resampler_func = SDL_CleanupAudioStreamResampler;
slouken@10773
   843
        }
slouken@10773
   844
icculus@10757
   845
        /* Convert us to the final format after resampling. */
slouken@10773
   846
        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
slouken@10773
   847
            SDL_FreeAudioStream(retval);
icculus@10757
   848
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
   849
        }
icculus@10757
   850
    }
icculus@10757
   851
icculus@10757
   852
    retval->queue = SDL_NewDataQueue(packetlen, packetlen * 2);
icculus@10757
   853
    if (!retval->queue) {
slouken@10773
   854
        SDL_FreeAudioStream(retval);
icculus@10757
   855
        return NULL;  /* SDL_NewDataQueue should have called SDL_SetError. */
icculus@10757
   856
    }
icculus@10757
   857
icculus@10757
   858
    return retval;
icculus@10757
   859
}
icculus@10757
   860
icculus@10757
   861
static Uint8 *
icculus@10757
   862
EnsureBufferSize(Uint8 **buf, int *len, const int newlen)
icculus@10757
   863
{
icculus@10757
   864
    if (*len < newlen) {
icculus@10757
   865
        void *ptr = SDL_realloc(*buf, newlen);
icculus@10757
   866
        if (!ptr) {
icculus@10757
   867
            SDL_OutOfMemory();
icculus@10757
   868
            return NULL;
icculus@10757
   869
        }
icculus@10757
   870
        *buf = (Uint8 *) ptr;
icculus@10757
   871
        *len = newlen;
icculus@10757
   872
    }
icculus@10757
   873
    return *buf;
icculus@10757
   874
}
icculus@10757
   875
icculus@10757
   876
int
icculus@10757
   877
SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, const Uint32 _buflen)
icculus@10757
   878
{
icculus@10757
   879
    int buflen = (int) _buflen;
icculus@10757
   880
icculus@10757
   881
    if (!stream) {
icculus@10757
   882
        return SDL_InvalidParamError("stream");
icculus@10757
   883
    } else if (!buf) {
icculus@10757
   884
        return SDL_InvalidParamError("buf");
icculus@10757
   885
    } else if (buflen == 0) {
icculus@10757
   886
        return 0;  /* nothing to do. */
icculus@10757
   887
    } else if ((buflen % stream->src_sample_frame_size) != 0) {
icculus@10757
   888
        return SDL_SetError("Can't add partial sample frames");
icculus@10757
   889
    }
icculus@10757
   890
icculus@10757
   891
    if (stream->cvt_before_resampling.needed) {
icculus@10757
   892
        const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
icculus@10757
   893
        Uint8 *workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
icculus@10757
   894
        if (workbuf == NULL) {
icculus@10757
   895
            return -1;  /* probably out of memory. */
icculus@10757
   896
        }
icculus@10757
   897
        SDL_memcpy(workbuf, buf, buflen);
icculus@10757
   898
        stream->cvt_before_resampling.buf = workbuf;
icculus@10757
   899
        stream->cvt_before_resampling.len = buflen;
icculus@10757
   900
        if (SDL_ConvertAudio(&stream->cvt_before_resampling) == -1) {
icculus@10757
   901
            return -1;   /* uhoh! */
icculus@10757
   902
        }
icculus@10757
   903
        buf = workbuf;
icculus@10757
   904
        buflen = stream->cvt_before_resampling.len_cvt;
icculus@10757
   905
    }
icculus@10757
   906
icculus@10757
   907
    if (stream->dst_rate != stream->src_rate) {
icculus@10757
   908
        const int workbuflen = buflen * ((int) SDL_ceil(stream->rate_incr));
icculus@10757
   909
        float *workbuf = (float *) EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
icculus@10757
   910
        if (workbuf == NULL) {
icculus@10757
   911
            return -1;  /* probably out of memory. */
icculus@10757
   912
        }
slouken@10773
   913
        buflen = stream->resampler_func(stream, (float *) buf, buflen, workbuf, workbuflen);
icculus@10757
   914
        buf = workbuf;
icculus@10757
   915
    }
icculus@10757
   916
icculus@10757
   917
    if (stream->cvt_after_resampling.needed) {
icculus@10757
   918
        const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
icculus@10757
   919
        Uint8 *workbuf;
icculus@10757
   920
icculus@10757
   921
        if (buf == stream->resample_buffer) {
icculus@10757
   922
            workbuf = EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
icculus@10757
   923
        } else {
icculus@10757
   924
            const int inplace = (buf == stream->work_buffer);
icculus@10757
   925
            workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
icculus@10757
   926
            if (workbuf && !inplace) {
icculus@10757
   927
                SDL_memcpy(workbuf, buf, buflen);
icculus@10757
   928
            }
icculus@10757
   929
        }
icculus@10757
   930
icculus@10757
   931
        if (workbuf == NULL) {
icculus@10757
   932
            return -1;  /* probably out of memory. */
icculus@10757
   933
        }
icculus@10757
   934
icculus@10757
   935
        stream->cvt_after_resampling.buf = workbuf;
icculus@10757
   936
        stream->cvt_after_resampling.len = buflen;
icculus@10757
   937
        if (SDL_ConvertAudio(&stream->cvt_after_resampling) == -1) {
icculus@10757
   938
            return -1;   /* uhoh! */
icculus@10757
   939
        }
icculus@10757
   940
        buf = workbuf;
icculus@10757
   941
        buflen = stream->cvt_after_resampling.len_cvt;
icculus@10757
   942
    }
icculus@10757
   943
icculus@10757
   944
    return SDL_WriteToDataQueue(stream->queue, buf, buflen);
icculus@10757
   945
}
icculus@10757
   946
icculus@10757
   947
void
icculus@10757
   948
SDL_AudioStreamClear(SDL_AudioStream *stream)
icculus@10757
   949
{
icculus@10757
   950
    if (!stream) {
icculus@10757
   951
        SDL_InvalidParamError("stream");
icculus@10757
   952
    } else {
icculus@10757
   953
        SDL_ClearDataQueue(stream->queue, stream->packetlen * 2);
icculus@10776
   954
        if (stream->reset_resampler_func) {
icculus@10776
   955
            stream->reset_resampler_func(stream);
icculus@10776
   956
        }
icculus@10757
   957
    }
icculus@10757
   958
}
icculus@10757
   959
icculus@10757
   960
icculus@10757
   961
/* get converted/resampled data from the stream */
icculus@10757
   962
int
icculus@10764
   963
SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, const Uint32 len)
icculus@10757
   964
{
icculus@10757
   965
    if (!stream) {
icculus@10757
   966
        return SDL_InvalidParamError("stream");
icculus@10757
   967
    } else if (!buf) {
icculus@10757
   968
        return SDL_InvalidParamError("buf");
icculus@10757
   969
    } else if (len == 0) {
icculus@10757
   970
        return 0;  /* nothing to do. */
icculus@10757
   971
    } else if ((len % stream->dst_sample_frame_size) != 0) {
icculus@10757
   972
        return SDL_SetError("Can't request partial sample frames");
icculus@10757
   973
    }
icculus@10757
   974
icculus@10764
   975
    return (int) SDL_ReadFromDataQueue(stream->queue, buf, len);
icculus@10757
   976
}
icculus@10757
   977
icculus@10757
   978
/* number of converted/resampled bytes available */
icculus@10757
   979
int
icculus@10757
   980
SDL_AudioStreamAvailable(SDL_AudioStream *stream)
icculus@10757
   981
{
icculus@10757
   982
    return stream ? (int) SDL_CountDataQueue(stream->queue) : 0;
icculus@10757
   983
}
icculus@10757
   984
icculus@10757
   985
/* dispose of a stream */
icculus@10757
   986
void
icculus@10757
   987
SDL_FreeAudioStream(SDL_AudioStream *stream)
icculus@10757
   988
{
icculus@10757
   989
    if (stream) {
slouken@10773
   990
        if (stream->cleanup_resampler_func) {
slouken@10773
   991
            stream->cleanup_resampler_func(stream);
slouken@10773
   992
        }
icculus@10757
   993
        SDL_FreeDataQueue(stream->queue);
icculus@10757
   994
        SDL_free(stream->work_buffer);
icculus@10757
   995
        SDL_free(stream->resample_buffer);
icculus@10757
   996
        SDL_free(stream);
icculus@10757
   997
    }
icculus@10757
   998
}
icculus@10757
   999
icculus@10575
  1000
/* vi: set ts=4 sw=4 expandtab: */
slouken@2716
  1001