src/audio/SDL_audiocvt.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 18 Aug 2017 16:52:19 -0400
changeset 11319 86b1fde471c6
parent 11128 9dda3f3e9794
child 11405 4e12f78c2b0e
permissions -rw-r--r--
audio: better docs on conversion APIs, error if not init'd (thanks, Simon!).

Fixes Bugzilla #3662.
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
icculus@11319
    25
#include "SDL.h"
slouken@0
    26
#include "SDL_audio.h"
icculus@1982
    27
#include "SDL_audio_c.h"
slouken@0
    28
slouken@10773
    29
#include "SDL_loadso.h"
icculus@6281
    30
#include "SDL_assert.h"
icculus@10757
    31
#include "../SDL_dataqueue.h"
icculus@10835
    32
#include "SDL_cpuinfo.h"
icculus@6281
    33
icculus@10835
    34
#ifdef __SSE3__
icculus@10835
    35
#define HAVE_SSE3_INTRINSICS 1
icculus@10832
    36
#endif
icculus@10832
    37
icculus@10832
    38
#if HAVE_SSE3_INTRINSICS
icculus@10832
    39
/* Effectively mix right and left channels into a single channel */
icculus@10832
    40
static void SDLCALL
icculus@10832
    41
SDL_ConvertStereoToMono_SSE3(SDL_AudioCVT * cvt, SDL_AudioFormat format)
icculus@10832
    42
{
icculus@10832
    43
    float *dst = (float *) cvt->buf;
icculus@10832
    44
    const float *src = dst;
icculus@10832
    45
    int i = cvt->len_cvt / 8;
icculus@10832
    46
icculus@10832
    47
    LOG_DEBUG_CONVERT("stereo", "mono (using SSE3)");
icculus@10832
    48
    SDL_assert(format == AUDIO_F32SYS);
icculus@10832
    49
icculus@10832
    50
    /* We can only do this if dst is aligned to 16 bytes; since src is the
icculus@10832
    51
       same pointer and it moves by 2, it can't be forcibly aligned. */
icculus@10832
    52
    if ((((size_t) dst) & 15) == 0) {
icculus@10832
    53
        /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
icculus@10832
    54
        const __m128 divby2 = _mm_set1_ps(0.5f);
icculus@10832
    55
        while (i >= 4) {   /* 4 * float32 */
icculus@10832
    56
            _mm_store_ps(dst, _mm_mul_ps(_mm_hadd_ps(_mm_load_ps(src), _mm_load_ps(src+4)), divby2));
icculus@10832
    57
            i -= 4; src += 8; dst += 4;
icculus@10832
    58
        }
icculus@10832
    59
    }
icculus@10832
    60
icculus@10832
    61
    /* Finish off any leftovers with scalar operations. */
icculus@10832
    62
    while (i) {
icculus@10832
    63
        *dst = (src[0] + src[1]) * 0.5f;
icculus@10832
    64
        dst++; i--; src += 2;
icculus@10832
    65
    }
icculus@10832
    66
icculus@10832
    67
    cvt->len_cvt /= 2;
icculus@10832
    68
    if (cvt->filters[++cvt->filter_index]) {
icculus@10832
    69
        cvt->filters[cvt->filter_index] (cvt, format);
icculus@10832
    70
    }
icculus@10832
    71
}
icculus@10832
    72
#endif
icculus@10832
    73
slouken@0
    74
/* Effectively mix right and left channels into a single channel */
icculus@1982
    75
static void SDLCALL
icculus@10793
    76
SDL_ConvertStereoToMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@0
    77
{
icculus@10575
    78
    float *dst = (float *) cvt->buf;
icculus@10575
    79
    const float *src = dst;
slouken@1895
    80
    int i;
slouken@0
    81
icculus@10575
    82
    LOG_DEBUG_CONVERT("stereo", "mono");
icculus@10575
    83
    SDL_assert(format == AUDIO_F32SYS);
slouken@0
    84
icculus@10575
    85
    for (i = cvt->len_cvt / 8; i; --i, src += 2) {
icculus@10831
    86
        *(dst++) = (src[0] + src[1]) * 0.5f;
slouken@1895
    87
    }
icculus@1982
    88
slouken@1895
    89
    cvt->len_cvt /= 2;
slouken@1895
    90
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
    91
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
    92
    }
slouken@0
    93
}
slouken@0
    94
icculus@1982
    95
icculus@10793
    96
/* Convert from 5.1 to stereo. Average left and right, discard subwoofer. */
icculus@1982
    97
static void SDLCALL
icculus@10793
    98
SDL_Convert51ToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
    99
{
icculus@10575
   100
    float *dst = (float *) cvt->buf;
icculus@10575
   101
    const float *src = dst;
slouken@1895
   102
    int i;
slouken@942
   103
icculus@10793
   104
    LOG_DEBUG_CONVERT("5.1", "stereo");
icculus@10575
   105
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
   106
icculus@10793
   107
    /* this assumes FL+FR+FC+subwoof+BL+BR layout. */
icculus@10575
   108
    for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 2) {
icculus@10793
   109
        const double front_center = (double) src[2];
icculus@10793
   110
        dst[0] = (float) ((src[0] + front_center + src[4]) / 3.0);  /* left */
icculus@10793
   111
        dst[1] = (float) ((src[1] + front_center + src[5]) / 3.0);  /* right */
icculus@1982
   112
    }
slouken@942
   113
slouken@1895
   114
    cvt->len_cvt /= 3;
slouken@1895
   115
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   116
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   117
    }
slouken@942
   118
}
slouken@942
   119
slouken@942
   120
icculus@10793
   121
/* Convert from 5.1 to quad */
icculus@1982
   122
static void SDLCALL
icculus@10793
   123
SDL_Convert51ToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
   124
{
icculus@10575
   125
    float *dst = (float *) cvt->buf;
icculus@10575
   126
    const float *src = dst;
slouken@1895
   127
    int i;
slouken@942
   128
icculus@10793
   129
    LOG_DEBUG_CONVERT("5.1", "quad");
icculus@10575
   130
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
   131
icculus@10793
   132
    /* assumes quad is FL+FR+BL+BR layout and 5.1 is FL+FR+FC+subwoof+BL+BR */
icculus@10575
   133
    for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 4) {
icculus@10793
   134
        /* FIXME: this is a good candidate for SIMD. */
icculus@10793
   135
        const double front_center = (double) src[2];
icculus@10793
   136
        dst[0] = (float) ((src[0] + front_center) * 0.5);  /* FL */
icculus@10793
   137
        dst[1] = (float) ((src[1] + front_center) * 0.5);  /* FR */
icculus@10793
   138
        dst[2] = (float) ((src[4] + front_center) * 0.5);  /* BL */
icculus@10793
   139
        dst[3] = (float) ((src[5] + front_center) * 0.5);  /* BR */
icculus@1982
   140
    }
slouken@942
   141
icculus@1982
   142
    cvt->len_cvt /= 6;
icculus@1982
   143
    cvt->len_cvt *= 4;
slouken@1895
   144
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   145
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   146
    }
slouken@942
   147
}
slouken@0
   148
icculus@10793
   149
slouken@0
   150
/* Duplicate a mono channel to both stereo channels */
icculus@1982
   151
static void SDLCALL
icculus@10793
   152
SDL_ConvertMonoToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@0
   153
{
icculus@10575
   154
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@10575
   155
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
slouken@1895
   156
    int i;
slouken@0
   157
icculus@10575
   158
    LOG_DEBUG_CONVERT("mono", "stereo");
icculus@10575
   159
    SDL_assert(format == AUDIO_F32SYS);
slouken@0
   160
icculus@10575
   161
    for (i = cvt->len_cvt / sizeof (float); i; --i) {
icculus@10575
   162
        src--;
icculus@10575
   163
        dst -= 2;
icculus@10575
   164
        dst[0] = dst[1] = *src;
icculus@1982
   165
    }
slouken@0
   166
slouken@1895
   167
    cvt->len_cvt *= 2;
slouken@1895
   168
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   169
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   170
    }
slouken@0
   171
}
slouken@0
   172
slouken@942
   173
slouken@942
   174
/* Duplicate a stereo channel to a pseudo-5.1 stream */
icculus@1982
   175
static void SDLCALL
icculus@10793
   176
SDL_ConvertStereoTo51(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
   177
{
slouken@1895
   178
    int i;
icculus@10575
   179
    float lf, rf, ce;
icculus@10575
   180
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@10575
   181
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 3);
slouken@942
   182
icculus@10575
   183
    LOG_DEBUG_CONVERT("stereo", "5.1");
icculus@10575
   184
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
   185
icculus@10575
   186
    for (i = cvt->len_cvt / 8; i; --i) {
icculus@10575
   187
        dst -= 6;
icculus@10575
   188
        src -= 2;
icculus@10575
   189
        lf = src[0];
icculus@10575
   190
        rf = src[1];
icculus@10793
   191
        ce = (lf + rf) * 0.5f;
icculus@10793
   192
        dst[0] = lf + (lf - ce);  /* FL */
icculus@10793
   193
        dst[1] = rf + (rf - ce);  /* FR */
icculus@10793
   194
        dst[2] = ce;  /* FC */
icculus@10793
   195
        dst[3] = ce;  /* !!! FIXME: wrong! This is the subwoofer. */
icculus@10793
   196
        dst[4] = lf;  /* BL */
icculus@10793
   197
        dst[5] = rf;  /* BR */
icculus@10575
   198
    }
slouken@942
   199
slouken@1895
   200
    cvt->len_cvt *= 3;
slouken@1895
   201
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   202
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   203
    }
slouken@942
   204
}
slouken@942
   205
slouken@942
   206
slouken@942
   207
/* Duplicate a stereo channel to a pseudo-4.0 stream */
icculus@1982
   208
static void SDLCALL
icculus@10793
   209
SDL_ConvertStereoToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
   210
{
icculus@10575
   211
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@10575
   212
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
icculus@10793
   213
    float lf, rf;
slouken@1895
   214
    int i;
slouken@942
   215
icculus@10575
   216
    LOG_DEBUG_CONVERT("stereo", "quad");
icculus@10575
   217
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
   218
icculus@10575
   219
    for (i = cvt->len_cvt / 8; i; --i) {
icculus@10575
   220
        dst -= 4;
icculus@10575
   221
        src -= 2;
icculus@10575
   222
        lf = src[0];
icculus@10575
   223
        rf = src[1];
icculus@10793
   224
        dst[0] = lf;  /* FL */
icculus@10793
   225
        dst[1] = rf;  /* FR */
icculus@10793
   226
        dst[2] = lf;  /* BL */
icculus@10793
   227
        dst[3] = rf;  /* BR */
icculus@10575
   228
    }
slouken@942
   229
slouken@1895
   230
    cvt->len_cvt *= 2;
slouken@1895
   231
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   232
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   233
    }
slouken@0
   234
}
slouken@0
   235
icculus@10799
   236
static int
icculus@10799
   237
SDL_ResampleAudioSimple(const int chans, const double rate_incr,
icculus@10799
   238
                        float *last_sample, const float *inbuf,
icculus@10799
   239
                        const int inbuflen, float *outbuf, const int outbuflen)
icculus@10799
   240
{
icculus@10817
   241
    const int framelen = chans * (int)sizeof (float);
icculus@10799
   242
    const int total = (inbuflen / framelen);
icculus@10817
   243
    const int finalpos = (total * chans) - chans;
icculus@10817
   244
    const int dest_samples = (int)(((double)total) * rate_incr);
icculus@10799
   245
    const double src_incr = 1.0 / rate_incr;
icculus@10833
   246
    float *dst;
icculus@10833
   247
    double idx;
icculus@10799
   248
    int i;
icculus@10799
   249
icculus@10817
   250
    SDL_assert((dest_samples * framelen) <= outbuflen);
icculus@10799
   251
    SDL_assert((inbuflen % framelen) == 0);
icculus@10799
   252
icculus@10840
   253
    if (rate_incr > 1.0) {  /* upsample */
icculus@10833
   254
        float *target = (outbuf + chans);
icculus@10833
   255
        dst = outbuf + (dest_samples * chans);
icculus@10833
   256
        idx = (double) total;
icculus@10833
   257
icculus@10840
   258
        if (chans == 1) {
icculus@10840
   259
            const float final_sample = inbuf[finalpos];
icculus@10840
   260
            float earlier_sample = inbuf[finalpos];
icculus@10840
   261
            while (dst > target) {
icculus@10840
   262
                const int pos = ((int) idx) * chans;
icculus@10840
   263
                const float *src = &inbuf[pos];
icculus@10840
   264
                const float val = *(--src);
icculus@10840
   265
                SDL_assert(pos >= 0.0);
icculus@10840
   266
                *(--dst) = (val + earlier_sample) * 0.5f;
icculus@10840
   267
                earlier_sample = val;
icculus@10840
   268
                idx -= src_incr;
icculus@10840
   269
            }
icculus@10840
   270
            /* do last sample, interpolated against previous run's state. */
icculus@10840
   271
            *(--dst) = (inbuf[0] + last_sample[0]) * 0.5f;
icculus@10840
   272
            *last_sample = final_sample;
icculus@10840
   273
        } else if (chans == 2) {
icculus@10840
   274
            const float final_sample2 = inbuf[finalpos+1];
icculus@10840
   275
            const float final_sample1 = inbuf[finalpos];
icculus@10840
   276
            float earlier_sample2 = inbuf[finalpos];
icculus@10840
   277
            float earlier_sample1 = inbuf[finalpos-1];
icculus@10840
   278
            while (dst > target) {
icculus@10840
   279
                const int pos = ((int) idx) * chans;
icculus@10840
   280
                const float *src = &inbuf[pos];
icculus@10840
   281
                const float val2 = *(--src);
icculus@10840
   282
                const float val1 = *(--src);
icculus@10840
   283
                SDL_assert(pos >= 0.0);
icculus@10840
   284
                *(--dst) = (val2 + earlier_sample2) * 0.5f;
icculus@10840
   285
                *(--dst) = (val1 + earlier_sample1) * 0.5f;
icculus@10840
   286
                earlier_sample2 = val2;
icculus@10840
   287
                earlier_sample1 = val1;
icculus@10840
   288
                idx -= src_incr;
icculus@10840
   289
            }
icculus@10840
   290
            /* do last sample, interpolated against previous run's state. */
icculus@10840
   291
            *(--dst) = (inbuf[1] + last_sample[1]) * 0.5f;
icculus@10840
   292
            *(--dst) = (inbuf[0] + last_sample[0]) * 0.5f;
icculus@10840
   293
            last_sample[1] = final_sample2;
icculus@10840
   294
            last_sample[0] = final_sample1;
icculus@10840
   295
        } else {
icculus@10840
   296
            const float *earlier_sample = &inbuf[finalpos];
icculus@10840
   297
            float final_sample[8];
icculus@10840
   298
            SDL_memcpy(final_sample, &inbuf[finalpos], framelen);
icculus@10840
   299
            while (dst > target) {
icculus@10840
   300
                const int pos = ((int) idx) * chans;
icculus@10840
   301
                const float *src = &inbuf[pos];
icculus@10840
   302
                SDL_assert(pos >= 0.0);
icculus@10840
   303
                for (i = chans - 1; i >= 0; i--) {
icculus@10840
   304
                    const float val = *(--src);
icculus@10840
   305
                    *(--dst) = (val + earlier_sample[i]) * 0.5f;
icculus@10840
   306
                }
icculus@10840
   307
                earlier_sample = src;
icculus@10840
   308
                idx -= src_incr;
icculus@10840
   309
            }
icculus@10840
   310
            /* do last sample, interpolated against previous run's state. */
icculus@10833
   311
            for (i = chans - 1; i >= 0; i--) {
icculus@10840
   312
                const float val = inbuf[i];
icculus@10840
   313
                *(--dst) = (val + last_sample[i]) * 0.5f;
icculus@10833
   314
            }
icculus@10840
   315
            SDL_memcpy(last_sample, final_sample, framelen);
icculus@10799
   316
        }
icculus@10833
   317
icculus@10841
   318
        dst = (outbuf + (dest_samples * chans));
icculus@10840
   319
    } else {  /* downsample */
icculus@10833
   320
        float *target = (outbuf + (dest_samples * chans));
icculus@10833
   321
        dst = outbuf;
icculus@10833
   322
        idx = 0.0;
icculus@10840
   323
        if (chans == 1) {
icculus@10840
   324
            float last = *last_sample;
icculus@10840
   325
            while (dst < target) {
icculus@10840
   326
                const int pos = ((int) idx) * chans;
icculus@10840
   327
                const float val = inbuf[pos];
icculus@10840
   328
                SDL_assert(pos <= finalpos);
icculus@10840
   329
                *(dst++) = (val + last) * 0.5f;
icculus@10840
   330
                last = val;
icculus@10840
   331
                idx += src_incr;
icculus@10833
   332
            }
icculus@10840
   333
            *last_sample = last;
icculus@10840
   334
        } else if (chans == 2) {
icculus@10840
   335
            float last1 = last_sample[0];
icculus@10840
   336
            float last2 = last_sample[1];
icculus@10840
   337
            while (dst < target) {
icculus@10840
   338
                const int pos = ((int) idx) * chans;
icculus@10840
   339
                const float val1 = inbuf[pos];
icculus@10840
   340
                const float val2 = inbuf[pos+1];
icculus@10840
   341
                SDL_assert(pos <= finalpos);
icculus@10840
   342
                *(dst++) = (val1 + last1) * 0.5f;
icculus@10840
   343
                *(dst++) = (val2 + last2) * 0.5f;
icculus@10840
   344
                last1 = val1;
icculus@10840
   345
                last2 = val2;
icculus@10840
   346
                idx += src_incr;
icculus@10840
   347
            }
icculus@10840
   348
            last_sample[0] = last1;
icculus@10840
   349
            last_sample[1] = last2;
icculus@10840
   350
        } else {
icculus@10840
   351
            while (dst < target) {
icculus@10840
   352
                const int pos = ((int) idx) * chans;
icculus@10840
   353
                const float *src = &inbuf[pos];
icculus@10840
   354
                SDL_assert(pos <= finalpos);
icculus@10840
   355
                for (i = 0; i < chans; i++) {
icculus@10840
   356
                    const float val = *(src++);
icculus@10840
   357
                    *(dst++) = (val + last_sample[i]) * 0.5f;
icculus@10840
   358
                    last_sample[i] = val;
icculus@10840
   359
                }
icculus@10840
   360
                idx += src_incr;
icculus@10840
   361
            }
icculus@10833
   362
        }
icculus@10799
   363
    }
icculus@10799
   364
icculus@10833
   365
    return (int) ((dst - outbuf) * ((int) sizeof (float)));
icculus@10799
   366
}
icculus@10799
   367
icculus@10834
   368
/* We keep one special-case fast path around for an extremely common audio format. */
icculus@10834
   369
static int
icculus@10834
   370
SDL_ResampleAudioSimple_si16_c2(const double rate_incr,
icculus@10834
   371
                        Sint16 *last_sample, const Sint16 *inbuf,
icculus@10834
   372
                        const int inbuflen, Sint16 *outbuf, const int outbuflen)
icculus@10834
   373
{
icculus@10834
   374
    const int chans = 2;
icculus@10834
   375
    const int framelen = 4;  /* stereo 16 bit */
icculus@10834
   376
    const int total = (inbuflen / framelen);
icculus@10834
   377
    const int finalpos = (total * chans) - chans;
icculus@10834
   378
    const int dest_samples = (int)(((double)total) * rate_incr);
icculus@10834
   379
    const double src_incr = 1.0 / rate_incr;
icculus@10834
   380
    Sint16 *dst;
icculus@10834
   381
    double idx;
icculus@10834
   382
icculus@10834
   383
    SDL_assert((dest_samples * framelen) <= outbuflen);
icculus@10834
   384
    SDL_assert((inbuflen % framelen) == 0);
icculus@10834
   385
icculus@10834
   386
    if (rate_incr > 1.0) {
icculus@10834
   387
        Sint16 *target = (outbuf + chans);
icculus@10834
   388
        const Sint16 final_right = inbuf[finalpos+1];
icculus@10834
   389
        const Sint16 final_left = inbuf[finalpos];
icculus@10834
   390
        Sint16 earlier_right = inbuf[finalpos-1];
icculus@10834
   391
        Sint16 earlier_left = inbuf[finalpos-2];
icculus@10834
   392
        dst = outbuf + (dest_samples * chans);
icculus@10834
   393
        idx = (double) total;
icculus@10834
   394
icculus@10834
   395
        while (dst > target) {
icculus@10834
   396
            const int pos = ((int) idx) * chans;
icculus@10834
   397
            const Sint16 *src = &inbuf[pos];
icculus@10834
   398
            const Sint16 right = *(--src);
icculus@10834
   399
            const Sint16 left = *(--src);
icculus@10834
   400
            SDL_assert(pos >= 0.0);
icculus@10834
   401
            *(--dst) = (((Sint32) right) + ((Sint32) earlier_right)) >> 1;
icculus@10834
   402
            *(--dst) = (((Sint32) left) + ((Sint32) earlier_left)) >> 1;
icculus@10834
   403
            earlier_right = right;
icculus@10834
   404
            earlier_left = left;
icculus@10834
   405
            idx -= src_incr;
icculus@10834
   406
        }
icculus@10834
   407
icculus@10834
   408
        /* do last sample, interpolated against previous run's state. */
icculus@10834
   409
        *(--dst) = (((Sint32) inbuf[1]) + ((Sint32) last_sample[1])) >> 1;
icculus@10834
   410
        *(--dst) = (((Sint32) inbuf[0]) + ((Sint32) last_sample[0])) >> 1;
icculus@10834
   411
        last_sample[1] = final_right;
icculus@10834
   412
        last_sample[0] = final_left;
icculus@10834
   413
icculus@10841
   414
        dst = (outbuf + (dest_samples * chans));
icculus@10834
   415
    } else {
icculus@10834
   416
        Sint16 *target = (outbuf + (dest_samples * chans));
icculus@10834
   417
        dst = outbuf;
icculus@10834
   418
        idx = 0.0;
icculus@10834
   419
        while (dst < target) {
icculus@10834
   420
            const int pos = ((int) idx) * chans;
icculus@10834
   421
            const Sint16 *src = &inbuf[pos];
icculus@10834
   422
            const Sint16 left = *(src++);
icculus@10834
   423
            const Sint16 right = *(src++);
icculus@10834
   424
            SDL_assert(pos <= finalpos);
icculus@10834
   425
            *(dst++) = (((Sint32) left) + ((Sint32) last_sample[0])) >> 1;
icculus@10834
   426
            *(dst++) = (((Sint32) right) + ((Sint32) last_sample[1])) >> 1;
icculus@10834
   427
            last_sample[0] = left;
icculus@10834
   428
            last_sample[1] = right;
icculus@10834
   429
            idx += src_incr;
icculus@10834
   430
        }
icculus@10834
   431
    }
icculus@10834
   432
icculus@10834
   433
    return (int) ((dst - outbuf) * ((int) sizeof (Sint16)));
icculus@10834
   434
}
icculus@10834
   435
icculus@10834
   436
static void SDLCALL
icculus@10834
   437
SDL_ResampleCVT_si16_c2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
icculus@10834
   438
{
icculus@10834
   439
    const Sint16 *src = (const Sint16 *) cvt->buf;
icculus@10834
   440
    const int srclen = cvt->len_cvt;
icculus@10839
   441
    Sint16 *dst = (Sint16 *) cvt->buf;
icculus@10839
   442
    const int dstlen = (cvt->len * cvt->len_mult);
icculus@10925
   443
    Sint16 state[2];
icculus@10925
   444
icculus@10925
   445
    state[0] = src[0];
icculus@10925
   446
    state[1] = src[1];
icculus@10834
   447
icculus@10834
   448
    SDL_assert(format == AUDIO_S16SYS);
icculus@10834
   449
icculus@10834
   450
    cvt->len_cvt = SDL_ResampleAudioSimple_si16_c2(cvt->rate_incr, state, src, srclen, dst, dstlen);
icculus@10834
   451
    if (cvt->filters[++cvt->filter_index]) {
icculus@10834
   452
        cvt->filters[cvt->filter_index](cvt, format);
icculus@10834
   453
    }
icculus@10834
   454
}
icculus@10834
   455
slouken@0
   456
slouken@1895
   457
int
slouken@1895
   458
SDL_ConvertAudio(SDL_AudioCVT * cvt)
slouken@0
   459
{
icculus@3021
   460
    /* !!! FIXME: (cvt) should be const; stack-copy it here. */
icculus@3021
   461
    /* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */
icculus@3021
   462
slouken@1895
   463
    /* Make sure there's data to convert */
slouken@1895
   464
    if (cvt->buf == NULL) {
icculus@10575
   465
        return SDL_SetError("No buffer allocated for conversion");
slouken@1895
   466
    }
icculus@10575
   467
slouken@1895
   468
    /* Return okay if no conversion is necessary */
slouken@1895
   469
    cvt->len_cvt = cvt->len;
slouken@1895
   470
    if (cvt->filters[0] == NULL) {
icculus@10575
   471
        return 0;
slouken@1895
   472
    }
slouken@0
   473
slouken@1895
   474
    /* Set up the conversion and go! */
slouken@1895
   475
    cvt->filter_index = 0;
slouken@1895
   476
    cvt->filters[0] (cvt, cvt->src_format);
icculus@10575
   477
    return 0;
slouken@0
   478
}
slouken@0
   479
icculus@10575
   480
static void SDLCALL
icculus@10575
   481
SDL_Convert_Byteswap(SDL_AudioCVT *cvt, SDL_AudioFormat format)
icculus@10575
   482
{
slouken@10579
   483
#if DEBUG_CONVERT
slouken@10579
   484
    printf("Converting byte order\n");
slouken@10579
   485
#endif
icculus@1982
   486
icculus@10575
   487
    switch (SDL_AUDIO_BITSIZE(format)) {
icculus@10575
   488
        #define CASESWAP(b) \
icculus@10575
   489
            case b: { \
icculus@10575
   490
                Uint##b *ptr = (Uint##b *) cvt->buf; \
icculus@10575
   491
                int i; \
icculus@10575
   492
                for (i = cvt->len_cvt / sizeof (*ptr); i; --i, ++ptr) { \
icculus@10575
   493
                    *ptr = SDL_Swap##b(*ptr); \
icculus@10575
   494
                } \
icculus@10575
   495
                break; \
icculus@10575
   496
            }
icculus@1982
   497
icculus@10575
   498
        CASESWAP(16);
icculus@10575
   499
        CASESWAP(32);
icculus@10575
   500
        CASESWAP(64);
icculus@10575
   501
icculus@10575
   502
        #undef CASESWAP
icculus@10575
   503
icculus@10575
   504
        default: SDL_assert(!"unhandled byteswap datatype!"); break;
icculus@10575
   505
    }
icculus@10575
   506
icculus@10575
   507
    if (cvt->filters[++cvt->filter_index]) {
icculus@10575
   508
        /* flip endian flag for data. */
icculus@10575
   509
        if (format & SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   510
            format &= ~SDL_AUDIO_MASK_ENDIAN;
icculus@10575
   511
        } else {
icculus@10575
   512
            format |= SDL_AUDIO_MASK_ENDIAN;
icculus@10575
   513
        }
icculus@10575
   514
        cvt->filters[cvt->filter_index](cvt, format);
icculus@10575
   515
    }
icculus@1982
   516
}
icculus@1982
   517
slouken@11096
   518
static int
slouken@11096
   519
SDL_AddAudioCVTFilter(SDL_AudioCVT *cvt, const SDL_AudioFilter filter)
slouken@11096
   520
{
slouken@11096
   521
    if (cvt->filter_index >= SDL_AUDIOCVT_MAX_FILTERS) {
slouken@11096
   522
        return SDL_SetError("Too many filters needed for conversion, exceeded maximum of %d", SDL_AUDIOCVT_MAX_FILTERS);
slouken@11096
   523
    }
slouken@11096
   524
    if (filter == NULL) {
slouken@11096
   525
        return SDL_SetError("Audio filter pointer is NULL");
slouken@11096
   526
    }
slouken@11096
   527
    cvt->filters[cvt->filter_index++] = filter;
slouken@11096
   528
    cvt->filters[cvt->filter_index] = NULL; /* Moving terminator */
slouken@11096
   529
    return 0;
slouken@11096
   530
}
icculus@1982
   531
icculus@1982
   532
static int
icculus@10575
   533
SDL_BuildAudioTypeCVTToFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat src_fmt)
icculus@1982
   534
{
icculus@10575
   535
    int retval = 0;  /* 0 == no conversion necessary. */
icculus@1982
   536
icculus@10575
   537
    if ((SDL_AUDIO_ISBIGENDIAN(src_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
slouken@11096
   538
        if (SDL_AddAudioCVTFilter(cvt, SDL_Convert_Byteswap) < 0) {
slouken@11096
   539
            return -1;
slouken@11096
   540
        }
icculus@10575
   541
        retval = 1;  /* added a converter. */
icculus@10575
   542
    }
icculus@1982
   543
icculus@10575
   544
    if (!SDL_AUDIO_ISFLOAT(src_fmt)) {
icculus@10576
   545
        const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt);
icculus@10576
   546
        const Uint16 dst_bitsize = 32;
icculus@10575
   547
        SDL_AudioFilter filter = NULL;
icculus@10576
   548
icculus@10575
   549
        switch (src_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   550
            case AUDIO_S8: filter = SDL_Convert_S8_to_F32; break;
icculus@10575
   551
            case AUDIO_U8: filter = SDL_Convert_U8_to_F32; break;
icculus@10575
   552
            case AUDIO_S16: filter = SDL_Convert_S16_to_F32; break;
philipp@10591
   553
            case AUDIO_U16: filter = SDL_Convert_U16_to_F32; break;
icculus@10575
   554
            case AUDIO_S32: filter = SDL_Convert_S32_to_F32; break;
icculus@10575
   555
            default: SDL_assert(!"Unexpected audio format!"); break;
icculus@1982
   556
        }
icculus@1982
   557
icculus@10575
   558
        if (!filter) {
icculus@11319
   559
            return SDL_SetError("No conversion from source format to float available");
icculus@10575
   560
        }
icculus@10575
   561
slouken@11096
   562
        if (SDL_AddAudioCVTFilter(cvt, filter) < 0) {
slouken@11096
   563
            return -1;
slouken@11096
   564
        }
icculus@1982
   565
        if (src_bitsize < dst_bitsize) {
icculus@1982
   566
            const int mult = (dst_bitsize / src_bitsize);
icculus@1982
   567
            cvt->len_mult *= mult;
icculus@1982
   568
            cvt->len_ratio *= mult;
icculus@1982
   569
        } else if (src_bitsize > dst_bitsize) {
icculus@1982
   570
            cvt->len_ratio /= (src_bitsize / dst_bitsize);
icculus@1982
   571
        }
icculus@10576
   572
icculus@10575
   573
        retval = 1;  /* added a converter. */
icculus@1982
   574
    }
icculus@1982
   575
icculus@10575
   576
    return retval;
icculus@1982
   577
}
icculus@1982
   578
icculus@10575
   579
static int
icculus@10575
   580
SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
icculus@10575
   581
{
icculus@10575
   582
    int retval = 0;  /* 0 == no conversion necessary. */
icculus@3021
   583
icculus@10575
   584
    if (!SDL_AUDIO_ISFLOAT(dst_fmt)) {
icculus@10577
   585
        const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt);
icculus@10577
   586
        const Uint16 src_bitsize = 32;
icculus@10575
   587
        SDL_AudioFilter filter = NULL;
icculus@10575
   588
        switch (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   589
            case AUDIO_S8: filter = SDL_Convert_F32_to_S8; break;
icculus@10575
   590
            case AUDIO_U8: filter = SDL_Convert_F32_to_U8; break;
icculus@10575
   591
            case AUDIO_S16: filter = SDL_Convert_F32_to_S16; break;
philipp@10591
   592
            case AUDIO_U16: filter = SDL_Convert_F32_to_U16; break;
icculus@10575
   593
            case AUDIO_S32: filter = SDL_Convert_F32_to_S32; break;
icculus@10575
   594
            default: SDL_assert(!"Unexpected audio format!"); break;
icculus@10575
   595
        }
slouken@2716
   596
icculus@10575
   597
        if (!filter) {
icculus@11319
   598
            return SDL_SetError("No conversion from float to destination format available");
icculus@10575
   599
        }
icculus@10575
   600
slouken@11096
   601
        if (SDL_AddAudioCVTFilter(cvt, filter) < 0) {
slouken@11096
   602
            return -1;
slouken@11096
   603
        }
icculus@10575
   604
        if (src_bitsize < dst_bitsize) {
icculus@10575
   605
            const int mult = (dst_bitsize / src_bitsize);
icculus@10575
   606
            cvt->len_mult *= mult;
icculus@10575
   607
            cvt->len_ratio *= mult;
icculus@10575
   608
        } else if (src_bitsize > dst_bitsize) {
icculus@10575
   609
            cvt->len_ratio /= (src_bitsize / dst_bitsize);
icculus@10575
   610
        }
icculus@10575
   611
        retval = 1;  /* added a converter. */
icculus@10575
   612
    }
icculus@10575
   613
icculus@10575
   614
    if ((SDL_AUDIO_ISBIGENDIAN(dst_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
slouken@11096
   615
        if (SDL_AddAudioCVTFilter(cvt, SDL_Convert_Byteswap) < 0) {
slouken@11096
   616
            return -1;
slouken@11096
   617
        }
icculus@10575
   618
        retval = 1;  /* added a converter. */
icculus@10575
   619
    }
icculus@10575
   620
icculus@10575
   621
    return retval;
icculus@3021
   622
}
slouken@2716
   623
icculus@10799
   624
static void
icculus@10799
   625
SDL_ResampleCVT(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format)
icculus@10799
   626
{
icculus@10799
   627
    const float *src = (const float *) cvt->buf;
icculus@10799
   628
    const int srclen = cvt->len_cvt;
icculus@10833
   629
    float *dst = (float *) cvt->buf;
icculus@10833
   630
    const int dstlen = (cvt->len * cvt->len_mult);
icculus@10804
   631
    float state[8];
icculus@10756
   632
icculus@10799
   633
    SDL_assert(format == AUDIO_F32SYS);
icculus@10799
   634
slouken@10805
   635
    SDL_memcpy(state, src, chans*sizeof(*src));
icculus@10799
   636
icculus@10804
   637
    cvt->len_cvt = SDL_ResampleAudioSimple(chans, cvt->rate_incr, state, src, srclen, dst, dstlen);
icculus@10799
   638
    if (cvt->filters[++cvt->filter_index]) {
icculus@10799
   639
        cvt->filters[cvt->filter_index](cvt, format);
icculus@10799
   640
    }
icculus@10799
   641
}
icculus@10799
   642
icculus@10799
   643
/* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't
icculus@10799
   644
   !!! FIXME:  store channel info, so we have to have function entry
icculus@10799
   645
   !!! FIXME:  points for each supported channel count and multiple
icculus@10799
   646
   !!! FIXME:  vs arbitrary. When we rev the ABI, clean this up. */
icculus@10756
   647
#define RESAMPLER_FUNCS(chans) \
icculus@10756
   648
    static void SDLCALL \
icculus@10799
   649
    SDL_ResampleCVT_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10799
   650
        SDL_ResampleCVT(cvt, chans, format); \
icculus@10756
   651
    }
icculus@10756
   652
RESAMPLER_FUNCS(1)
icculus@10756
   653
RESAMPLER_FUNCS(2)
icculus@10756
   654
RESAMPLER_FUNCS(4)
icculus@10756
   655
RESAMPLER_FUNCS(6)
icculus@10756
   656
RESAMPLER_FUNCS(8)
icculus@10756
   657
#undef RESAMPLER_FUNCS
icculus@10756
   658
icculus@10799
   659
static SDL_AudioFilter
icculus@10799
   660
ChooseCVTResampler(const int dst_channels)
icculus@3021
   661
{
icculus@10799
   662
    switch (dst_channels) {
icculus@10799
   663
        case 1: return SDL_ResampleCVT_c1;
icculus@10799
   664
        case 2: return SDL_ResampleCVT_c2;
icculus@10799
   665
        case 4: return SDL_ResampleCVT_c4;
icculus@10799
   666
        case 6: return SDL_ResampleCVT_c6;
icculus@10799
   667
        case 8: return SDL_ResampleCVT_c8;
icculus@10799
   668
        default: break;
icculus@3021
   669
    }
slouken@2716
   670
icculus@10799
   671
    return NULL;
icculus@10756
   672
}
icculus@10575
   673
icculus@3021
   674
static int
icculus@10756
   675
SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels,
icculus@10756
   676
                          const int src_rate, const int dst_rate)
icculus@3021
   677
{
icculus@10756
   678
    SDL_AudioFilter filter;
icculus@3021
   679
icculus@10756
   680
    if (src_rate == dst_rate) {
icculus@10756
   681
        return 0;  /* no conversion necessary. */
slouken@2716
   682
    }
slouken@2716
   683
icculus@10799
   684
    filter = ChooseCVTResampler(dst_channels);
icculus@10756
   685
    if (filter == NULL) {
icculus@10756
   686
        return SDL_SetError("No conversion available for these rates");
icculus@10756
   687
    }
icculus@10756
   688
icculus@10756
   689
    /* Update (cvt) with filter details... */
slouken@11096
   690
    if (SDL_AddAudioCVTFilter(cvt, filter) < 0) {
slouken@11096
   691
        return -1;
slouken@11096
   692
    }
icculus@10756
   693
    if (src_rate < dst_rate) {
icculus@10756
   694
        const double mult = ((double) dst_rate) / ((double) src_rate);
icculus@10756
   695
        cvt->len_mult *= (int) SDL_ceil(mult);
icculus@10756
   696
        cvt->len_ratio *= mult;
icculus@10756
   697
    } else {
icculus@10756
   698
        cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
icculus@10756
   699
    }
icculus@10756
   700
icculus@10756
   701
    return 1;               /* added a converter. */
slouken@2716
   702
}
icculus@1982
   703
icculus@11097
   704
static SDL_bool
icculus@11097
   705
SDL_SupportedAudioFormat(const SDL_AudioFormat fmt)
icculus@11097
   706
{
icculus@11097
   707
    switch (fmt) {
icculus@11097
   708
        case AUDIO_U8:
icculus@11097
   709
        case AUDIO_S8:
icculus@11097
   710
        case AUDIO_U16LSB:
icculus@11097
   711
        case AUDIO_S16LSB:
icculus@11097
   712
        case AUDIO_U16MSB:
icculus@11097
   713
        case AUDIO_S16MSB:
icculus@11097
   714
        case AUDIO_S32LSB:
icculus@11097
   715
        case AUDIO_S32MSB:
icculus@11097
   716
        case AUDIO_F32LSB:
icculus@11097
   717
        case AUDIO_F32MSB:
icculus@11097
   718
            return SDL_TRUE;  /* supported. */
icculus@11097
   719
icculus@11097
   720
        default:
icculus@11097
   721
            break;
icculus@11097
   722
    }
icculus@11097
   723
icculus@11097
   724
    return SDL_FALSE;  /* unsupported. */
icculus@11097
   725
}
icculus@11097
   726
icculus@11097
   727
static SDL_bool
icculus@11097
   728
SDL_SupportedChannelCount(const int channels)
icculus@11097
   729
{
icculus@11097
   730
    switch (channels) {
icculus@11097
   731
        case 1:  /* mono */
icculus@11097
   732
        case 2:  /* stereo */
icculus@11097
   733
        case 4:  /* quad */
icculus@11097
   734
        case 6:  /* 5.1 */
icculus@11097
   735
            return SDL_TRUE;  /* supported. */
icculus@11097
   736
icculus@11097
   737
        case 8:  /* !!! FIXME: 7.1 */
icculus@11097
   738
        default:
icculus@11097
   739
            break;
icculus@11097
   740
    }
icculus@11097
   741
icculus@11097
   742
    return SDL_FALSE;  /* unsupported. */
icculus@11097
   743
}
icculus@11097
   744
icculus@1982
   745
icculus@1982
   746
/* Creates a set of audio filters to convert from one format to another.
icculus@11319
   747
   Returns 0 if no conversion is needed, 1 if the audio filter is set up,
icculus@11319
   748
   or -1 if an error like invalid parameter, unsupported format, etc. occurred.
slouken@0
   749
*/
slouken@1895
   750
slouken@1895
   751
int
slouken@1895
   752
SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
icculus@1982
   753
                  SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate,
icculus@1982
   754
                  SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate)
slouken@0
   755
{
aschiffler@6819
   756
    /* Sanity check target pointer */
aschiffler@6819
   757
    if (cvt == NULL) {
icculus@7037
   758
        return SDL_InvalidParamError("cvt");
aschiffler@6819
   759
    }
slouken@7191
   760
icculus@11319
   761
    /* Conversions from and to float require the audio subsystem to be initialized */
icculus@11319
   762
    if (!SDL_WasInit(SDL_INIT_AUDIO)) {
icculus@11319
   763
        return SDL_SetError("Audio subsystem has not been initialized");
icculus@11319
   764
    }
icculus@11319
   765
slouken@10767
   766
    /* Make sure we zero out the audio conversion before error checking */
slouken@10767
   767
    SDL_zerop(cvt);
slouken@10767
   768
icculus@11097
   769
    if (!SDL_SupportedAudioFormat(src_fmt)) {
icculus@7037
   770
        return SDL_SetError("Invalid source format");
icculus@11097
   771
    } else if (!SDL_SupportedAudioFormat(dst_fmt)) {
icculus@7037
   772
        return SDL_SetError("Invalid destination format");
icculus@11097
   773
    } else if (!SDL_SupportedChannelCount(src_channels)) {
icculus@11097
   774
        return SDL_SetError("Invalid source channels");
icculus@11097
   775
    } else if (!SDL_SupportedChannelCount(dst_channels)) {
icculus@11097
   776
        return SDL_SetError("Invalid destination channels");
icculus@11097
   777
    } else if (src_rate == 0) {
icculus@11097
   778
        return SDL_SetError("Source rate is zero");
icculus@11097
   779
    } else if (dst_rate == 0) {
icculus@11097
   780
        return SDL_SetError("Destination rate is zero");
icculus@1982
   781
    }
icculus@3021
   782
slouken@10579
   783
#if DEBUG_CONVERT
icculus@1982
   784
    printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n",
slouken@1985
   785
           src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate);
slouken@1985
   786
#endif
icculus@1982
   787
slouken@1895
   788
    /* Start off with no conversion necessary */
icculus@1982
   789
    cvt->src_format = src_fmt;
icculus@1982
   790
    cvt->dst_format = dst_fmt;
slouken@1895
   791
    cvt->needed = 0;
slouken@1895
   792
    cvt->filter_index = 0;
slouken@1895
   793
    cvt->filters[0] = NULL;
slouken@1895
   794
    cvt->len_mult = 1;
slouken@1895
   795
    cvt->len_ratio = 1.0;
icculus@3021
   796
    cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);
slouken@0
   797
icculus@10834
   798
    /* SDL now favors float32 as its preferred internal format, and considers
icculus@10834
   799
       everything else to be a degenerate case that we might have to make
icculus@10834
   800
       multiple passes over the data to convert to and from float32 as
icculus@10834
   801
       necessary. That being said, we keep one special case around for
icculus@10834
   802
       efficiency: stereo data in Sint16 format, in the native byte order,
icculus@10834
   803
       that only needs resampling. This is likely to be the most popular
icculus@10834
   804
       legacy format, that apps, hardware and the OS are likely to be able
icculus@10834
   805
       to process directly, so we handle this one case directly without
icculus@10834
   806
       unnecessary conversions. This means that apps on embedded devices
icculus@10834
   807
       without floating point hardware should consider aiming for this
icculus@10834
   808
       format as well. */
icculus@10834
   809
    if ((src_channels == 2) && (dst_channels == 2) && (src_fmt == AUDIO_S16SYS) && (dst_fmt == AUDIO_S16SYS) && (src_rate != dst_rate)) {
icculus@10834
   810
        cvt->needed = 1;
slouken@11096
   811
        if (SDL_AddAudioCVTFilter(cvt, SDL_ResampleCVT_si16_c2) < 0) {
slouken@11096
   812
            return -1;
slouken@11096
   813
        }
icculus@10834
   814
        if (src_rate < dst_rate) {
icculus@10834
   815
            const double mult = ((double) dst_rate) / ((double) src_rate);
icculus@10834
   816
            cvt->len_mult *= (int) SDL_ceil(mult);
icculus@10834
   817
            cvt->len_ratio *= mult;
icculus@10834
   818
        } else {
icculus@10834
   819
            cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
icculus@10834
   820
        }
icculus@10834
   821
        return 1;
icculus@10834
   822
    }
icculus@10834
   823
icculus@10575
   824
    /* Type conversion goes like this now:
icculus@10575
   825
        - byteswap to CPU native format first if necessary.
icculus@10575
   826
        - convert to native Float32 if necessary.
icculus@10575
   827
        - resample and change channel count if necessary.
icculus@10575
   828
        - convert back to native format.
icculus@10575
   829
        - byteswap back to foreign format if necessary.
icculus@10575
   830
icculus@10575
   831
       The expectation is we can process data faster in float32
icculus@10575
   832
       (possibly with SIMD), and making several passes over the same
icculus@10756
   833
       buffer is likely to be CPU cache-friendly, avoiding the
icculus@10575
   834
       biggest performance hit in modern times. Previously we had
icculus@10575
   835
       (script-generated) custom converters for every data type and
icculus@10575
   836
       it was a bloat on SDL compile times and final library size. */
icculus@10575
   837
slouken@10767
   838
    /* see if we can skip float conversion entirely. */
slouken@10767
   839
    if (src_rate == dst_rate && src_channels == dst_channels) {
slouken@10767
   840
        if (src_fmt == dst_fmt) {
slouken@10767
   841
            return 0;
slouken@10767
   842
        }
slouken@10767
   843
slouken@10767
   844
        /* just a byteswap needed? */
slouken@10767
   845
        if ((src_fmt & ~SDL_AUDIO_MASK_ENDIAN) == (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN)) {
slouken@11096
   846
            if (SDL_AddAudioCVTFilter(cvt, SDL_Convert_Byteswap) < 0) {
slouken@11096
   847
                return -1;
slouken@11096
   848
            }
slouken@10767
   849
            cvt->needed = 1;
slouken@10767
   850
            return 1;
slouken@10767
   851
        }
icculus@10575
   852
    }
icculus@10575
   853
icculus@1982
   854
    /* Convert data types, if necessary. Updates (cvt). */
slouken@10767
   855
    if (SDL_BuildAudioTypeCVTToFloat(cvt, src_fmt) < 0) {
slouken@1985
   856
        return -1;              /* shouldn't happen, but just in case... */
icculus@3021
   857
    }
slouken@0
   858
icculus@1982
   859
    /* Channel conversion */
slouken@1895
   860
    if (src_channels != dst_channels) {
slouken@1895
   861
        if ((src_channels == 1) && (dst_channels > 1)) {
slouken@11096
   862
            if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertMonoToStereo) < 0) {
slouken@11096
   863
                return -1;
slouken@11096
   864
            }
slouken@1895
   865
            cvt->len_mult *= 2;
slouken@1895
   866
            src_channels = 2;
slouken@1895
   867
            cvt->len_ratio *= 2;
slouken@1895
   868
        }
slouken@1895
   869
        if ((src_channels == 2) && (dst_channels == 6)) {
slouken@11096
   870
            if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertStereoTo51) < 0) {
slouken@11096
   871
                return -1;
slouken@11096
   872
            }
slouken@1895
   873
            src_channels = 6;
slouken@1895
   874
            cvt->len_mult *= 3;
slouken@1895
   875
            cvt->len_ratio *= 3;
slouken@1895
   876
        }
slouken@1895
   877
        if ((src_channels == 2) && (dst_channels == 4)) {
slouken@11096
   878
            if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertStereoToQuad) < 0) {
slouken@11096
   879
                return -1;
slouken@11096
   880
            }
slouken@1895
   881
            src_channels = 4;
slouken@1895
   882
            cvt->len_mult *= 2;
slouken@1895
   883
            cvt->len_ratio *= 2;
slouken@1895
   884
        }
slouken@1895
   885
        while ((src_channels * 2) <= dst_channels) {
slouken@11096
   886
            if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertMonoToStereo) < 0) {
slouken@11096
   887
                return -1;
slouken@11096
   888
            }
slouken@1895
   889
            cvt->len_mult *= 2;
slouken@1895
   890
            src_channels *= 2;
slouken@1895
   891
            cvt->len_ratio *= 2;
slouken@1895
   892
        }
slouken@1895
   893
        if ((src_channels == 6) && (dst_channels <= 2)) {
slouken@11096
   894
            if (SDL_AddAudioCVTFilter(cvt, SDL_Convert51ToStereo) < 0) {
slouken@11096
   895
                return -1;
slouken@11096
   896
            }
slouken@1895
   897
            src_channels = 2;
slouken@1895
   898
            cvt->len_ratio /= 3;
slouken@1895
   899
        }
slouken@1895
   900
        if ((src_channels == 6) && (dst_channels == 4)) {
slouken@11096
   901
            if (SDL_AddAudioCVTFilter(cvt, SDL_Convert51ToQuad) < 0) {
slouken@11096
   902
                return -1;
slouken@11096
   903
            }
slouken@1895
   904
            src_channels = 4;
slouken@1895
   905
            cvt->len_ratio /= 2;
slouken@1895
   906
        }
slouken@1895
   907
        /* This assumes that 4 channel audio is in the format:
slouken@1895
   908
           Left {front/back} + Right {front/back}
slouken@1895
   909
           so converting to L/R stereo works properly.
slouken@1895
   910
         */
slouken@1895
   911
        while (((src_channels % 2) == 0) &&
slouken@1895
   912
               ((src_channels / 2) >= dst_channels)) {
icculus@10832
   913
            SDL_AudioFilter filter = NULL;
icculus@10832
   914
icculus@10832
   915
            #if HAVE_SSE3_INTRINSICS
icculus@10832
   916
            if (SDL_HasSSE3()) {
icculus@10832
   917
                filter = SDL_ConvertStereoToMono_SSE3;
icculus@10832
   918
            }
icculus@10832
   919
            #endif
icculus@10832
   920
icculus@10832
   921
            if (!filter) {
icculus@10832
   922
                filter = SDL_ConvertStereoToMono;
icculus@10832
   923
            }
icculus@10832
   924
slouken@11096
   925
            if (SDL_AddAudioCVTFilter(cvt, filter) < 0) {
slouken@11096
   926
                return -1;
slouken@11096
   927
            }
icculus@10832
   928
slouken@1895
   929
            src_channels /= 2;
slouken@1895
   930
            cvt->len_ratio /= 2;
slouken@1895
   931
        }
slouken@1895
   932
        if (src_channels != dst_channels) {
slouken@1895
   933
            /* Uh oh.. */ ;
slouken@1895
   934
        }
slouken@1895
   935
    }
slouken@0
   936
icculus@3021
   937
    /* Do rate conversion, if necessary. Updates (cvt). */
slouken@10767
   938
    if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) < 0) {
icculus@3021
   939
        return -1;              /* shouldn't happen, but just in case... */
slouken@2716
   940
    }
slouken@2716
   941
icculus@10756
   942
    /* Move to final data type. */
slouken@10767
   943
    if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) < 0) {
icculus@10575
   944
        return -1;              /* shouldn't happen, but just in case... */
slouken@1895
   945
    }
icculus@10575
   946
icculus@10575
   947
    cvt->needed = (cvt->filter_index != 0);
slouken@1895
   948
    return (cvt->needed);
slouken@0
   949
}
slouken@1895
   950
icculus@10842
   951
typedef int (*SDL_ResampleAudioStreamFunc)(SDL_AudioStream *stream, const void *inbuf, const int inbuflen, void *outbuf, const int outbuflen);
slouken@10773
   952
typedef void (*SDL_ResetAudioStreamResamplerFunc)(SDL_AudioStream *stream);
slouken@10773
   953
typedef void (*SDL_CleanupAudioStreamResamplerFunc)(SDL_AudioStream *stream);
icculus@10757
   954
icculus@10757
   955
struct SDL_AudioStream
icculus@10757
   956
{
icculus@10757
   957
    SDL_AudioCVT cvt_before_resampling;
icculus@10757
   958
    SDL_AudioCVT cvt_after_resampling;
icculus@10757
   959
    SDL_DataQueue *queue;
icculus@10844
   960
    Uint8 *work_buffer_base;  /* maybe unaligned pointer from SDL_realloc(). */
icculus@10757
   961
    int work_buffer_len;
icculus@10757
   962
    int src_sample_frame_size;
icculus@10757
   963
    SDL_AudioFormat src_format;
icculus@10757
   964
    Uint8 src_channels;
icculus@10757
   965
    int src_rate;
icculus@10757
   966
    int dst_sample_frame_size;
icculus@10757
   967
    SDL_AudioFormat dst_format;
icculus@10757
   968
    Uint8 dst_channels;
icculus@10757
   969
    int dst_rate;
icculus@10757
   970
    double rate_incr;
icculus@10757
   971
    Uint8 pre_resample_channels;
slouken@10773
   972
    int packetlen;
slouken@10773
   973
    void *resampler_state;
slouken@10773
   974
    SDL_ResampleAudioStreamFunc resampler_func;
slouken@10773
   975
    SDL_ResetAudioStreamResamplerFunc reset_resampler_func;
slouken@10773
   976
    SDL_CleanupAudioStreamResamplerFunc cleanup_resampler_func;
slouken@10773
   977
};
slouken@10773
   978
icculus@10851
   979
static Uint8 *
icculus@10851
   980
EnsureStreamBufferSize(SDL_AudioStream *stream, const int newlen)
icculus@10851
   981
{
icculus@10851
   982
    Uint8 *ptr;
icculus@10851
   983
    size_t offset;
icculus@10851
   984
icculus@10851
   985
    if (stream->work_buffer_len >= newlen) {
icculus@10851
   986
        ptr = stream->work_buffer_base;
icculus@10851
   987
    } else {
icculus@10851
   988
        ptr = (Uint8 *) SDL_realloc(stream->work_buffer_base, newlen + 32);
icculus@10851
   989
        if (!ptr) {
icculus@10851
   990
            SDL_OutOfMemory();
icculus@10851
   991
            return NULL;
icculus@10851
   992
        }
icculus@10851
   993
        /* Make sure we're aligned to 16 bytes for SIMD code. */
icculus@10851
   994
        stream->work_buffer_base = ptr;
icculus@10851
   995
        stream->work_buffer_len = newlen;
icculus@10851
   996
    }
icculus@10851
   997
icculus@10851
   998
    offset = ((size_t) ptr) & 15;
icculus@10851
   999
    return offset ? ptr + (16 - offset) : ptr;
icculus@10851
  1000
}
icculus@10851
  1001
slouken@10777
  1002
#ifdef HAVE_LIBSAMPLERATE_H
slouken@10773
  1003
static int
icculus@10842
  1004
SDL_ResampleAudioStream_SRC(SDL_AudioStream *stream, const void *_inbuf, const int inbuflen, void *_outbuf, const int outbuflen)
slouken@10773
  1005
{
icculus@10842
  1006
    const float *inbuf = (const float *) _inbuf;
icculus@10842
  1007
    float *outbuf = (float *) _outbuf;
icculus@10799
  1008
    const int framelen = sizeof(float) * stream->pre_resample_channels;
icculus@10790
  1009
    SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
slouken@10773
  1010
    SRC_DATA data;
slouken@10773
  1011
    int result;
slouken@10773
  1012
icculus@10851
  1013
    if (inbuf == ((const float *) outbuf)) {  /* libsamplerate can't work in-place. */
icculus@10851
  1014
        Uint8 *ptr = EnsureStreamBufferSize(stream, inbuflen + outbuflen);
icculus@10851
  1015
        if (ptr == NULL) {
icculus@10851
  1016
            SDL_OutOfMemory();
icculus@10851
  1017
            return 0;
icculus@10851
  1018
        }
icculus@10851
  1019
        SDL_memcpy(ptr + outbuflen, ptr, inbuflen);
icculus@10851
  1020
        inbuf = (const float *) (ptr + outbuflen);
icculus@10851
  1021
        outbuf = (float *) ptr;
icculus@10851
  1022
    }
icculus@10851
  1023
slouken@10777
  1024
    data.data_in = (float *)inbuf; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */
icculus@10799
  1025
    data.input_frames = inbuflen / framelen;
slouken@10773
  1026
    data.input_frames_used = 0;
slouken@10773
  1027
slouken@10773
  1028
    data.data_out = outbuf;
icculus@10799
  1029
    data.output_frames = outbuflen / framelen;
slouken@10773
  1030
slouken@10773
  1031
    data.end_of_input = 0;
slouken@10773
  1032
    data.src_ratio = stream->rate_incr;
slouken@10773
  1033
icculus@10790
  1034
    result = SRC_src_process(state, &data);
slouken@10773
  1035
    if (result != 0) {
icculus@10790
  1036
        SDL_SetError("src_process() failed: %s", SRC_src_strerror(result));
slouken@10773
  1037
        return 0;
slouken@10773
  1038
    }
slouken@10773
  1039
slouken@10773
  1040
    /* If this fails, we need to store them off somewhere */
slouken@10773
  1041
    SDL_assert(data.input_frames_used == data.input_frames);
slouken@10773
  1042
slouken@10773
  1043
    return data.output_frames_gen * (sizeof(float) * stream->pre_resample_channels);
slouken@10773
  1044
}
slouken@10773
  1045
slouken@10773
  1046
static void
slouken@10773
  1047
SDL_ResetAudioStreamResampler_SRC(SDL_AudioStream *stream)
slouken@10773
  1048
{
icculus@10790
  1049
    SRC_src_reset((SRC_STATE *)stream->resampler_state);
slouken@10773
  1050
}
slouken@10773
  1051
slouken@10773
  1052
static void
slouken@10773
  1053
SDL_CleanupAudioStreamResampler_SRC(SDL_AudioStream *stream)
slouken@10773
  1054
{
icculus@10790
  1055
    SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
slouken@10773
  1056
    if (state) {
icculus@10790
  1057
        SRC_src_delete(state);
slouken@10773
  1058
    }
slouken@10773
  1059
slouken@10773
  1060
    stream->resampler_state = NULL;
slouken@10773
  1061
    stream->resampler_func = NULL;
slouken@10773
  1062
    stream->reset_resampler_func = NULL;
slouken@10773
  1063
    stream->cleanup_resampler_func = NULL;
slouken@10773
  1064
}
slouken@10773
  1065
slouken@10773
  1066
static SDL_bool
slouken@10773
  1067
SetupLibSampleRateResampling(SDL_AudioStream *stream)
slouken@10773
  1068
{
icculus@10790
  1069
    int result = 0;
icculus@10790
  1070
    SRC_STATE *state = NULL;
slouken@10773
  1071
icculus@10790
  1072
    if (SRC_available) {
icculus@10849
  1073
        state = SRC_src_new(SRC_converter, stream->pre_resample_channels, &result);
icculus@10790
  1074
        if (!state) {
icculus@10790
  1075
            SDL_SetError("src_new() failed: %s", SRC_src_strerror(result));
icculus@10790
  1076
        }
slouken@10773
  1077
    }
slouken@10773
  1078
icculus@10790
  1079
    if (!state) {
icculus@10790
  1080
        SDL_CleanupAudioStreamResampler_SRC(stream);
slouken@10773
  1081
        return SDL_FALSE;
slouken@10773
  1082
    }
slouken@10773
  1083
slouken@10773
  1084
    stream->resampler_state = state;
slouken@10773
  1085
    stream->resampler_func = SDL_ResampleAudioStream_SRC;
slouken@10773
  1086
    stream->reset_resampler_func = SDL_ResetAudioStreamResampler_SRC;
slouken@10773
  1087
    stream->cleanup_resampler_func = SDL_CleanupAudioStreamResampler_SRC;
slouken@10773
  1088
slouken@10773
  1089
    return SDL_TRUE;
slouken@10773
  1090
}
icculus@10790
  1091
#endif /* HAVE_LIBSAMPLERATE_H */
slouken@10773
  1092
slouken@10773
  1093
slouken@10773
  1094
typedef struct
slouken@10773
  1095
{
icculus@10757
  1096
    SDL_bool resampler_seeded;
icculus@10842
  1097
    union
icculus@10842
  1098
    {
icculus@10842
  1099
        float f[8];
icculus@10842
  1100
        Sint16 si16[2];
icculus@10842
  1101
    } resampler_state;
slouken@10773
  1102
} SDL_AudioStreamResamplerState;
slouken@10773
  1103
slouken@10773
  1104
static int
icculus@10842
  1105
SDL_ResampleAudioStream(SDL_AudioStream *stream, const void *_inbuf, const int inbuflen, void *_outbuf, const int outbuflen)
slouken@10773
  1106
{
icculus@10842
  1107
    const float *inbuf = (const float *) _inbuf;
icculus@10842
  1108
    float *outbuf = (float *) _outbuf;
slouken@10773
  1109
    SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
slouken@10773
  1110
    const int chans = (int)stream->pre_resample_channels;
slouken@10773
  1111
icculus@10842
  1112
    SDL_assert(chans <= SDL_arraysize(state->resampler_state.f));
slouken@10773
  1113
slouken@10773
  1114
    if (!state->resampler_seeded) {
icculus@10842
  1115
        SDL_memcpy(state->resampler_state.f, inbuf, chans * sizeof (float));
slouken@10773
  1116
        state->resampler_seeded = SDL_TRUE;
slouken@10773
  1117
    }
slouken@10773
  1118
icculus@10842
  1119
    return SDL_ResampleAudioSimple(chans, stream->rate_incr, state->resampler_state.f, inbuf, inbuflen, outbuf, outbuflen);
icculus@10842
  1120
}
icculus@10842
  1121
icculus@10842
  1122
static int
icculus@10842
  1123
SDL_ResampleAudioStream_si16_c2(SDL_AudioStream *stream, const void *_inbuf, const int inbuflen, void *_outbuf, const int outbuflen)
icculus@10842
  1124
{
icculus@10842
  1125
    const Sint16 *inbuf = (const Sint16 *) _inbuf;
icculus@10842
  1126
    Sint16 *outbuf = (Sint16 *) _outbuf;
icculus@10842
  1127
    SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
icculus@10842
  1128
icculus@10926
  1129
    SDL_assert(((int)stream->pre_resample_channels) <= SDL_arraysize(state->resampler_state.si16));
icculus@10842
  1130
icculus@10842
  1131
    if (!state->resampler_seeded) {
icculus@10842
  1132
        state->resampler_state.si16[0] = inbuf[0];
icculus@10842
  1133
        state->resampler_state.si16[1] = inbuf[1];
icculus@10842
  1134
        state->resampler_seeded = SDL_TRUE;
icculus@10842
  1135
    }
icculus@10842
  1136
icculus@10842
  1137
    return SDL_ResampleAudioSimple_si16_c2(stream->rate_incr, state->resampler_state.si16, inbuf, inbuflen, outbuf, outbuflen);
slouken@10773
  1138
}
slouken@10773
  1139
slouken@10773
  1140
static void
slouken@10773
  1141
SDL_ResetAudioStreamResampler(SDL_AudioStream *stream)
slouken@10773
  1142
{
slouken@10773
  1143
    SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
slouken@10773
  1144
    state->resampler_seeded = SDL_FALSE;
slouken@10773
  1145
}
slouken@10773
  1146
slouken@10773
  1147
static void
slouken@10773
  1148
SDL_CleanupAudioStreamResampler(SDL_AudioStream *stream)
slouken@10773
  1149
{
slouken@10773
  1150
    SDL_free(stream->resampler_state);
slouken@10773
  1151
}
icculus@10757
  1152
icculus@10789
  1153
SDL_AudioStream *
icculus@10789
  1154
SDL_NewAudioStream(const SDL_AudioFormat src_format,
icculus@10789
  1155
                   const Uint8 src_channels,
icculus@10789
  1156
                   const int src_rate,
icculus@10789
  1157
                   const SDL_AudioFormat dst_format,
icculus@10789
  1158
                   const Uint8 dst_channels,
icculus@10789
  1159
                   const int dst_rate)
icculus@10757
  1160
{
icculus@10757
  1161
    const int packetlen = 4096;  /* !!! FIXME: good enough for now. */
icculus@10757
  1162
    Uint8 pre_resample_channels;
icculus@10757
  1163
    SDL_AudioStream *retval;
icculus@10842
  1164
#ifndef HAVE_LIBSAMPLERATE_H
icculus@10842
  1165
    const SDL_bool SRC_available = SDL_FALSE;
icculus@10842
  1166
#endif
icculus@10757
  1167
icculus@10757
  1168
    retval = (SDL_AudioStream *) SDL_calloc(1, sizeof (SDL_AudioStream));
icculus@10757
  1169
    if (!retval) {
icculus@10757
  1170
        return NULL;
icculus@10757
  1171
    }
icculus@10757
  1172
icculus@10757
  1173
    /* If increasing channels, do it after resampling, since we'd just
icculus@10757
  1174
       do more work to resample duplicate channels. If we're decreasing, do
icculus@10757
  1175
       it first so we resample the interpolated data instead of interpolating
icculus@10757
  1176
       the resampled data (!!! FIXME: decide if that works in practice, though!). */
icculus@10757
  1177
    pre_resample_channels = SDL_min(src_channels, dst_channels);
icculus@10757
  1178
icculus@10883
  1179
    retval->src_sample_frame_size = (SDL_AUDIO_BITSIZE(src_format) / 8) * src_channels;
icculus@10757
  1180
    retval->src_format = src_format;
icculus@10757
  1181
    retval->src_channels = src_channels;
icculus@10757
  1182
    retval->src_rate = src_rate;
icculus@10883
  1183
    retval->dst_sample_frame_size = (SDL_AUDIO_BITSIZE(dst_format) / 8) * dst_channels;
icculus@10757
  1184
    retval->dst_format = dst_format;
icculus@10757
  1185
    retval->dst_channels = dst_channels;
icculus@10757
  1186
    retval->dst_rate = dst_rate;
icculus@10757
  1187
    retval->pre_resample_channels = pre_resample_channels;
icculus@10757
  1188
    retval->packetlen = packetlen;
icculus@10757
  1189
    retval->rate_incr = ((double) dst_rate) / ((double) src_rate);
icculus@10757
  1190
icculus@10757
  1191
    /* Not resampling? It's an easy conversion (and maybe not even that!). */
icculus@10757
  1192
    if (src_rate == dst_rate) {
icculus@10757
  1193
        retval->cvt_before_resampling.needed = SDL_FALSE;
slouken@10773
  1194
        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
slouken@10773
  1195
            SDL_FreeAudioStream(retval);
icculus@10757
  1196
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
  1197
        }
icculus@10842
  1198
    /* fast path special case for stereo Sint16 data that just needs resampling. */
icculus@10842
  1199
    } else if ((!SRC_available) && (src_channels == 2) && (dst_channels == 2) && (src_format == AUDIO_S16SYS) && (dst_format == AUDIO_S16SYS)) {
icculus@10842
  1200
        SDL_assert(src_rate != dst_rate);
icculus@10842
  1201
        retval->resampler_state = SDL_calloc(1, sizeof(SDL_AudioStreamResamplerState));
icculus@10842
  1202
        if (!retval->resampler_state) {
icculus@10842
  1203
            SDL_FreeAudioStream(retval);
icculus@10842
  1204
            SDL_OutOfMemory();
icculus@10842
  1205
            return NULL;
icculus@10842
  1206
        }
icculus@10842
  1207
        retval->resampler_func = SDL_ResampleAudioStream_si16_c2;
icculus@10842
  1208
        retval->reset_resampler_func = SDL_ResetAudioStreamResampler;
icculus@10842
  1209
        retval->cleanup_resampler_func = SDL_CleanupAudioStreamResampler;
icculus@10757
  1210
    } else {
icculus@10757
  1211
        /* Don't resample at first. Just get us to Float32 format. */
icculus@10757
  1212
        /* !!! FIXME: convert to int32 on devices without hardware float. */
slouken@10773
  1213
        if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) < 0) {
slouken@10773
  1214
            SDL_FreeAudioStream(retval);
icculus@10757
  1215
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
  1216
        }
icculus@10757
  1217
slouken@10777
  1218
#ifdef HAVE_LIBSAMPLERATE_H
slouken@10773
  1219
        SetupLibSampleRateResampling(retval);
slouken@10773
  1220
#endif
slouken@10773
  1221
slouken@10773
  1222
        if (!retval->resampler_func) {
slouken@10773
  1223
            retval->resampler_state = SDL_calloc(1, sizeof(SDL_AudioStreamResamplerState));
slouken@10773
  1224
            if (!retval->resampler_state) {
slouken@10773
  1225
                SDL_FreeAudioStream(retval);
slouken@10773
  1226
                SDL_OutOfMemory();
slouken@10773
  1227
                return NULL;
slouken@10773
  1228
            }
slouken@10773
  1229
            retval->resampler_func = SDL_ResampleAudioStream;
slouken@10773
  1230
            retval->reset_resampler_func = SDL_ResetAudioStreamResampler;
slouken@10773
  1231
            retval->cleanup_resampler_func = SDL_CleanupAudioStreamResampler;
slouken@10773
  1232
        }
slouken@10773
  1233
icculus@10757
  1234
        /* Convert us to the final format after resampling. */
slouken@10773
  1235
        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
slouken@10773
  1236
            SDL_FreeAudioStream(retval);
icculus@10757
  1237
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
  1238
        }
icculus@10757
  1239
    }
icculus@10757
  1240
icculus@10757
  1241
    retval->queue = SDL_NewDataQueue(packetlen, packetlen * 2);
icculus@10757
  1242
    if (!retval->queue) {
slouken@10773
  1243
        SDL_FreeAudioStream(retval);
icculus@10757
  1244
        return NULL;  /* SDL_NewDataQueue should have called SDL_SetError. */
icculus@10757
  1245
    }
icculus@10757
  1246
icculus@10757
  1247
    return retval;
icculus@10757
  1248
}
icculus@10757
  1249
icculus@10757
  1250
int
icculus@10757
  1251
SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, const Uint32 _buflen)
icculus@10757
  1252
{
icculus@10757
  1253
    int buflen = (int) _buflen;
icculus@10846
  1254
    const void *origbuf = buf;
icculus@10757
  1255
icculus@10844
  1256
    /* !!! FIXME: several converters can take advantage of SIMD, but only
icculus@10844
  1257
       !!! FIXME:  if the data is aligned to 16 bytes. EnsureStreamBufferSize()
icculus@10844
  1258
       !!! FIXME:  guarantees the buffer will align, but the
icculus@10844
  1259
       !!! FIXME:  converters will iterate over the data backwards if
icculus@10844
  1260
       !!! FIXME:  the output grows, and this means we won't align if buflen
icculus@10844
  1261
       !!! FIXME:  isn't a multiple of 16. In these cases, we should chop off
icculus@10844
  1262
       !!! FIXME:  a few samples at the end and convert them separately. */
icculus@10844
  1263
icculus@10757
  1264
    if (!stream) {
icculus@10757
  1265
        return SDL_InvalidParamError("stream");
icculus@10757
  1266
    } else if (!buf) {
icculus@10757
  1267
        return SDL_InvalidParamError("buf");
icculus@10757
  1268
    } else if (buflen == 0) {
icculus@10757
  1269
        return 0;  /* nothing to do. */
icculus@10757
  1270
    } else if ((buflen % stream->src_sample_frame_size) != 0) {
icculus@10757
  1271
        return SDL_SetError("Can't add partial sample frames");
icculus@10757
  1272
    }
icculus@10757
  1273
icculus@10757
  1274
    if (stream->cvt_before_resampling.needed) {
icculus@10757
  1275
        const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
icculus@10844
  1276
        Uint8 *workbuf = EnsureStreamBufferSize(stream, workbuflen);
icculus@10757
  1277
        if (workbuf == NULL) {
icculus@10757
  1278
            return -1;  /* probably out of memory. */
icculus@10757
  1279
        }
icculus@10846
  1280
        SDL_assert(buf == origbuf);
icculus@10757
  1281
        SDL_memcpy(workbuf, buf, buflen);
icculus@10757
  1282
        stream->cvt_before_resampling.buf = workbuf;
icculus@10757
  1283
        stream->cvt_before_resampling.len = buflen;
icculus@10757
  1284
        if (SDL_ConvertAudio(&stream->cvt_before_resampling) == -1) {
icculus@10757
  1285
            return -1;   /* uhoh! */
icculus@10757
  1286
        }
icculus@10757
  1287
        buf = workbuf;
icculus@10757
  1288
        buflen = stream->cvt_before_resampling.len_cvt;
icculus@10757
  1289
    }
icculus@10757
  1290
icculus@10757
  1291
    if (stream->dst_rate != stream->src_rate) {
icculus@10757
  1292
        const int workbuflen = buflen * ((int) SDL_ceil(stream->rate_incr));
icculus@10844
  1293
        Uint8 *workbuf = EnsureStreamBufferSize(stream, workbuflen);
icculus@10757
  1294
        if (workbuf == NULL) {
icculus@10757
  1295
            return -1;  /* probably out of memory. */
icculus@10757
  1296
        }
icculus@10851
  1297
        /* don't SDL_memcpy(workbuf, buf, buflen) here; our resampler can work inplace or not,
icculus@10851
  1298
           libsamplerate needs buffers to be separate; either way, avoid a copy here if possible. */
icculus@10851
  1299
        if (buf != origbuf) {
icculus@10851
  1300
            buf = workbuf;  /* in case we realloc()'d the pointer. */
icculus@10843
  1301
        }
icculus@10851
  1302
        buflen = stream->resampler_func(stream, buf, buflen, workbuf, workbuflen);
icculus@10851
  1303
        buf = EnsureStreamBufferSize(stream, workbuflen);
icculus@10851
  1304
        SDL_assert(buf != NULL);  /* shouldn't be growing, just aligning. */
icculus@10757
  1305
    }
icculus@10757
  1306
icculus@10757
  1307
    if (stream->cvt_after_resampling.needed) {
icculus@10842
  1308
        const int workbuflen = buflen * stream->cvt_after_resampling.len_mult;  /* will be "* 1" if not needed */
icculus@10844
  1309
        Uint8 *workbuf = EnsureStreamBufferSize(stream, workbuflen);
icculus@10757
  1310
        if (workbuf == NULL) {
icculus@10757
  1311
            return -1;  /* probably out of memory. */
icculus@10757
  1312
        }
icculus@10846
  1313
        if (buf == origbuf) {  /* copy if we haven't before. */
icculus@11128
  1314
            SDL_memcpy(workbuf, origbuf, buflen);
icculus@10843
  1315
        }
icculus@10757
  1316
        stream->cvt_after_resampling.buf = workbuf;
icculus@10757
  1317
        stream->cvt_after_resampling.len = buflen;
icculus@10757
  1318
        if (SDL_ConvertAudio(&stream->cvt_after_resampling) == -1) {
icculus@10757
  1319
            return -1;   /* uhoh! */
icculus@10757
  1320
        }
icculus@10757
  1321
        buf = workbuf;
icculus@10757
  1322
        buflen = stream->cvt_after_resampling.len_cvt;
icculus@10757
  1323
    }
icculus@10757
  1324
icculus@10757
  1325
    return SDL_WriteToDataQueue(stream->queue, buf, buflen);
icculus@10757
  1326
}
icculus@10757
  1327
icculus@10757
  1328
void
icculus@10757
  1329
SDL_AudioStreamClear(SDL_AudioStream *stream)
icculus@10757
  1330
{
icculus@10757
  1331
    if (!stream) {
icculus@10757
  1332
        SDL_InvalidParamError("stream");
icculus@10757
  1333
    } else {
icculus@10757
  1334
        SDL_ClearDataQueue(stream->queue, stream->packetlen * 2);
icculus@10776
  1335
        if (stream->reset_resampler_func) {
icculus@10776
  1336
            stream->reset_resampler_func(stream);
icculus@10776
  1337
        }
icculus@10757
  1338
    }
icculus@10757
  1339
}
icculus@10757
  1340
icculus@10757
  1341
icculus@10757
  1342
/* get converted/resampled data from the stream */
icculus@10757
  1343
int
icculus@10764
  1344
SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, const Uint32 len)
icculus@10757
  1345
{
icculus@10757
  1346
    if (!stream) {
icculus@10757
  1347
        return SDL_InvalidParamError("stream");
icculus@10757
  1348
    } else if (!buf) {
icculus@10757
  1349
        return SDL_InvalidParamError("buf");
icculus@10757
  1350
    } else if (len == 0) {
icculus@10757
  1351
        return 0;  /* nothing to do. */
icculus@10757
  1352
    } else if ((len % stream->dst_sample_frame_size) != 0) {
icculus@10757
  1353
        return SDL_SetError("Can't request partial sample frames");
icculus@10757
  1354
    }
icculus@10757
  1355
icculus@10764
  1356
    return (int) SDL_ReadFromDataQueue(stream->queue, buf, len);
icculus@10757
  1357
}
icculus@10757
  1358
icculus@10757
  1359
/* number of converted/resampled bytes available */
icculus@10757
  1360
int
icculus@10757
  1361
SDL_AudioStreamAvailable(SDL_AudioStream *stream)
icculus@10757
  1362
{
icculus@10757
  1363
    return stream ? (int) SDL_CountDataQueue(stream->queue) : 0;
icculus@10757
  1364
}
icculus@10757
  1365
icculus@10757
  1366
/* dispose of a stream */
icculus@10757
  1367
void
icculus@10757
  1368
SDL_FreeAudioStream(SDL_AudioStream *stream)
icculus@10757
  1369
{
icculus@10757
  1370
    if (stream) {
slouken@10773
  1371
        if (stream->cleanup_resampler_func) {
slouken@10773
  1372
            stream->cleanup_resampler_func(stream);
slouken@10773
  1373
        }
icculus@10757
  1374
        SDL_FreeDataQueue(stream->queue);
icculus@10844
  1375
        SDL_free(stream->work_buffer_base);
icculus@10757
  1376
        SDL_free(stream);
icculus@10757
  1377
    }
icculus@10757
  1378
}
icculus@10757
  1379
icculus@10575
  1380
/* vi: set ts=4 sw=4 expandtab: */
slouken@2716
  1381