src/audio/SDL_audiocvt.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 05 Jan 2017 23:53:46 -0800
changeset 10767 b6046389b839
parent 10764 f9bf759e9dd1
child 10773 016a40fda1bb
permissions -rw-r--r--
Don't do any audio conversion if none is necessary
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@10767
   470
    /* Make sure we zero out the audio conversion before error checking */
slouken@10767
   471
    SDL_zerop(cvt);
slouken@10767
   472
slouken@3491
   473
    /* there are no unsigned types over 16 bits, so catch this up front. */
icculus@1982
   474
    if ((SDL_AUDIO_BITSIZE(src_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(src_fmt))) {
icculus@7037
   475
        return SDL_SetError("Invalid source format");
icculus@1982
   476
    }
icculus@1982
   477
    if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) {
icculus@7037
   478
        return SDL_SetError("Invalid destination format");
icculus@1982
   479
    }
icculus@3021
   480
icculus@3021
   481
    /* prevent possible divisions by zero, etc. */
aschiffler@6819
   482
    if ((src_channels == 0) || (dst_channels == 0)) {
icculus@7037
   483
        return SDL_SetError("Source or destination channels is zero");
aschiffler@6819
   484
    }
icculus@3021
   485
    if ((src_rate == 0) || (dst_rate == 0)) {
icculus@7037
   486
        return SDL_SetError("Source or destination rate is zero");
icculus@3021
   487
    }
slouken@10579
   488
#if DEBUG_CONVERT
icculus@1982
   489
    printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n",
slouken@1985
   490
           src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate);
slouken@1985
   491
#endif
icculus@1982
   492
slouken@1895
   493
    /* Start off with no conversion necessary */
icculus@1982
   494
    cvt->src_format = src_fmt;
icculus@1982
   495
    cvt->dst_format = dst_fmt;
slouken@1895
   496
    cvt->needed = 0;
slouken@1895
   497
    cvt->filter_index = 0;
slouken@1895
   498
    cvt->filters[0] = NULL;
slouken@1895
   499
    cvt->len_mult = 1;
slouken@1895
   500
    cvt->len_ratio = 1.0;
icculus@3021
   501
    cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);
slouken@0
   502
icculus@10575
   503
    /* Type conversion goes like this now:
icculus@10575
   504
        - byteswap to CPU native format first if necessary.
icculus@10575
   505
        - convert to native Float32 if necessary.
icculus@10575
   506
        - resample and change channel count if necessary.
icculus@10575
   507
        - convert back to native format.
icculus@10575
   508
        - byteswap back to foreign format if necessary.
icculus@10575
   509
icculus@10575
   510
       The expectation is we can process data faster in float32
icculus@10575
   511
       (possibly with SIMD), and making several passes over the same
icculus@10756
   512
       buffer is likely to be CPU cache-friendly, avoiding the
icculus@10575
   513
       biggest performance hit in modern times. Previously we had
icculus@10575
   514
       (script-generated) custom converters for every data type and
icculus@10575
   515
       it was a bloat on SDL compile times and final library size. */
icculus@10575
   516
slouken@10767
   517
    /* see if we can skip float conversion entirely. */
slouken@10767
   518
    if (src_rate == dst_rate && src_channels == dst_channels) {
slouken@10767
   519
        if (src_fmt == dst_fmt) {
slouken@10767
   520
            return 0;
slouken@10767
   521
        }
slouken@10767
   522
slouken@10767
   523
        /* just a byteswap needed? */
slouken@10767
   524
        if ((src_fmt & ~SDL_AUDIO_MASK_ENDIAN) == (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN)) {
slouken@10767
   525
            cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
slouken@10767
   526
            cvt->needed = 1;
slouken@10767
   527
            return 1;
slouken@10767
   528
        }
icculus@10575
   529
    }
icculus@10575
   530
icculus@1982
   531
    /* Convert data types, if necessary. Updates (cvt). */
slouken@10767
   532
    if (SDL_BuildAudioTypeCVTToFloat(cvt, src_fmt) < 0) {
slouken@1985
   533
        return -1;              /* shouldn't happen, but just in case... */
icculus@3021
   534
    }
slouken@0
   535
icculus@1982
   536
    /* Channel conversion */
slouken@1895
   537
    if (src_channels != dst_channels) {
slouken@1895
   538
        if ((src_channels == 1) && (dst_channels > 1)) {
slouken@1895
   539
            cvt->filters[cvt->filter_index++] = SDL_ConvertStereo;
slouken@1895
   540
            cvt->len_mult *= 2;
slouken@1895
   541
            src_channels = 2;
slouken@1895
   542
            cvt->len_ratio *= 2;
slouken@1895
   543
        }
slouken@1895
   544
        if ((src_channels == 2) && (dst_channels == 6)) {
slouken@1895
   545
            cvt->filters[cvt->filter_index++] = SDL_ConvertSurround;
slouken@1895
   546
            src_channels = 6;
slouken@1895
   547
            cvt->len_mult *= 3;
slouken@1895
   548
            cvt->len_ratio *= 3;
slouken@1895
   549
        }
slouken@1895
   550
        if ((src_channels == 2) && (dst_channels == 4)) {
slouken@1895
   551
            cvt->filters[cvt->filter_index++] = SDL_ConvertSurround_4;
slouken@1895
   552
            src_channels = 4;
slouken@1895
   553
            cvt->len_mult *= 2;
slouken@1895
   554
            cvt->len_ratio *= 2;
slouken@1895
   555
        }
slouken@1895
   556
        while ((src_channels * 2) <= dst_channels) {
slouken@1895
   557
            cvt->filters[cvt->filter_index++] = SDL_ConvertStereo;
slouken@1895
   558
            cvt->len_mult *= 2;
slouken@1895
   559
            src_channels *= 2;
slouken@1895
   560
            cvt->len_ratio *= 2;
slouken@1895
   561
        }
slouken@1895
   562
        if ((src_channels == 6) && (dst_channels <= 2)) {
slouken@1895
   563
            cvt->filters[cvt->filter_index++] = SDL_ConvertStrip;
slouken@1895
   564
            src_channels = 2;
slouken@1895
   565
            cvt->len_ratio /= 3;
slouken@1895
   566
        }
slouken@1895
   567
        if ((src_channels == 6) && (dst_channels == 4)) {
slouken@1895
   568
            cvt->filters[cvt->filter_index++] = SDL_ConvertStrip_2;
slouken@1895
   569
            src_channels = 4;
slouken@1895
   570
            cvt->len_ratio /= 2;
slouken@1895
   571
        }
slouken@1895
   572
        /* This assumes that 4 channel audio is in the format:
slouken@1895
   573
           Left {front/back} + Right {front/back}
slouken@1895
   574
           so converting to L/R stereo works properly.
slouken@1895
   575
         */
slouken@1895
   576
        while (((src_channels % 2) == 0) &&
slouken@1895
   577
               ((src_channels / 2) >= dst_channels)) {
slouken@1895
   578
            cvt->filters[cvt->filter_index++] = SDL_ConvertMono;
slouken@1895
   579
            src_channels /= 2;
slouken@1895
   580
            cvt->len_ratio /= 2;
slouken@1895
   581
        }
slouken@1895
   582
        if (src_channels != dst_channels) {
slouken@1895
   583
            /* Uh oh.. */ ;
slouken@1895
   584
        }
slouken@1895
   585
    }
slouken@0
   586
icculus@3021
   587
    /* Do rate conversion, if necessary. Updates (cvt). */
slouken@10767
   588
    if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) < 0) {
icculus@3021
   589
        return -1;              /* shouldn't happen, but just in case... */
slouken@2716
   590
    }
slouken@2716
   591
icculus@10756
   592
    /* Move to final data type. */
slouken@10767
   593
    if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) < 0) {
icculus@10575
   594
        return -1;              /* shouldn't happen, but just in case... */
slouken@1895
   595
    }
icculus@10575
   596
icculus@10575
   597
    cvt->needed = (cvt->filter_index != 0);
slouken@1895
   598
    return (cvt->needed);
slouken@0
   599
}
slouken@1895
   600
icculus@10757
   601
icculus@10757
   602
struct SDL_AudioStream
icculus@10757
   603
{
icculus@10757
   604
    SDL_AudioCVT cvt_before_resampling;
icculus@10757
   605
    SDL_AudioCVT cvt_after_resampling;
icculus@10757
   606
    SDL_DataQueue *queue;
icculus@10757
   607
    Uint8 *work_buffer;
icculus@10757
   608
    int work_buffer_len;
icculus@10757
   609
    Uint8 *resample_buffer;
icculus@10757
   610
    int resample_buffer_len;
icculus@10757
   611
    int src_sample_frame_size;
icculus@10757
   612
    SDL_AudioFormat src_format;
icculus@10757
   613
    Uint8 src_channels;
icculus@10757
   614
    int src_rate;
icculus@10757
   615
    int dst_sample_frame_size;
icculus@10757
   616
    SDL_AudioFormat dst_format;
icculus@10757
   617
    Uint8 dst_channels;
icculus@10757
   618
    int dst_rate;
icculus@10757
   619
    double rate_incr;
icculus@10757
   620
    Uint8 pre_resample_channels;
icculus@10757
   621
    SDL_bool resampler_seeded;
icculus@10757
   622
    float resampler_state[8];
icculus@10757
   623
    int packetlen;
icculus@10757
   624
};
icculus@10757
   625
icculus@10757
   626
SDL_AudioStream *SDL_NewAudioStream(const SDL_AudioFormat src_format,
icculus@10757
   627
                                    const Uint8 src_channels,
icculus@10757
   628
                                    const int src_rate,
icculus@10757
   629
                                    const SDL_AudioFormat dst_format,
icculus@10757
   630
                                    const Uint8 dst_channels,
icculus@10757
   631
                                    const int dst_rate)
icculus@10757
   632
{
icculus@10757
   633
    const int packetlen = 4096;  /* !!! FIXME: good enough for now. */
icculus@10757
   634
    Uint8 pre_resample_channels;
icculus@10757
   635
    SDL_AudioStream *retval;
icculus@10757
   636
icculus@10757
   637
    retval = (SDL_AudioStream *) SDL_calloc(1, sizeof (SDL_AudioStream));
icculus@10757
   638
    if (!retval) {
icculus@10757
   639
        return NULL;
icculus@10757
   640
    }
icculus@10757
   641
icculus@10757
   642
    /* If increasing channels, do it after resampling, since we'd just
icculus@10757
   643
       do more work to resample duplicate channels. If we're decreasing, do
icculus@10757
   644
       it first so we resample the interpolated data instead of interpolating
icculus@10757
   645
       the resampled data (!!! FIXME: decide if that works in practice, though!). */
icculus@10757
   646
    pre_resample_channels = SDL_min(src_channels, dst_channels);
icculus@10757
   647
icculus@10757
   648
    retval->src_sample_frame_size = SDL_AUDIO_BITSIZE(src_format) * src_channels;
icculus@10757
   649
    retval->src_format = src_format;
icculus@10757
   650
    retval->src_channels = src_channels;
icculus@10757
   651
    retval->src_rate = src_rate;
icculus@10757
   652
    retval->dst_sample_frame_size = SDL_AUDIO_BITSIZE(dst_format) * dst_channels;
icculus@10757
   653
    retval->dst_format = dst_format;
icculus@10757
   654
    retval->dst_channels = dst_channels;
icculus@10757
   655
    retval->dst_rate = dst_rate;
icculus@10757
   656
    retval->pre_resample_channels = pre_resample_channels;
icculus@10757
   657
    retval->packetlen = packetlen;
icculus@10757
   658
    retval->rate_incr = ((double) dst_rate) / ((double) src_rate);
icculus@10757
   659
icculus@10757
   660
    /* Not resampling? It's an easy conversion (and maybe not even that!). */
icculus@10757
   661
    if (src_rate == dst_rate) {
icculus@10757
   662
        retval->cvt_before_resampling.needed = SDL_FALSE;
icculus@10757
   663
        retval->cvt_before_resampling.len_mult = 1;
icculus@10757
   664
        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_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
    } else {
icculus@10757
   669
        /* Don't resample at first. Just get us to Float32 format. */
icculus@10757
   670
        /* !!! FIXME: convert to int32 on devices without hardware float. */
icculus@10757
   671
        if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) == -1) {
icculus@10757
   672
            SDL_free(retval);
icculus@10757
   673
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
   674
        }
icculus@10757
   675
icculus@10757
   676
        /* Convert us to the final format after resampling. */
icculus@10757
   677
        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) == -1) {
icculus@10757
   678
            SDL_free(retval);
icculus@10757
   679
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
   680
        }
icculus@10757
   681
    }
icculus@10757
   682
icculus@10757
   683
    retval->queue = SDL_NewDataQueue(packetlen, packetlen * 2);
icculus@10757
   684
    if (!retval->queue) {
icculus@10757
   685
        SDL_free(retval);
icculus@10757
   686
        return NULL;  /* SDL_NewDataQueue should have called SDL_SetError. */
icculus@10757
   687
    }
icculus@10757
   688
icculus@10757
   689
    return retval;
icculus@10757
   690
}
icculus@10757
   691
icculus@10757
   692
icculus@10757
   693
static int
icculus@10757
   694
ResampleAudioStream(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
icculus@10757
   695
{
icculus@10757
   696
    /* !!! FIXME: this resampler sucks, but not much worse than our usual resampler.  :)  */  /* ... :( */
icculus@10757
   697
    const int chans = (int) stream->pre_resample_channels;
icculus@10757
   698
    const int framelen = chans * sizeof (float);
icculus@10757
   699
    const int total = (inbuflen / framelen);
icculus@10757
   700
    const int finalpos = total - chans;
icculus@10757
   701
    const double src_incr = 1.0 / stream->rate_incr;
icculus@10757
   702
    double idx = 0.0;
icculus@10757
   703
    float *dst = outbuf;
icculus@10757
   704
    float last_sample[SDL_arraysize(stream->resampler_state)];
icculus@10757
   705
    int consumed = 0;
icculus@10757
   706
    int i;
icculus@10757
   707
icculus@10757
   708
    SDL_assert(chans <= SDL_arraysize(last_sample));
icculus@10757
   709
    SDL_assert((inbuflen % framelen) == 0);
icculus@10757
   710
icculus@10757
   711
    if (!stream->resampler_seeded) {
icculus@10757
   712
        for (i = 0; i < chans; i++) {
icculus@10757
   713
            stream->resampler_state[i] = inbuf[i];
icculus@10757
   714
        }
icculus@10757
   715
        stream->resampler_seeded = SDL_TRUE;
icculus@10757
   716
    }
icculus@10757
   717
icculus@10757
   718
    for (i = 0; i < chans; i++) {
icculus@10757
   719
        last_sample[i] = stream->resampler_state[i];
icculus@10757
   720
    }
icculus@10757
   721
icculus@10757
   722
    while (consumed < total) {
icculus@10757
   723
        const int pos = ((int) idx) * chans;
icculus@10757
   724
        const float *src = &inbuf[(pos >= finalpos) ? finalpos : pos];
icculus@10757
   725
        SDL_assert(dst < (outbuf + (outbuflen / framelen)));
icculus@10757
   726
        for (i = 0; i < chans; i++) {
icculus@10757
   727
            const float val = *(src++);
icculus@10757
   728
            *(dst++) = (val + last_sample[i]) * 0.5f;
icculus@10757
   729
            last_sample[i] = val;
icculus@10757
   730
        }
icculus@10757
   731
        consumed = pos + chans;
icculus@10757
   732
        idx += src_incr;
icculus@10757
   733
    }
icculus@10757
   734
icculus@10757
   735
    for (i = 0; i < chans; i++) {
icculus@10757
   736
        stream->resampler_state[i] = last_sample[i];
icculus@10757
   737
    }
icculus@10757
   738
icculus@10759
   739
    return (int) ((dst - outbuf) * sizeof (float));
icculus@10757
   740
}
icculus@10757
   741
icculus@10757
   742
static Uint8 *
icculus@10757
   743
EnsureBufferSize(Uint8 **buf, int *len, const int newlen)
icculus@10757
   744
{
icculus@10757
   745
    if (*len < newlen) {
icculus@10757
   746
        void *ptr = SDL_realloc(*buf, newlen);
icculus@10757
   747
        if (!ptr) {
icculus@10757
   748
            SDL_OutOfMemory();
icculus@10757
   749
            return NULL;
icculus@10757
   750
        }
icculus@10757
   751
        *buf = (Uint8 *) ptr;
icculus@10757
   752
        *len = newlen;
icculus@10757
   753
    }
icculus@10757
   754
    return *buf;
icculus@10757
   755
}
icculus@10757
   756
icculus@10757
   757
int
icculus@10757
   758
SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, const Uint32 _buflen)
icculus@10757
   759
{
icculus@10757
   760
    int buflen = (int) _buflen;
icculus@10757
   761
icculus@10757
   762
    if (!stream) {
icculus@10757
   763
        return SDL_InvalidParamError("stream");
icculus@10757
   764
    } else if (!buf) {
icculus@10757
   765
        return SDL_InvalidParamError("buf");
icculus@10757
   766
    } else if (buflen == 0) {
icculus@10757
   767
        return 0;  /* nothing to do. */
icculus@10757
   768
    } else if ((buflen % stream->src_sample_frame_size) != 0) {
icculus@10757
   769
        return SDL_SetError("Can't add partial sample frames");
icculus@10757
   770
    }
icculus@10757
   771
icculus@10757
   772
    if (stream->cvt_before_resampling.needed) {
icculus@10757
   773
        const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
icculus@10757
   774
        Uint8 *workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
icculus@10757
   775
        if (workbuf == NULL) {
icculus@10757
   776
            return -1;  /* probably out of memory. */
icculus@10757
   777
        }
icculus@10757
   778
        SDL_memcpy(workbuf, buf, buflen);
icculus@10757
   779
        stream->cvt_before_resampling.buf = workbuf;
icculus@10757
   780
        stream->cvt_before_resampling.len = buflen;
icculus@10757
   781
        if (SDL_ConvertAudio(&stream->cvt_before_resampling) == -1) {
icculus@10757
   782
            return -1;   /* uhoh! */
icculus@10757
   783
        }
icculus@10757
   784
        buf = workbuf;
icculus@10757
   785
        buflen = stream->cvt_before_resampling.len_cvt;
icculus@10757
   786
    }
icculus@10757
   787
icculus@10757
   788
    if (stream->dst_rate != stream->src_rate) {
icculus@10757
   789
        const int workbuflen = buflen * ((int) SDL_ceil(stream->rate_incr));
icculus@10757
   790
        float *workbuf = (float *) EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
icculus@10757
   791
        if (workbuf == NULL) {
icculus@10757
   792
            return -1;  /* probably out of memory. */
icculus@10757
   793
        }
icculus@10757
   794
        buflen = ResampleAudioStream(stream, (float *) buf, buflen, workbuf, workbuflen);
icculus@10757
   795
        buf = workbuf;
icculus@10757
   796
    }
icculus@10757
   797
icculus@10757
   798
    if (stream->cvt_after_resampling.needed) {
icculus@10757
   799
        const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
icculus@10757
   800
        Uint8 *workbuf;
icculus@10757
   801
icculus@10757
   802
        if (buf == stream->resample_buffer) {
icculus@10757
   803
            workbuf = EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
icculus@10757
   804
        } else {
icculus@10757
   805
            const int inplace = (buf == stream->work_buffer);
icculus@10757
   806
            workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
icculus@10757
   807
            if (workbuf && !inplace) {
icculus@10757
   808
                SDL_memcpy(workbuf, buf, buflen);
icculus@10757
   809
            }
icculus@10757
   810
        }
icculus@10757
   811
icculus@10757
   812
        if (workbuf == NULL) {
icculus@10757
   813
            return -1;  /* probably out of memory. */
icculus@10757
   814
        }
icculus@10757
   815
icculus@10757
   816
        stream->cvt_after_resampling.buf = workbuf;
icculus@10757
   817
        stream->cvt_after_resampling.len = buflen;
icculus@10757
   818
        if (SDL_ConvertAudio(&stream->cvt_after_resampling) == -1) {
icculus@10757
   819
            return -1;   /* uhoh! */
icculus@10757
   820
        }
icculus@10757
   821
        buf = workbuf;
icculus@10757
   822
        buflen = stream->cvt_after_resampling.len_cvt;
icculus@10757
   823
    }
icculus@10757
   824
icculus@10757
   825
    return SDL_WriteToDataQueue(stream->queue, buf, buflen);
icculus@10757
   826
}
icculus@10757
   827
icculus@10757
   828
void
icculus@10757
   829
SDL_AudioStreamClear(SDL_AudioStream *stream)
icculus@10757
   830
{
icculus@10757
   831
    if (!stream) {
icculus@10757
   832
        SDL_InvalidParamError("stream");
icculus@10757
   833
    } else {
icculus@10757
   834
        SDL_ClearDataQueue(stream->queue, stream->packetlen * 2);
icculus@10757
   835
        stream->resampler_seeded = SDL_FALSE;
icculus@10757
   836
    }
icculus@10757
   837
}
icculus@10757
   838
icculus@10757
   839
icculus@10757
   840
/* get converted/resampled data from the stream */
icculus@10757
   841
int
icculus@10764
   842
SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, const Uint32 len)
icculus@10757
   843
{
icculus@10757
   844
    if (!stream) {
icculus@10757
   845
        return SDL_InvalidParamError("stream");
icculus@10757
   846
    } else if (!buf) {
icculus@10757
   847
        return SDL_InvalidParamError("buf");
icculus@10757
   848
    } else if (len == 0) {
icculus@10757
   849
        return 0;  /* nothing to do. */
icculus@10757
   850
    } else if ((len % stream->dst_sample_frame_size) != 0) {
icculus@10757
   851
        return SDL_SetError("Can't request partial sample frames");
icculus@10757
   852
    }
icculus@10757
   853
icculus@10764
   854
    return (int) SDL_ReadFromDataQueue(stream->queue, buf, len);
icculus@10757
   855
}
icculus@10757
   856
icculus@10757
   857
/* number of converted/resampled bytes available */
icculus@10757
   858
int
icculus@10757
   859
SDL_AudioStreamAvailable(SDL_AudioStream *stream)
icculus@10757
   860
{
icculus@10757
   861
    return stream ? (int) SDL_CountDataQueue(stream->queue) : 0;
icculus@10757
   862
}
icculus@10757
   863
icculus@10757
   864
/* dispose of a stream */
icculus@10757
   865
void
icculus@10757
   866
SDL_FreeAudioStream(SDL_AudioStream *stream)
icculus@10757
   867
{
icculus@10757
   868
    if (stream) {
icculus@10757
   869
        SDL_FreeDataQueue(stream->queue);
icculus@10757
   870
        SDL_free(stream->work_buffer);
icculus@10757
   871
        SDL_free(stream->resample_buffer);
icculus@10757
   872
        SDL_free(stream);
icculus@10757
   873
    }
icculus@10757
   874
}
icculus@10757
   875
icculus@10575
   876
/* vi: set ts=4 sw=4 expandtab: */
slouken@2716
   877