src/audio/SDL_audiocvt.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 22 Jan 2017 20:18:59 -0500
changeset 10831 fcbb4d7f2344
parent 10830 92013fad89d1
child 10832 189266031c6f
permissions -rw-r--r--
audio: don't cast to double in SDL_ConvertStereoToMono().

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