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