src/audio/SDL_audiocvt.c
author Ryan C. Gordon <icculus@icculus.org>
Tue, 24 Jan 2017 00:03:36 -0500
changeset 10841 b9d6a3d65394
parent 10840 3c3708a0b217
child 10842 30b37eaf3b3c
permissions -rw-r--r--
audio: Fixed off-by-one error in upsampling.
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
slouken@10773
   861
typedef int (*SDL_ResampleAudioStreamFunc)(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *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
slouken@10773
   893
SDL_ResampleAudioStream_SRC(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
slouken@10773
   894
{
icculus@10799
   895
    const int framelen = sizeof(float) * stream->pre_resample_channels;
icculus@10790
   896
    SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
slouken@10773
   897
    SRC_DATA data;
slouken@10773
   898
    int result;
slouken@10773
   899
slouken@10777
   900
    data.data_in = (float *)inbuf; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */
icculus@10799
   901
    data.input_frames = inbuflen / framelen;
slouken@10773
   902
    data.input_frames_used = 0;
slouken@10773
   903
slouken@10773
   904
    data.data_out = outbuf;
icculus@10799
   905
    data.output_frames = outbuflen / framelen;
slouken@10773
   906
slouken@10773
   907
    data.end_of_input = 0;
slouken@10773
   908
    data.src_ratio = stream->rate_incr;
slouken@10773
   909
icculus@10790
   910
    result = SRC_src_process(state, &data);
slouken@10773
   911
    if (result != 0) {
icculus@10790
   912
        SDL_SetError("src_process() failed: %s", SRC_src_strerror(result));
slouken@10773
   913
        return 0;
slouken@10773
   914
    }
slouken@10773
   915
slouken@10773
   916
    /* If this fails, we need to store them off somewhere */
slouken@10773
   917
    SDL_assert(data.input_frames_used == data.input_frames);
slouken@10773
   918
slouken@10773
   919
    return data.output_frames_gen * (sizeof(float) * stream->pre_resample_channels);
slouken@10773
   920
}
slouken@10773
   921
slouken@10773
   922
static void
slouken@10773
   923
SDL_ResetAudioStreamResampler_SRC(SDL_AudioStream *stream)
slouken@10773
   924
{
icculus@10790
   925
    SRC_src_reset((SRC_STATE *)stream->resampler_state);
slouken@10773
   926
}
slouken@10773
   927
slouken@10773
   928
static void
slouken@10773
   929
SDL_CleanupAudioStreamResampler_SRC(SDL_AudioStream *stream)
slouken@10773
   930
{
icculus@10790
   931
    SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
slouken@10773
   932
    if (state) {
icculus@10790
   933
        SRC_src_delete(state);
slouken@10773
   934
    }
slouken@10773
   935
slouken@10773
   936
    stream->resampler_state = NULL;
slouken@10773
   937
    stream->resampler_func = NULL;
slouken@10773
   938
    stream->reset_resampler_func = NULL;
slouken@10773
   939
    stream->cleanup_resampler_func = NULL;
slouken@10773
   940
}
slouken@10773
   941
slouken@10773
   942
static SDL_bool
slouken@10773
   943
SetupLibSampleRateResampling(SDL_AudioStream *stream)
slouken@10773
   944
{
icculus@10790
   945
    int result = 0;
icculus@10790
   946
    SRC_STATE *state = NULL;
slouken@10773
   947
icculus@10790
   948
    if (SRC_available) {
icculus@10790
   949
        state = SRC_src_new(SRC_SINC_FASTEST, stream->pre_resample_channels, &result);
icculus@10790
   950
        if (!state) {
icculus@10790
   951
            SDL_SetError("src_new() failed: %s", SRC_src_strerror(result));
icculus@10790
   952
        }
slouken@10773
   953
    }
slouken@10773
   954
icculus@10790
   955
    if (!state) {
icculus@10790
   956
        SDL_CleanupAudioStreamResampler_SRC(stream);
slouken@10773
   957
        return SDL_FALSE;
slouken@10773
   958
    }
slouken@10773
   959
slouken@10773
   960
    stream->resampler_state = state;
slouken@10773
   961
    stream->resampler_func = SDL_ResampleAudioStream_SRC;
slouken@10773
   962
    stream->reset_resampler_func = SDL_ResetAudioStreamResampler_SRC;
slouken@10773
   963
    stream->cleanup_resampler_func = SDL_CleanupAudioStreamResampler_SRC;
slouken@10773
   964
slouken@10773
   965
    return SDL_TRUE;
slouken@10773
   966
}
icculus@10790
   967
#endif /* HAVE_LIBSAMPLERATE_H */
slouken@10773
   968
slouken@10773
   969
slouken@10773
   970
typedef struct
slouken@10773
   971
{
icculus@10757
   972
    SDL_bool resampler_seeded;
icculus@10757
   973
    float resampler_state[8];
slouken@10773
   974
} SDL_AudioStreamResamplerState;
slouken@10773
   975
slouken@10773
   976
static int
slouken@10773
   977
SDL_ResampleAudioStream(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
slouken@10773
   978
{
slouken@10773
   979
    SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
slouken@10773
   980
    const int chans = (int)stream->pre_resample_channels;
slouken@10773
   981
icculus@10799
   982
    SDL_assert(chans <= SDL_arraysize(state->resampler_state));
slouken@10773
   983
slouken@10773
   984
    if (!state->resampler_seeded) {
icculus@10799
   985
        int i;
slouken@10773
   986
        for (i = 0; i < chans; i++) {
slouken@10773
   987
            state->resampler_state[i] = inbuf[i];
slouken@10773
   988
        }
slouken@10773
   989
        state->resampler_seeded = SDL_TRUE;
slouken@10773
   990
    }
slouken@10773
   991
icculus@10799
   992
    return SDL_ResampleAudioSimple(chans, stream->rate_incr, state->resampler_state, inbuf, inbuflen, outbuf, outbuflen);
slouken@10773
   993
}
slouken@10773
   994
slouken@10773
   995
static void
slouken@10773
   996
SDL_ResetAudioStreamResampler(SDL_AudioStream *stream)
slouken@10773
   997
{
slouken@10773
   998
    SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
slouken@10773
   999
    state->resampler_seeded = SDL_FALSE;
slouken@10773
  1000
}
slouken@10773
  1001
slouken@10773
  1002
static void
slouken@10773
  1003
SDL_CleanupAudioStreamResampler(SDL_AudioStream *stream)
slouken@10773
  1004
{
slouken@10773
  1005
    SDL_free(stream->resampler_state);
slouken@10773
  1006
}
icculus@10757
  1007
icculus@10789
  1008
SDL_AudioStream *
icculus@10789
  1009
SDL_NewAudioStream(const SDL_AudioFormat src_format,
icculus@10789
  1010
                   const Uint8 src_channels,
icculus@10789
  1011
                   const int src_rate,
icculus@10789
  1012
                   const SDL_AudioFormat dst_format,
icculus@10789
  1013
                   const Uint8 dst_channels,
icculus@10789
  1014
                   const int dst_rate)
icculus@10757
  1015
{
icculus@10757
  1016
    const int packetlen = 4096;  /* !!! FIXME: good enough for now. */
icculus@10757
  1017
    Uint8 pre_resample_channels;
icculus@10757
  1018
    SDL_AudioStream *retval;
icculus@10757
  1019
icculus@10757
  1020
    retval = (SDL_AudioStream *) SDL_calloc(1, sizeof (SDL_AudioStream));
icculus@10757
  1021
    if (!retval) {
icculus@10757
  1022
        return NULL;
icculus@10757
  1023
    }
icculus@10757
  1024
icculus@10757
  1025
    /* If increasing channels, do it after resampling, since we'd just
icculus@10757
  1026
       do more work to resample duplicate channels. If we're decreasing, do
icculus@10757
  1027
       it first so we resample the interpolated data instead of interpolating
icculus@10757
  1028
       the resampled data (!!! FIXME: decide if that works in practice, though!). */
icculus@10757
  1029
    pre_resample_channels = SDL_min(src_channels, dst_channels);
icculus@10757
  1030
icculus@10757
  1031
    retval->src_sample_frame_size = SDL_AUDIO_BITSIZE(src_format) * src_channels;
icculus@10757
  1032
    retval->src_format = src_format;
icculus@10757
  1033
    retval->src_channels = src_channels;
icculus@10757
  1034
    retval->src_rate = src_rate;
icculus@10757
  1035
    retval->dst_sample_frame_size = SDL_AUDIO_BITSIZE(dst_format) * dst_channels;
icculus@10757
  1036
    retval->dst_format = dst_format;
icculus@10757
  1037
    retval->dst_channels = dst_channels;
icculus@10757
  1038
    retval->dst_rate = dst_rate;
icculus@10757
  1039
    retval->pre_resample_channels = pre_resample_channels;
icculus@10757
  1040
    retval->packetlen = packetlen;
icculus@10757
  1041
    retval->rate_incr = ((double) dst_rate) / ((double) src_rate);
icculus@10757
  1042
icculus@10757
  1043
    /* Not resampling? It's an easy conversion (and maybe not even that!). */
icculus@10757
  1044
    if (src_rate == dst_rate) {
icculus@10757
  1045
        retval->cvt_before_resampling.needed = SDL_FALSE;
icculus@10757
  1046
        retval->cvt_before_resampling.len_mult = 1;
slouken@10773
  1047
        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
slouken@10773
  1048
            SDL_FreeAudioStream(retval);
icculus@10757
  1049
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
  1050
        }
icculus@10757
  1051
    } else {
icculus@10757
  1052
        /* Don't resample at first. Just get us to Float32 format. */
icculus@10757
  1053
        /* !!! FIXME: convert to int32 on devices without hardware float. */
slouken@10773
  1054
        if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) < 0) {
slouken@10773
  1055
            SDL_FreeAudioStream(retval);
icculus@10757
  1056
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
  1057
        }
icculus@10757
  1058
slouken@10777
  1059
#ifdef HAVE_LIBSAMPLERATE_H
slouken@10773
  1060
        SetupLibSampleRateResampling(retval);
slouken@10773
  1061
#endif
slouken@10773
  1062
slouken@10773
  1063
        if (!retval->resampler_func) {
slouken@10773
  1064
            retval->resampler_state = SDL_calloc(1, sizeof(SDL_AudioStreamResamplerState));
slouken@10773
  1065
            if (!retval->resampler_state) {
slouken@10773
  1066
                SDL_FreeAudioStream(retval);
slouken@10773
  1067
                SDL_OutOfMemory();
slouken@10773
  1068
                return NULL;
slouken@10773
  1069
            }
slouken@10773
  1070
            retval->resampler_func = SDL_ResampleAudioStream;
slouken@10773
  1071
            retval->reset_resampler_func = SDL_ResetAudioStreamResampler;
slouken@10773
  1072
            retval->cleanup_resampler_func = SDL_CleanupAudioStreamResampler;
slouken@10773
  1073
        }
slouken@10773
  1074
icculus@10757
  1075
        /* Convert us to the final format after resampling. */
slouken@10773
  1076
        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
slouken@10773
  1077
            SDL_FreeAudioStream(retval);
icculus@10757
  1078
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
  1079
        }
icculus@10757
  1080
    }
icculus@10757
  1081
icculus@10757
  1082
    retval->queue = SDL_NewDataQueue(packetlen, packetlen * 2);
icculus@10757
  1083
    if (!retval->queue) {
slouken@10773
  1084
        SDL_FreeAudioStream(retval);
icculus@10757
  1085
        return NULL;  /* SDL_NewDataQueue should have called SDL_SetError. */
icculus@10757
  1086
    }
icculus@10757
  1087
icculus@10757
  1088
    return retval;
icculus@10757
  1089
}
icculus@10757
  1090
icculus@10757
  1091
static Uint8 *
icculus@10757
  1092
EnsureBufferSize(Uint8 **buf, int *len, const int newlen)
icculus@10757
  1093
{
icculus@10757
  1094
    if (*len < newlen) {
icculus@10757
  1095
        void *ptr = SDL_realloc(*buf, newlen);
icculus@10757
  1096
        if (!ptr) {
icculus@10757
  1097
            SDL_OutOfMemory();
icculus@10757
  1098
            return NULL;
icculus@10757
  1099
        }
icculus@10757
  1100
        *buf = (Uint8 *) ptr;
icculus@10757
  1101
        *len = newlen;
icculus@10757
  1102
    }
icculus@10757
  1103
    return *buf;
icculus@10757
  1104
}
icculus@10757
  1105
icculus@10757
  1106
int
icculus@10757
  1107
SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, const Uint32 _buflen)
icculus@10757
  1108
{
icculus@10757
  1109
    int buflen = (int) _buflen;
icculus@10757
  1110
icculus@10757
  1111
    if (!stream) {
icculus@10757
  1112
        return SDL_InvalidParamError("stream");
icculus@10757
  1113
    } else if (!buf) {
icculus@10757
  1114
        return SDL_InvalidParamError("buf");
icculus@10757
  1115
    } else if (buflen == 0) {
icculus@10757
  1116
        return 0;  /* nothing to do. */
icculus@10757
  1117
    } else if ((buflen % stream->src_sample_frame_size) != 0) {
icculus@10757
  1118
        return SDL_SetError("Can't add partial sample frames");
icculus@10757
  1119
    }
icculus@10757
  1120
icculus@10757
  1121
    if (stream->cvt_before_resampling.needed) {
icculus@10757
  1122
        const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
icculus@10757
  1123
        Uint8 *workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
icculus@10757
  1124
        if (workbuf == NULL) {
icculus@10757
  1125
            return -1;  /* probably out of memory. */
icculus@10757
  1126
        }
icculus@10757
  1127
        SDL_memcpy(workbuf, buf, buflen);
icculus@10757
  1128
        stream->cvt_before_resampling.buf = workbuf;
icculus@10757
  1129
        stream->cvt_before_resampling.len = buflen;
icculus@10757
  1130
        if (SDL_ConvertAudio(&stream->cvt_before_resampling) == -1) {
icculus@10757
  1131
            return -1;   /* uhoh! */
icculus@10757
  1132
        }
icculus@10757
  1133
        buf = workbuf;
icculus@10757
  1134
        buflen = stream->cvt_before_resampling.len_cvt;
icculus@10757
  1135
    }
icculus@10757
  1136
icculus@10757
  1137
    if (stream->dst_rate != stream->src_rate) {
icculus@10757
  1138
        const int workbuflen = buflen * ((int) SDL_ceil(stream->rate_incr));
icculus@10757
  1139
        float *workbuf = (float *) EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
icculus@10757
  1140
        if (workbuf == NULL) {
icculus@10757
  1141
            return -1;  /* probably out of memory. */
icculus@10757
  1142
        }
slouken@10773
  1143
        buflen = stream->resampler_func(stream, (float *) buf, buflen, workbuf, workbuflen);
icculus@10757
  1144
        buf = workbuf;
icculus@10757
  1145
    }
icculus@10757
  1146
icculus@10757
  1147
    if (stream->cvt_after_resampling.needed) {
icculus@10757
  1148
        const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
icculus@10757
  1149
        Uint8 *workbuf;
icculus@10757
  1150
icculus@10757
  1151
        if (buf == stream->resample_buffer) {
icculus@10757
  1152
            workbuf = EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
icculus@10757
  1153
        } else {
icculus@10757
  1154
            const int inplace = (buf == stream->work_buffer);
icculus@10757
  1155
            workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
icculus@10757
  1156
            if (workbuf && !inplace) {
icculus@10757
  1157
                SDL_memcpy(workbuf, buf, buflen);
icculus@10757
  1158
            }
icculus@10757
  1159
        }
icculus@10757
  1160
icculus@10757
  1161
        if (workbuf == NULL) {
icculus@10757
  1162
            return -1;  /* probably out of memory. */
icculus@10757
  1163
        }
icculus@10757
  1164
icculus@10757
  1165
        stream->cvt_after_resampling.buf = workbuf;
icculus@10757
  1166
        stream->cvt_after_resampling.len = buflen;
icculus@10757
  1167
        if (SDL_ConvertAudio(&stream->cvt_after_resampling) == -1) {
icculus@10757
  1168
            return -1;   /* uhoh! */
icculus@10757
  1169
        }
icculus@10757
  1170
        buf = workbuf;
icculus@10757
  1171
        buflen = stream->cvt_after_resampling.len_cvt;
icculus@10757
  1172
    }
icculus@10757
  1173
icculus@10757
  1174
    return SDL_WriteToDataQueue(stream->queue, buf, buflen);
icculus@10757
  1175
}
icculus@10757
  1176
icculus@10757
  1177
void
icculus@10757
  1178
SDL_AudioStreamClear(SDL_AudioStream *stream)
icculus@10757
  1179
{
icculus@10757
  1180
    if (!stream) {
icculus@10757
  1181
        SDL_InvalidParamError("stream");
icculus@10757
  1182
    } else {
icculus@10757
  1183
        SDL_ClearDataQueue(stream->queue, stream->packetlen * 2);
icculus@10776
  1184
        if (stream->reset_resampler_func) {
icculus@10776
  1185
            stream->reset_resampler_func(stream);
icculus@10776
  1186
        }
icculus@10757
  1187
    }
icculus@10757
  1188
}
icculus@10757
  1189
icculus@10757
  1190
icculus@10757
  1191
/* get converted/resampled data from the stream */
icculus@10757
  1192
int
icculus@10764
  1193
SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, const Uint32 len)
icculus@10757
  1194
{
icculus@10757
  1195
    if (!stream) {
icculus@10757
  1196
        return SDL_InvalidParamError("stream");
icculus@10757
  1197
    } else if (!buf) {
icculus@10757
  1198
        return SDL_InvalidParamError("buf");
icculus@10757
  1199
    } else if (len == 0) {
icculus@10757
  1200
        return 0;  /* nothing to do. */
icculus@10757
  1201
    } else if ((len % stream->dst_sample_frame_size) != 0) {
icculus@10757
  1202
        return SDL_SetError("Can't request partial sample frames");
icculus@10757
  1203
    }
icculus@10757
  1204
icculus@10764
  1205
    return (int) SDL_ReadFromDataQueue(stream->queue, buf, len);
icculus@10757
  1206
}
icculus@10757
  1207
icculus@10757
  1208
/* number of converted/resampled bytes available */
icculus@10757
  1209
int
icculus@10757
  1210
SDL_AudioStreamAvailable(SDL_AudioStream *stream)
icculus@10757
  1211
{
icculus@10757
  1212
    return stream ? (int) SDL_CountDataQueue(stream->queue) : 0;
icculus@10757
  1213
}
icculus@10757
  1214
icculus@10757
  1215
/* dispose of a stream */
icculus@10757
  1216
void
icculus@10757
  1217
SDL_FreeAudioStream(SDL_AudioStream *stream)
icculus@10757
  1218
{
icculus@10757
  1219
    if (stream) {
slouken@10773
  1220
        if (stream->cleanup_resampler_func) {
slouken@10773
  1221
            stream->cleanup_resampler_func(stream);
slouken@10773
  1222
        }
icculus@10757
  1223
        SDL_FreeDataQueue(stream->queue);
icculus@10757
  1224
        SDL_free(stream->work_buffer);
icculus@10757
  1225
        SDL_free(stream->resample_buffer);
icculus@10757
  1226
        SDL_free(stream);
icculus@10757
  1227
    }
icculus@10757
  1228
}
icculus@10757
  1229
icculus@10575
  1230
/* vi: set ts=4 sw=4 expandtab: */
slouken@2716
  1231