src/audio/SDL_audiocvt.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 09 Jan 2017 16:31:57 -0500
changeset 10804 1502bb751ca4
parent 10799 234f71894a52
child 10805 30a00d960dc9
permissions -rw-r--r--
audio: Don't ever use libsamplerate in the SDL_AudioCVT codepath.

It causes audio pops if you're converting in chunks (and needs to
allocate/initialize/free on each convert). We'll either adjust this interface
when we break ABI for 2.1 to make this usable, or publish the SDL_AudioStream
API for those that want a streaming solution.

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