src/audio/SDL_audiocvt.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 06 Jan 2017 01:02:58 -0500
changeset 10764 f9bf759e9dd1
parent 10759 d698fdebebfe
child 10767 b6046389b839
permissions -rw-r--r--
audio: Fixed SDL_AudioStreamGet() function parameters.

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