src/audio/SDL_audiocvt.c
author Ryan C. Gordon <icculus@icculus.org>
Tue, 24 Jan 2017 00:08:24 -0500
changeset 10842 30b37eaf3b3c
parent 10841 b9d6a3d65394
child 10843 31c9dede7b9c
permissions -rw-r--r--
audio: allow stereo Sint16 resampling fast path in SDL_AudioStream.

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