src/audio/SDL_audiocvt.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 22 Jan 2017 23:48:15 -0500
changeset 10833 86f6353f1aae
parent 10832 189266031c6f
child 10834 336efe4fc373
permissions -rw-r--r--
audio: Make the simple resampler operate in-place.

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